from __future__ import annotations

import difflib
from dataclasses import dataclass
from typing import Any, Dict, Optional

from pydantic import BaseModel, ConfigDict, Field


@dataclass(frozen=True)
class EvalRequest:
    dataset_path: str
    task_type: str
    prompt: str
    job_id: str

    def to_dict(self) -> dict[str, Any]:
        return {
            "dataset_path": self.dataset_path,
            "task_type": self.task_type,
            "prompt": self.prompt,
            "job_id": self.job_id,
        }


def unified_diff(old: str, new: str, fromfile: str = "base_prompt", tofile: str = "optimized_prompt") -> str:
    old_lines = old.splitlines(keepends=True)
    new_lines = new.splitlines(keepends=True)
    return "".join(difflib.unified_diff(old_lines, new_lines, fromfile=fromfile, tofile=tofile, n=3))


# Route targets are determined at runtime from the workflow; keep type flexible and validate in orchestrator.
NextAgent = str


class PlannerDecisionModel(BaseModel):
    next_agent: NextAgent
    reason: str


class PlannerOutputModel(BaseModel):
    decision: PlannerDecisionModel


class TestExecutorOutputModel(BaseModel):
    job_id: str
    result_id: str


class MetricsExecutorOutputModel(BaseModel):
    result_id: str
    metrics: Dict[str, Any]


class BadCaseAnalyzerOutputModel(BaseModel):
    analysis: Dict[str, Any]


class PromptOptimizerOutputModel(BaseModel):
    optimized: Dict[str, Any]


class VersionManagerOutputModel(BaseModel):
    record: Dict[str, Any]


class LoopControllerOutputModel(BaseModel):
    model_config = ConfigDict(populate_by_name=True)

    continue_: bool = Field(alias="continue")
    stop_reason: str = ""


class ErrorOutputModel(BaseModel):
    error: str
    details: Optional[Dict[str, Any]] = None
