"""Optimization result dataclass."""

from __future__ import annotations

import json
from dataclasses import asdict, dataclass, field
from pathlib import Path
from typing import Any


@dataclass
class OptimizationResult:
    """Result of a prompt optimization run."""
    
    success: bool
    """Whether the optimization completed successfully."""
    
    session_id: str | None = None
    """Session identifier that tracks this experiment."""
    
    best_version: str | None = None
    """The version identifier of the best prompt found."""
    
    final_score: float | None = None
    """The final evaluation score achieved."""

    stop_reason: str | None = None
    """Reason for stopping the optimization loop."""
    
    state: dict[str, Any] = field(default_factory=dict)
    """Final shared state after optimization."""

    dimension_scores: dict[str, float] | None = None
    """Optional dimension scores for the final prompt, if available."""

    best_branch: str | None = None
    """Best branch selected from iteration candidates."""

    best_commit_id: str | None = None
    """Commit id of the best candidate if available."""

    best_candidate_id: str | None = None
    """Candidate id of the best candidate if available."""

    experiment_id: str | None = None
    """Experiment id of the best candidate if available."""

    experiment_link: str | None = None
    """Experiment link of the best candidate if available."""

    # ── Serialization ─────────────────────────────────────────────

    def to_dict(self) -> dict[str, Any]:
        """Convert to a JSON-serialisable dictionary."""
        return asdict(self)

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> OptimizationResult:
        """Reconstruct an ``OptimizationResult`` from a dictionary.

        Unknown keys are silently ignored so that files written by newer
        versions of the SDK can still be loaded by older ones.
        """
        known_fields = {f.name for f in cls.__dataclass_fields__.values()}
        filtered = {k: v for k, v in data.items() if k in known_fields}
        # Backward compat: old result.json files store "run_id" instead of "session_id"
        if "session_id" not in filtered and "run_id" in data:
            filtered["session_id"] = data["run_id"]
        return cls(**filtered)

    def save(self, path: str | Path) -> Path:
        """Persist this result to a JSON file.

        Args:
            path: File path to write.  Parent directories are created
                automatically if they don't exist.

        Returns:
            The resolved ``Path`` that was written.
        """
        p = Path(path)
        p.parent.mkdir(parents=True, exist_ok=True)
        p.write_text(
            json.dumps(self.to_dict(), indent=2, ensure_ascii=False, default=str),
            encoding="utf-8",
        )
        return p

    @classmethod
    def load(cls, path: str | Path) -> OptimizationResult:
        """Load a previously saved result from a JSON file.

        Args:
            path: File path to read.

        Returns:
            The reconstructed ``OptimizationResult``.

        Raises:
            FileNotFoundError: If *path* does not exist.
        """
        p = Path(path)
        if not p.exists():
            raise FileNotFoundError(f"Result file not found: {p}")
        data = json.loads(p.read_text(encoding="utf-8"))
        return cls.from_dict(data)
