"""Server batch job persistence (MySQL)."""

from __future__ import annotations

import json
from datetime import datetime, timezone
from typing import Any, Optional

from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from server.domain.models import ServerBatchJob


class ServerBatchJobRepositoryImpl:
    def __init__(self, session: AsyncSession):
        self.session = session

    async def get_by_id(self, job_id: int) -> Optional[ServerBatchJob]:
        result = await self.session.execute(
            select(ServerBatchJob).where(ServerBatchJob.id == job_id)
        )
        return result.scalar_one_or_none()

    async def create(self, job: ServerBatchJob) -> ServerBatchJob:
        self.session.add(job)
        await self.session.commit()
        return job

    async def upsert_running(
        self,
        *,
        job_id: int,
        op: str,
        label: str,
        server_ids: list[int],
        operator: str,
        params: Optional[dict[str, Any]] = None,
    ) -> ServerBatchJob:
        row = await self.get_by_id(job_id)
        if row:
            return row
        job = ServerBatchJob(
            id=job_id,
            op=op,
            label=label,
            server_ids=json.dumps(server_ids, ensure_ascii=False),
            status="running",
            results=json.dumps([], ensure_ascii=False),
            operator=operator,
            params=json.dumps(params or {}, ensure_ascii=False),
        )
        return await self.create(job)

    async def finalize(
        self,
        job_id: int,
        *,
        status: str,
        results: list[dict[str, Any]],
    ) -> Optional[ServerBatchJob]:
        row = await self.get_by_id(job_id)
        if not row:
            return None
        row.status = status
        row.results = json.dumps(results, ensure_ascii=False)
        row.completed_at = datetime.now(timezone.utc)
        await self.session.commit()
        return row

    async def list_running(self) -> list[ServerBatchJob]:
        result = await self.session.execute(
            select(ServerBatchJob).where(ServerBatchJob.status == "running")
        )
        return list(result.scalars().all())

    async def list_recent(
        self,
        *,
        limit: int = 50,
        offset: int = 0,
        op: Optional[str] = None,
    ) -> tuple[list[ServerBatchJob], int]:
        filters = []
        if op:
            filters.append(ServerBatchJob.op == op)
        count_stmt = select(func.count(ServerBatchJob.id))
        if filters:
            count_stmt = count_stmt.where(*filters)
        total = int((await self.session.execute(count_stmt)).scalar() or 0)
        stmt = select(ServerBatchJob).order_by(ServerBatchJob.started_at.desc())
        if filters:
            stmt = stmt.where(*filters)
        result = await self.session.execute(stmt.limit(limit).offset(offset))
        return list(result.scalars().all()), total


def job_row_to_api(row: ServerBatchJob) -> dict[str, Any]:
    """Convert MySQL row to BatchJobStatus-shaped dict."""
    try:
        server_ids = json.loads(row.server_ids or "[]")
    except json.JSONDecodeError:
        server_ids = []
    try:
        results = json.loads(row.results or "[]")
    except json.JSONDecodeError:
        results = []
    if isinstance(results, dict):
        ordered = []
        for sid in server_ids:
            entry = results.get(str(sid))
            if entry:
                ordered.append(entry)
        results = ordered
    total = len(server_ids)
    success = sum(1 for r in results if r.get("success"))
    failed = sum(1 for r in results if not r.get("success"))
    done = len(results)
    return {
        "job_id": int(row.id),
        "op": row.op or "",
        "label": row.label or "",
        "status": row.status or "completed",
        "operator": row.operator or "",
        "total": total,
        "done": done,
        "remaining": max(0, total - done),
        "success": success,
        "failed": failed,
        "progress": f"{success}/{total}" if total else "—",
        "results": results,
        "started_at": row.started_at.isoformat() if row.started_at else None,
        "completed_at": row.completed_at.isoformat() if row.completed_at else None,
    }
