"""In-memory scan job storage for the Sanicode API server.

Scan jobs are stored in a dict keyed by scan_id. No persistence across
server restarts — durable storage is handled independently by the
scan-result.json file written by the scan pipeline.
"""

from __future__ import annotations

import asyncio
import uuid
from dataclasses import dataclass, field

from sanicode.report.persist import ScanResult
from sanicode.server.models import ScanStatus


@dataclass
class ScanJob:
    """Tracks the lifecycle of a single scan request."""

    scan_id: str
    status: ScanStatus
    target_path: str
    config_path: str | None = None
    result: ScanResult | None = None
    graph_data: dict | None = None
    error: str | None = None
    task: asyncio.Task | None = field(default=None, repr=False)


class ScanStore:
    """Thread-safe in-memory store for scan jobs."""

    def __init__(self) -> None:
        self._jobs: dict[str, ScanJob] = {}

    def create_job(
        self, target_path: str, config_path: str | None = None
    ) -> ScanJob:
        """Create a new scan job with a unique ID.

        Args:
            target_path: Filesystem path to scan.
            config_path: Optional path to sanicode.toml.

        Returns:
            The newly created ScanJob in pending status.
        """
        scan_id = uuid.uuid4().hex[:12]
        job = ScanJob(
            scan_id=scan_id,
            status=ScanStatus.pending,
            target_path=target_path,
            config_path=config_path,
        )
        self._jobs[scan_id] = job
        return job

    def get_job(self, scan_id: str) -> ScanJob | None:
        """Look up a scan job by ID, or return None."""
        return self._jobs.get(scan_id)

    def list_jobs(self) -> list[ScanJob]:
        """Return all scan jobs, most recent first."""
        return list(reversed(self._jobs.values()))

    @property
    def active_count(self) -> int:
        """Number of jobs currently running."""
        return sum(
            1 for j in self._jobs.values() if j.status == ScanStatus.running
        )
