"""
Playbook Generation Governance Job - ADR-104 T2/T6
==================================================
定期處理 LLM 生成 Playbook 的 DRAFT 黑洞:
- DRAFT + confidence >= 0.75 + 安全步驟 → REVIEW
- REVIEW + confidence >= 0.9 + 安全步驟 → APPROVED
"""

from __future__ import annotations

import asyncio
from dataclasses import dataclass

import structlog

from src.core.config import settings
from src.models.playbook import ActionType, Playbook, PlaybookSource, PlaybookStatus
from src.services.action_parser import is_safe_kubectl_action

logger = structlog.get_logger(__name__)


@dataclass
class PlaybookGovernanceReport:
    reviewed_count: int = 0
    approved_count: int = 0
    skipped_count: int = 0
    errors: list[str] | None = None

    def __post_init__(self) -> None:
        if self.errors is None:
            self.errors = []


def _is_generated_candidate(playbook: Playbook) -> bool:
    return playbook.source in (PlaybookSource.LLM_GENERATED, PlaybookSource.EXTRACTED)


def _has_safe_steps(playbook: Playbook) -> bool:
    if not playbook.repair_steps:
        return False
    for step in playbook.repair_steps:
        if step.action_type == ActionType.KUBECTL:
            if not is_safe_kubectl_action(step.command):
                return False
        elif step.action_type != ActionType.MANUAL and not step.requires_approval:
            return False
    return True


async def run_playbook_generation_governance_once(force: bool = False) -> PlaybookGovernanceReport:
    """Run one DRAFT/REVIEW governance pass."""
    if not force and not settings.ENABLE_PLAYBOOK_DRAFT_GOVERNANCE_JOB:
        return PlaybookGovernanceReport()

    from src.services.playbook_service import get_playbook_service

    service = get_playbook_service()
    report = PlaybookGovernanceReport()

    for status in (PlaybookStatus.DRAFT, PlaybookStatus.REVIEW):
        playbooks, _total = await service.list_playbooks(status=status, limit=200, offset=0)
        for playbook in playbooks:
            if not _is_generated_candidate(playbook) or not _has_safe_steps(playbook):
                report.skipped_count += 1
                continue
            try:
                if playbook.status == PlaybookStatus.DRAFT and playbook.ai_confidence >= 0.75:
                    await service.update_with_validation(
                        playbook.playbook_id,
                        {
                            "status": PlaybookStatus.REVIEW.value,
                            "notes": (playbook.notes or "") + "\n[Governance: DRAFT -> REVIEW]",
                        },
                    )
                    report.reviewed_count += 1
                    _record_governance("reviewed")
                elif playbook.status == PlaybookStatus.REVIEW and playbook.ai_confidence >= 0.9:
                    await service.update_with_validation(
                        playbook.playbook_id,
                        {
                            "status": PlaybookStatus.APPROVED.value,
                            "approved_by": "playbook_generation_governance",
                            "notes": (playbook.notes or "") + "\n[Governance: REVIEW -> APPROVED]",
                        },
                    )
                    if playbook.supersedes_playbook_id:
                        await _deprecate_superseded(
                            service,
                            superseded_id=playbook.supersedes_playbook_id,
                            replacement_id=playbook.playbook_id,
                        )
                    report.approved_count += 1
                    _record_governance("approved")
                else:
                    report.skipped_count += 1
            except Exception as exc:
                report.errors.append(f"{playbook.playbook_id}:{exc}")
                logger.warning("playbook_governance_update_failed", playbook_id=playbook.playbook_id, error=str(exc))

    logger.info(
        "playbook_generation_governance_done",
        reviewed=report.reviewed_count,
        approved=report.approved_count,
        skipped=report.skipped_count,
        errors=len(report.errors),
    )
    return report


async def _deprecate_superseded(service, superseded_id: str, replacement_id: str) -> None:
    """Deprecate an older Playbook version after replacement approval."""
    superseded = await service.get_by_id(superseded_id)
    if not superseded or superseded.status == PlaybookStatus.DEPRECATED:
        return
    await service.update_with_validation(
        superseded_id,
        {
            "status": PlaybookStatus.DEPRECATED.value,
            "notes": (
                (superseded.notes or "")
                + f"\n[Governance: superseded by {replacement_id}]"
            ),
        },
    )
    _record_governance("superseded")


async def run_playbook_generation_governance_loop() -> None:
    """Run governance forever at configured interval."""
    while True:
        try:
            await run_playbook_generation_governance_once()
        except Exception:
            logger.exception("playbook_generation_governance_fatal")
        await asyncio.sleep(settings.PLAYBOOK_DRAFT_GOVERNANCE_INTERVAL_SECONDS)


def _record_governance(outcome: str) -> None:
    try:
        from src.core.metrics import observe_playbook_status, record_playbook_generation

        record_playbook_generation(outcome=outcome, source="governance")
        if outcome == "reviewed":
            observe_playbook_status(status=PlaybookStatus.REVIEW.value, source="governance")
        elif outcome == "approved":
            observe_playbook_status(status=PlaybookStatus.APPROVED.value, source="governance")
    except Exception:
        pass
