# backend/app/automation/evaluator.py
# 이벤트 → 규칙 매칭 → 동작/승인 enqueue. automation.matched → approval enqueue 연합 고리.
from __future__ import annotations

import uuid
from datetime import UTC, datetime

from sqlmodel import Session, select

from ..models import (
    Approval,
    ApprovalLog,
    ApprovalSource,
    ApprovalStatus,
    AutomationRule,
    AutomationRunLog,
    AutonomyLevel,
    AutonomySetting,
    RiskLevel,
)
from .event_bus import Event, EventBus
from .events import APPROVAL_ENQUEUED, APPROVAL_EXECUTED, AUTOMATION_MATCHED

# cat → 결재 카드 기본 tone/icon(원본 매핑과 일관)
CAT_TONE = {"mail": "blue", "cal": "violet", "focus": "coral", "life": "green"}
CAT_ICON = {"mail": "mail", "cal": "cal", "focus": "zap", "life": "wallet"}

# 어떤 동작이 high-risk 인가 — 보내기/결제/삭제/전달 키워드
HIGH_RISK_RE = ("보내", "결제", "삭제", "전달", "발송", "일시정지", "송금")


def _risk_of(action: str) -> str:
    return RiskLevel.high.value if any(k in action for k in HIGH_RISK_RE) else RiskLevel.low.value


def _autonomy(s: Session) -> str:
    row = s.get(AutonomySetting, "default")
    return row.level if row else AutonomyLevel.mixed.value


def match_rules(s: Session, trigger_key: str) -> list[AutomationRule]:
    """trigger_key(예: 'mail.newsletter')와 켜진 규칙을 매칭.
    데모는 cat 기반 단순 매칭 + trigger 문자열 포함. 실연동(phase-13)이 정교화."""
    rules = s.exec(select(AutomationRule).where(AutomationRule.on == True)).all()  # noqa: E712
    head = trigger_key.split(".")[0]
    tail = trigger_key.split(".")[-1]
    return [r for r in rules if head in (r.cat, "") or tail in r.trigger]


def enqueue_from_rule(s: Session, rule: AutomationRule, ctx: dict, bus: EventBus) -> Approval:
    """규칙 동작을 승인 큐에 넣는다. low+mixed이상=자동 실행(executed), 그 외=pending."""
    risk = _risk_of(rule.action)
    level = _autonomy(s)
    auto_run = risk == RiskLevel.low.value and level in (
        AutonomyLevel.mixed.value,
        AutonomyLevel.full_auto.value,
    )
    ap = Approval(
        id="ap-" + uuid.uuid4().hex[:8],
        icon=CAT_ICON.get(rule.cat, "spark"),
        tone=CAT_TONE.get(rule.cat, "blue"),
        risk=risk,
        time=("자동 실행됨" if auto_run else "확인 필요"),
        title=ctx.get("title", rule.action),
        detail=ctx.get("detail", f"{rule.name} · {rule.trigger}"),
        cta=ctx.get("cta", "" if risk == RiskLevel.low.value else "실행"),
        alt=ctx.get("alt", "" if risk == RiskLevel.low.value else "나중에"),
        undo_label=(ctx.get("undo_label", "되돌리기") if risk == RiskLevel.low.value else ""),
        status=(ApprovalStatus.executed.value if auto_run else ApprovalStatus.pending.value),
        source=ApprovalSource.automation.value,
        rule_id=rule.id,
        executed_at=(datetime.now(UTC) if auto_run else None),
    )
    s.add(ap)
    s.add(
        AutomationRunLog(
            id="arun-" + uuid.uuid4().hex[:8],
            time="방금",
            rule_id=rule.id,
            rule=rule.name,
            text=ctx.get("text", rule.action),
        )
    )
    if auto_run:
        s.add(
            ApprovalLog(
                id="alog-" + uuid.uuid4().hex[:8],
                time="방금",
                text=ctx.get("text", rule.action),
                approval_id=ap.id,
            )
        )
    rule.runs += 1
    rule.last = "방금"
    s.add(rule)
    s.commit()
    s.refresh(ap)
    # 결재 enqueue 정본 이벤트명 = approval.enqueued
    bus.publish(
        APPROVAL_ENQUEUED,
        {
            "approval_id": ap.id,
            "rule_id": rule.id,
            "risk": risk,
            "title": ap.title,
            "auto_run": auto_run,
        },
    )
    if auto_run:
        bus.publish(
            APPROVAL_EXECUTED,
            {"approval_id": ap.id, "rule_id": rule.id, "risk": risk, "title": ap.title},
        )
    return ap


def register(bus: EventBus, session_factory) -> None:
    """버스에 evaluator 핸들러 등록. session_factory()는 Session 컨텍스트를 yield."""

    def on_matched(ev: Event) -> None:
        trigger_key = ev.payload.get("trigger_key", "")
        ctx = ev.payload.get("ctx", {})
        with session_factory() as s:
            for rule in match_rules(s, trigger_key):
                enqueue_from_rule(s, rule, ctx, bus)

    bus.subscribe(AUTOMATION_MATCHED, on_matched)
