"""活动报告读模型 load_campaign_report 与 ScenarioRepository.name_map 直测。

读模型是报告 / 分析 / 对比 / 导出共用的「取数 + 聚合」单一出口：
返回结果必须与手写三件套（list_by_campaign + scenario_names + generate_campaign_report）完全一致。
"""

from datetime import datetime, timedelta, timezone

import pytest
from agenteval.evaluation.report import generate_campaign_report, load_campaign_report
from agenteval.models import Campaign, CampaignPlanEntry, CampaignStatus, Case, CaseType, EvalRun, RunStatus, Scenario
from agenteval.storage.repository import CampaignRepository, RunRepository, ScenarioRepository
from sqlmodel import Session, SQLModel, create_engine

T0 = datetime(2026, 1, 1, 0, 0, 0, tzinfo=timezone.utc)


@pytest.fixture()
def db_session(tmp_path):
    from agenteval.storage.db import (  # noqa: F401
        CampaignDB,
        EvalResultDB,
        EvalRunDB,
        EvalTargetDB,
        FileCategoryDB,
        FileRecordDB,
        ScenarioDB,
        TurnDB,
    )
    engine = create_engine(
        f"sqlite:///{tmp_path / 'loader.db'}",
        connect_args={"check_same_thread": False},
    )
    SQLModel.metadata.create_all(engine)
    session = Session(engine)
    try:
        yield session
    finally:
        session.close()
        engine.dispose()


def _seed(session) -> Campaign:
    ScenarioRepository(session).create(Scenario(
        id="s-a", name="夜间问诊",
        cases=[Case(id="c1", type=CaseType.SINGLE, messages=["hi"])],
    ))
    campaign = CampaignRepository(session).create(Campaign(
        name="cycle", target_id="t-1", window_seconds=12, time_scale=1.0,
        plan=[CampaignPlanEntry(scenario_id="s-a", offset_seconds=0, count=1)],
    ))
    campaign.status = CampaignStatus.RUNNING
    campaign.started_at = T0
    campaign = CampaignRepository(session).update(campaign)
    RunRepository(session).create(EvalRun(
        target_id="t-1", scenario_id="s-a", campaign_id=campaign.id,
        status=RunStatus.COMPLETED, started_at=T0 + timedelta(seconds=1),
        summary={"total_cases": 1, "passed_cases": 1, "pass_rate": 1.0, "avg_latency_ms": 100},
    ))
    return campaign


def test_load_campaign_report_matches_manual_trio(db_session):
    campaign = _seed(db_session)
    report = load_campaign_report(db_session, campaign)

    runs = RunRepository(db_session).list_by_campaign(campaign.id)
    scenario_names = {s.id: s.name for s in ScenarioRepository(db_session).list_all()}
    expected = generate_campaign_report(campaign, runs, scenario_names=scenario_names)

    assert report == expected
    cap = next(c for c in report["capability_summary"] if c["scenario_id"] == "s-a")
    assert cap["scenario_name"] == "夜间问诊"


def test_load_campaign_report_empty_campaign(db_session):
    campaign = CampaignRepository(db_session).create(Campaign(
        name="empty", target_id="t-1", window_seconds=12,
        plan=[CampaignPlanEntry(scenario_id="s-x", offset_seconds=0, count=1)],
    ))
    report = load_campaign_report(db_session, campaign)
    assert report["summary"]["total_runs"] == 0
    assert report["capability_summary"] == []


def test_scenario_name_map(db_session):
    _seed(db_session)
    ScenarioRepository(db_session).create(Scenario(
        id="s-b", name="缴费引导",
        cases=[Case(id="c2", type=CaseType.SINGLE, messages=["缴费"])],
    ))
    assert ScenarioRepository(db_session).name_map() == {"s-a": "夜间问诊", "s-b": "缴费引导"}


def test_scenario_name_map_empty(db_session):
    assert ScenarioRepository(db_session).name_map() == {}
