"""Job and batch result dataclasses used across the screenshot package."""

from __future__ import annotations

from collections.abc import Iterable
from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path
from typing import TYPE_CHECKING, Protocol, runtime_checkable

from ._models_artifacts import ScreenshotArtifactsRequest, ScreenshotArtifactsResult
from ._shared.errors import ScreenshotError

if TYPE_CHECKING:
    from ._models_options import ScreenshotOptions


@runtime_checkable
class CancellationToken(Protocol):
    def raise_if_cancelled(self) -> None: ...


class ScreenshotCaptureOutcome(Enum):
    SUCCESS = "success"
    PARTIAL = "partial"
    FAILED = "failed"
    SKIPPED = "skipped"


@dataclass
class ScreenshotJob:
    job_id: str
    url: str
    output_root: Path
    options: ScreenshotOptions
    partition_date: str | None = None
    artifacts: ScreenshotArtifactsRequest = field(default_factory=ScreenshotArtifactsRequest)
    cancel_token: CancellationToken | None = None
    metadata: dict[str, object] = field(default_factory=dict)


@dataclass
class ScreenshotCaptureResult:
    requested: int
    captured: int
    failed: int
    metadata_path: Path | None
    entries: list[dict[str, object]] = field(default_factory=list)
    errors: list[ScreenshotError] = field(default_factory=list)
    job_id: str | None = None
    artifacts: ScreenshotArtifactsResult = field(default_factory=ScreenshotArtifactsResult)

    @property
    def outcome(self) -> ScreenshotCaptureOutcome:
        if self.captured > 0:
            if self.failed > 0 or self.errors:
                return ScreenshotCaptureOutcome.PARTIAL
            return ScreenshotCaptureOutcome.SUCCESS
        if self.failed > 0 or self.errors or self.requested > 0:
            return ScreenshotCaptureOutcome.FAILED
        return ScreenshotCaptureOutcome.SKIPPED

    @property
    def has_artifacts(self) -> bool:
        return self.captured > 0

    @property
    def error_messages(self) -> list[str]:
        return [error.message for error in self.errors]

    def add_error(self, error: ScreenshotError) -> None:
        self.errors.append(error)


@dataclass
class ScreenshotBatchResult:
    results: list[ScreenshotCaptureResult]

    @property
    def failures(self) -> list[ScreenshotCaptureResult]:
        return [
            result
            for result in self.results
            if result.outcome in {ScreenshotCaptureOutcome.PARTIAL, ScreenshotCaptureOutcome.FAILED}
        ]

    @property
    def successes(self) -> list[ScreenshotCaptureResult]:
        return [
            result for result in self.results if result.outcome is ScreenshotCaptureOutcome.SUCCESS
        ]

    @property
    def skipped(self) -> list[ScreenshotCaptureResult]:
        return [
            result for result in self.results if result.outcome is ScreenshotCaptureOutcome.SKIPPED
        ]

    @property
    def partials(self) -> list[ScreenshotCaptureResult]:
        return [
            result for result in self.results if result.outcome is ScreenshotCaptureOutcome.PARTIAL
        ]

    @property
    def outcome_counts(self) -> dict[str, int]:
        counts = {outcome.value: 0 for outcome in ScreenshotCaptureOutcome}
        for result in self.results:
            counts[result.outcome.value] += 1
        return counts

    def to_dict(self) -> dict[str, object]:
        return {
            "results": [
                {
                    "job_id": result.job_id,
                    "outcome": result.outcome.value,
                    "has_artifacts": result.has_artifacts,
                    "requested": result.requested,
                    "captured": result.captured,
                    "failed": result.failed,
                    "metadata_path": str(result.metadata_path) if result.metadata_path else None,
                    "artifacts": result.artifacts.to_dict(),
                    "errors": [error.to_dict() for error in result.errors],
                }
                for result in self.results
            ],
            "success_count": len(self.successes),
            "failure_count": len(self.failures),
            "partial_count": len(self.partials),
            "skipped_count": len(self.skipped),
            "outcome_counts": self.outcome_counts,
        }

    @classmethod
    def from_results(cls, results: Iterable[ScreenshotCaptureResult]) -> ScreenshotBatchResult:
        return cls(results=list(results))
