"""Exploration patrol (探索巡检).

常驻智能体的无状态巡检：筛出参与探索的正式线活动（running、
time_scale == 1、有种子集），汇报水位以来的新增结果与剩余探索预算，
构建完响应后推进水位，保证下次只报增量。
"""

from datetime import datetime
from typing import Any

from sqlmodel import Session

from agenteval.evaluation.report import generate_campaign_report
from agenteval.exploration.models import resolve_budget
from agenteval.models import Campaign, CampaignStatus, EvalRun
from agenteval.storage.db import as_utc, iso_utc, utc_now
from agenteval.storage.repository import (
    CampaignRepository,
    ExplorationSessionRepository,
    RunRepository,
    ScenarioRepository,
    TargetRepository,
)


def _new_runs_since(runs: list[EvalRun], watermark: datetime | None) -> list[EvalRun]:
    fresh = []
    for run in runs:
        if run.completed_at is None:
            continue
        if watermark is not None and as_utc(run.completed_at) <= watermark:
            continue
        fresh.append(run)
    return fresh


def patrol_report(db_session: Session) -> dict[str, Any]:
    """一次巡检的完整读模型：patrolled_at + 逐活动条目列表。

    水位取构建响应之后的时刻：查询与持久化之间完成的结果不会在下次重复上报。
    """
    campaign_repo = CampaignRepository(db_session)
    run_repo = RunRepository(db_session)
    exploration_repo = ExplorationSessionRepository(db_session)
    scenario_names = ScenarioRepository(db_session).name_map()
    target_names = {t.id: t.name for t in TargetRepository(db_session).list_all()}

    patrolled_at = utc_now()
    entries: list[dict[str, Any]] = []
    patrolled_campaigns: list[Campaign] = []
    for campaign in campaign_repo.list_all():
        if campaign.status != CampaignStatus.RUNNING:
            continue
        if campaign.time_scale != 1:
            continue
        if campaign.exploration_seeds is None:
            continue

        watermark = as_utc(campaign.last_patrolled_at) if campaign.last_patrolled_at else None
        fresh = _new_runs_since(run_repo.list_by_campaign(campaign.id), watermark)
        new_results = None
        if fresh:
            report = generate_campaign_report(campaign, fresh, scenario_names=scenario_names)
            new_results = {
                "summary": report["summary"],
                "capability_summary": report["capability_summary"],
            }

        budget = resolve_budget(campaign)
        sessions = exploration_repo.list_by_campaign(campaign.id)
        seconds_since_last_session = None
        if sessions:
            latest = max(as_utc(s.created_at) for s in sessions if s.created_at)
            seconds_since_last_session = int((patrolled_at - latest).total_seconds())

        entries.append(
            {
                "campaign_id": campaign.id,
                "campaign_name": campaign.name,
                "target_id": campaign.target_id,
                "target_name": target_names.get(campaign.target_id),
                "last_patrolled_at": iso_utc(campaign.last_patrolled_at),
                "new_results": new_results,
                "budget": {
                    "max_sessions": budget.max_sessions,
                    "sessions_used": len(sessions),
                    "remaining_sessions": max(0, budget.max_sessions - len(sessions)),
                    "max_turns": budget.max_turns,
                    "min_interval_seconds": budget.min_interval_seconds,
                    "seconds_since_last_session": seconds_since_last_session,
                },
            }
        )
        patrolled_campaigns.append(campaign)

    watermark_at = utc_now()
    for campaign in patrolled_campaigns:
        campaign_repo.touch_patrol_watermark(campaign.id, watermark_at)

    return {"patrolled_at": iso_utc(watermark_at), "campaigns": entries}
