"""顺序执行评测任务。

第一版刻意不并发：避免压测与质量评测混在一起，也降低误连环境时的影响面。
"""
from __future__ import annotations

import datetime as dt
import uuid
from dataclasses import dataclass
from typing import Callable, Optional

from .client import ZcbotClient
from .models import (
    CaseResult,
    EvalCase,
    EvalSuite,
    RepetitionResult,
    RunObservation,
    safe_case_slug,
)
from .scoring import score_observation


@dataclass(frozen=True)
class RunSettings:
    repetitions: Optional[int] = None
    timeout_s: float = 900.0


ProgressCallback = Callable[[str], None]


def run_suite(
    suite: EvalSuite,
    client: ZcbotClient,
    *,
    settings: RunSettings,
    progress: ProgressCallback = lambda _message: None,
) -> list[CaseResult]:
    stamp = (
        dt.datetime.now().strftime("%Y%m%d-%H%M%S")
        + "-"
        + uuid.uuid4().hex[:8]
    )
    results: list[CaseResult] = []
    for case in suite.cases:
        repetitions = (
            settings.repetitions
            if settings.repetitions is not None
            else case.repetitions or suite.default_repetitions
        )
        run_results: list[RepetitionResult] = []
        for index in range(1, repetitions + 1):
            slug = safe_case_slug(case.id)
            working_dir = f"eval-{slug}-{stamp}-{index}"
            task_name = f"eval-{slug}-{index}"
            progress(f"[INFO] {case.id} repetition {index}/{repetitions}")
            task = client.create_task(
                name=task_name,
                working_dir=working_dir,
                skill=case.skill,
                model_profile=case.model_profile,
            )
            task_id = str(task["task_id"])
            try:
                observation = client.run_prompt(
                    task_id=task_id,
                    prompt=case.prompt,
                    working_dir=working_dir,
                    timeout_s=case.timeout_s or settings.timeout_s,
                )
            except Exception as exc:
                observation = RunObservation(
                    run_status="error",
                    run_error=f"{type(exc).__name__}: {exc}",
                    task_id=task_id,
                    working_dir=working_dir,
                )
            score, assertions = score_observation(
                case.assertions, observation
            )
            run_results.append(
                RepetitionResult(
                    index=index,
                    observation=observation,
                    assertions=assertions,
                    score=score,
                )
            )
        case_score = sum(item.score for item in run_results) / len(run_results)
        passed = [
            item.score >= suite.pass_threshold for item in run_results
        ]
        results.append(
            CaseResult(
                case=case,
                repetitions=run_results,
                score=case_score,
                pass_at_1=passed[0],
                pass_all=all(passed),
            )
        )
    return results
