"""
AI Agent report source health read model.

This module builds a redacted, read-only source-health view for daily, weekly,
monthly reports. It intentionally does not send Telegram, write Gateway queues,
enable schedulers, execute AI repair, mutate incidents, or open runtime gates.
"""

from __future__ import annotations

import asyncio
from collections.abc import Awaitable, Callable
from datetime import datetime
from typing import Any
from zoneinfo import ZoneInfo

import structlog

from src.services.ai_agent_report_status_board import (
    load_latest_ai_agent_report_status_board,
)

logger = structlog.get_logger(__name__)

_SCHEMA_VERSION = "ai_agent_report_source_health_v1"
_CURRENT_TASK_ID = "P2-109"
_NEXT_TASK_ID = "P2-110"
_RUNTIME_AUTHORITY = "report_source_health_read_model_only_no_send_or_write"
_TAIPEI = ZoneInfo("Asia/Taipei")


async def build_ai_agent_report_source_health(days: int = 30) -> dict[str, Any]:
    """Build the report source health read model."""
    generated_at = datetime.now(_TAIPEI).isoformat()

    status_board = await _load_status_board()
    incident_summary = await _read_source(
        source_id="incident_summary",
        display_name="事件統計 read model",
        route="/api/v1/stats/incidents/summary",
        work_item_id="report-source-gap:incident_summary",
        reader=lambda: _read_incident_summary(days),
        extractor=lambda payload: {
            "total": _as_int(payload.get("total_incidents") or payload.get("total")),
            "resolved_rate": _as_float(payload.get("resolved_rate")),
        },
        next_action="接入 redacted public incident summary，確認 Alertmanager 入庫、recurrence mirror 與 freshness。",
    )
    resolution_stats = await _read_source(
        source_id="resolution_stats",
        display_name="解決率 read model",
        route="/api/v1/stats/incidents/resolution",
        work_item_id="report-source-gap:resolution_stats",
        reader=lambda: _read_resolution_stats(days),
        extractor=lambda payload: {
            "avg_minutes": payload.get("avg_minutes"),
            "resolution_rate": payload.get("resolutionRate") or payload.get("resolution_rate"),
        },
        next_action="接入 redacted public resolution stats，確認 resolved_at、duration 與 postmortem 寫回。",
    )
    ai_performance = await _read_source(
        source_id="ai_performance",
        display_name="AI 效能 read model",
        route="/api/v1/stats/ai-performance",
        work_item_id="report-source-gap:ai_performance",
        reader=lambda: _read_ai_performance(days),
        extractor=lambda payload: {
            "proposal_count": _as_int(payload.get("total_proposals")),
            "executed_count": _as_int(payload.get("executed_count")),
            "success_rate": _as_float(payload.get("success_rate")),
        },
        next_action="接入 redacted public AI performance stats，確認提案、執行、成功率與 fallback reason。",
    )
    disposition_stats = await _read_source(
        source_id="disposition_stats",
        display_name="處置統計 read model",
        route="/api/v1/stats/disposition",
        work_item_id="report-source-gap:disposition_stats",
        reader=_read_disposition_stats,
        extractor=lambda payload: payload,
        next_action="處置統計可讀時仍需追蹤 auto repair、manual handoff、cold-start trust 的占比。",
    )
    status_board_source = _build_status_board_source(status_board)

    sources = [
        incident_summary,
        resolution_stats,
        ai_performance,
        disposition_stats,
        status_board_source,
    ]
    ok_count = sum(1 for source in sources if source["source_ok"])
    gap_sources = [source for source in sources if not source["source_ok"]]
    all_zero = _is_all_zero(incident_summary, ai_performance, disposition_stats)
    source_count = len(sources)
    confidence_percent = round(ok_count / source_count * 100) if source_count else 0
    if all_zero and gap_sources:
        confidence_percent = min(confidence_percent, 40)

    no_send_previews = _build_no_send_previews(status_board, ok_count, source_count, gap_sources)
    work_items = _build_work_items(gap_sources, all_zero)
    automation_assets = _build_automation_assets(status_board, work_items)
    source_gap_playbook_verifier = _build_source_gap_playbook_verifier(
        sources,
        work_items,
    )

    return {
        "schema_version": _SCHEMA_VERSION,
        "generated_at": generated_at,
        "program_status": {
            "current_task_id": _CURRENT_TASK_ID,
            "next_task_id": _NEXT_TASK_ID,
            "overall_completion_percent": 100,
            "read_only_mode": True,
            "runtime_authority": _RUNTIME_AUTHORITY,
            "status_note": "日報 / 週報 / 月報資料源健康與 no-send preview 已可由 API 統一讀回。",
        },
        "source_health": sources,
        "all_zero_assessment": {
            "all_zero_observed": all_zero,
            "verdict": "source_gap_or_no_signal_requires_review" if all_zero else "signals_available_or_not_all_zero",
            "confidence_percent": confidence_percent,
            "blocking_reason": "資料源缺口存在時，全 0 不可視為健康。" if all_zero and gap_sources else "",
            "next_action": (
                "先處理 report-source-gap，再產生日報 / 週報 / 月報草案。"
                if gap_sources
                else "持續用趨勢、recurrence 與 verifier 判讀是否需要 AI 接手。"
            ),
        },
        "no_send_previews": no_send_previews,
        "automation_assets": automation_assets,
        "source_gap_playbook_verifier": source_gap_playbook_verifier,
        "work_items": work_items,
        "activation_boundaries": {
            "telegram_send_enabled": False,
            "gateway_queue_write_enabled": False,
            "scheduler_change_enabled": False,
            "ai_runtime_execution_enabled": False,
            "medium_low_auto_execution_enabled": False,
            "production_write_enabled": False,
            "secret_read_enabled": False,
        },
        "rollups": {
            "source_count": source_count,
            "source_ok_count": ok_count,
            "source_gap_count": len(gap_sources),
            "confidence_percent": confidence_percent,
            "no_send_preview_count": len(no_send_previews),
            "report_work_item_count": len(work_items),
            "source_gap_playbook_draft_count": len(source_gap_playbook_verifier),
            "source_gap_verifier_plan_count": len(source_gap_playbook_verifier),
            "source_gap_owner_review_required_count": len(source_gap_playbook_verifier),
            "live_send_allowed_count": 0,
            "runtime_gate_count": 0,
        },
    }


async def _load_status_board() -> dict[str, Any] | None:
    try:
        return await asyncio.to_thread(load_latest_ai_agent_report_status_board)
    except Exception as exc:
        logger.warning("report_source_health_status_board_failed", error=str(exc))
        return None


async def _read_source(
    *,
    source_id: str,
    display_name: str,
    route: str,
    work_item_id: str,
    reader: Callable[[], Awaitable[dict[str, Any]]],
    extractor: Callable[[dict[str, Any]], dict[str, Any]],
    next_action: str,
) -> dict[str, Any]:
    try:
        payload = await reader()
        metrics = extractor(payload or {})
        return {
            "source_id": source_id,
            "display_name": display_name,
            "route": route,
            "source_ok": True,
            "state": "ok",
            "freshness": "live_readback",
            "confidence_percent": 100,
            "metrics": metrics,
            "work_item_id": "",
            "next_action": next_action,
        }
    except Exception as exc:
        logger.warning("report_source_health_reader_failed", source_id=source_id, error=str(exc))
        return {
            "source_id": source_id,
            "display_name": display_name,
            "route": route,
            "source_ok": False,
            "state": "gap",
            "freshness": "unavailable",
            "confidence_percent": 0,
            "metrics": {},
            "work_item_id": work_item_id,
            "next_action": next_action,
        }


async def _read_incident_summary(days: int) -> dict[str, Any]:
    from src.services.stats_service import get_stats_service

    return await get_stats_service().get_incident_summary(days=days)


async def _read_resolution_stats(days: int) -> dict[str, Any]:
    from src.services.stats_service import get_stats_service

    return await get_stats_service().get_resolution_stats(days=days)


async def _read_ai_performance(days: int) -> dict[str, Any]:
    from src.services.stats_service import get_stats_service

    return await get_stats_service().get_ai_performance(days=days)


async def _read_disposition_stats() -> dict[str, Any]:
    from src.services.anomaly_counter import get_anomaly_counter

    summary, _ = await get_anomaly_counter().get_all_disposition_stats()
    total = _as_int(summary.get("total"))
    auto = _as_int(summary.get("auto_repair")) + _as_int(summary.get("cold_start_trust"))
    return {
        "total": total,
        "auto_repair": _as_int(summary.get("auto_repair")),
        "human_approved": _as_int(summary.get("human_approved")),
        "manual_resolved": _as_int(summary.get("manual_resolved")),
        "cold_start_trust": _as_int(summary.get("cold_start_trust")),
        "auto_rate": round(auto / total, 4) if total else 0,
    }


def _build_status_board_source(status_board: dict[str, Any] | None) -> dict[str, Any]:
    if not status_board:
        return {
            "source_id": "report_status_board",
            "display_name": "日報 / 週報 / 月報狀態板",
            "route": "/api/v1/agents/agent-report-status-board",
            "source_ok": False,
            "state": "gap",
            "freshness": "unavailable",
            "confidence_percent": 0,
            "metrics": {},
            "work_item_id": "report-source-gap:status_board",
            "next_action": "確認 committed snapshot、API route 與 no-send preview contract。",
        }
    rollups = status_board.get("rollups") or {}
    return {
        "source_id": "report_status_board",
        "display_name": "日報 / 週報 / 月報狀態板",
        "route": "/api/v1/agents/agent-report-status-board",
        "source_ok": True,
        "state": "ok",
        "freshness": "committed_snapshot",
        "confidence_percent": 100,
        "metrics": {
            "report_card_count": _as_int(rollups.get("report_card_count")),
            "agent_status_count": _as_int(rollups.get("agent_status_count")),
            "workload_done_total": _as_int(rollups.get("workload_done_total")),
            "workload_unit_total": _as_int(rollups.get("workload_unit_total")),
            "live_delivery_count": _as_int(rollups.get("live_delivery_count")),
        },
        "work_item_id": "",
        "next_action": "狀態板可讀；下一步接 no-send preview freshness 與 SRE 戰情室 digest route。",
    }


def _build_no_send_previews(
    status_board: dict[str, Any] | None,
    ok_count: int,
    source_count: int,
    gap_sources: list[dict[str, Any]],
) -> list[dict[str, Any]]:
    cards = (status_board or {}).get("report_status_cards") or []
    gap_ids = [source["source_id"] for source in gap_sources]
    previews = []
    for card in cards:
        previews.append({
            "cadence_id": card.get("cadence_id"),
            "display_name": card.get("display_name"),
            "owner_agent": card.get("owner_agent"),
            "delivery_state": "no_send_preview",
            "source_ready_count": ok_count,
            "source_total_count": source_count,
            "blocked_by_source_gap": bool(gap_sources),
            "gap_source_ids": gap_ids,
            "live_send_allowed": False,
            "gateway_queue_write_allowed": False,
            "next_gate": "補齊 source health 與 receipt readback 後，仍需人工批准才可實發。",
        })
    return previews


def _build_work_items(gap_sources: list[dict[str, Any]], all_zero: bool) -> list[dict[str, Any]]:
    work_items = [
        {
            "work_item_id": source["work_item_id"],
            "title": f"補齊 {source['display_name']} 資料鏈路",
            "state": "open",
            "blocking_reason": "report source unavailable",
            "next_action": source["next_action"],
        }
        for source in gap_sources
        if source.get("work_item_id")
    ]
    if all_zero:
        work_items.append({
            "work_item_id": "report-source-gap:all_zero_truth",
            "title": "全 0 報表真相判讀",
            "state": "open",
            "blocking_reason": "all-zero report cannot be treated as healthy",
            "next_action": "比對事件、處置、AI 效能、Git / deploy 與成本來源 freshness。",
        })
    return work_items


def _build_automation_assets(
    status_board: dict[str, Any] | None,
    work_items: list[dict[str, Any]],
) -> list[dict[str, Any]]:
    rollups = (status_board or {}).get("rollups") or {}
    return [
        {
            "asset_id": "km_report_digest",
            "label": "KM",
            "state": "draft_ready",
            "done_count": _as_int(rollups.get("agent_status_count")),
            "blocked_count": len(work_items),
            "next_action": "把資料缺口與 digest 結論回寫到 owner review 後的 KM 草稿。",
        },
        {
            "asset_id": "playbook_report_source_gap",
            "label": "PlayBook",
            "state": "draft_required" if work_items else "candidate_ready",
            "done_count": 0 if work_items else 1,
            "blocked_count": len(work_items),
            "next_action": "建立 report-source-gap 專屬 PlayBook；不可用通用兜底命令。",
        },
        {
            "asset_id": "script_report_readback",
            "label": "腳本",
            "state": "readback_only",
            "done_count": 1,
            "blocked_count": 0,
            "next_action": "保留 read-only API / no-send preview；不寫排程、不呼叫 Bot API。",
        },
        {
            "asset_id": "schedule_report_no_send",
            "label": "排程",
            "state": "no_send_preview",
            "done_count": _as_int(rollups.get("report_card_count")),
            "blocked_count": 0,
            "next_action": "日報 / 週報 / 月報先產生草案；live delivery 仍維持 0。",
        },
        {
            "asset_id": "verifier_report_source_health",
            "label": "Verifier",
            "state": "source_health_ready",
            "done_count": 1,
            "blocked_count": len(work_items),
            "next_action": "Verifier 需檢查 source_ok、all_zero_assessment 與 no_send_previews。",
        },
    ]


def _build_source_gap_playbook_verifier(
    sources: list[dict[str, Any]],
    work_items: list[dict[str, Any]],
) -> list[dict[str, Any]]:
    source_by_work_item = {
        source.get("work_item_id"): source
        for source in sources
        if source.get("work_item_id")
    }
    cards: list[dict[str, Any]] = []
    for item in work_items:
        work_item_id = str(item.get("work_item_id") or "")
        if not work_item_id:
            continue
        source = source_by_work_item.get(work_item_id) or {}
        source_id = str(source.get("source_id") or work_item_id.split(":")[-1])
        display_name = str(source.get("display_name") or item.get("title") or source_id)
        route = str(source.get("route") or "/api/v1/stats/sre-digest/preview")
        cards.append({
            "work_item_id": work_item_id,
            "source_id": source_id,
            "display_name": display_name,
            "route": route,
            "playbook_draft_id": f"playbook-draft:{work_item_id}",
            "verifier_plan_id": f"verifier-plan:{work_item_id}",
            "playbook_state": "draft_required",
            "verifier_state": "plan_required",
            "script_state": "readback_only",
            "schedule_state": "no_send_preview",
            "owner_review_required": True,
            "runtime_gate_open": False,
            "playbook_template_fields": [
                "source_id",
                "route",
                "expected_metrics",
                "freshness_slo",
                "fallback_behavior",
                "rollback_or_disable_plan",
                "owner_review",
                "verifier_plan",
            ],
            "verifier_checks": [
                "source_route_returns_200_or_declared_gap",
                "source_ok_semantics_match_metrics",
                "all_zero_assessment_not_treated_as_healthy",
                "no_send_preview_remains_no_send",
                "runtime_gate_count_remains_zero",
            ],
            "next_action": item.get("next_action") or "補 PlayBook 草案與 Verifier readback。",
        })
    return cards


def _is_all_zero(
    incident_summary: dict[str, Any],
    ai_performance: dict[str, Any],
    disposition_stats: dict[str, Any],
) -> bool:
    incident_total = _as_int(incident_summary.get("metrics", {}).get("total"))
    proposals = _as_int(ai_performance.get("metrics", {}).get("proposal_count"))
    executed = _as_int(ai_performance.get("metrics", {}).get("executed_count"))
    disposition_total = _as_int(disposition_stats.get("metrics", {}).get("total"))
    return incident_total == 0 and proposals == 0 and executed == 0 and disposition_total == 0


def _as_int(value: Any) -> int:
    try:
        return int(value or 0)
    except (TypeError, ValueError):
        return 0


def _as_float(value: Any) -> float:
    try:
        return float(value or 0.0)
    except (TypeError, ValueError):
        return 0.0
