# backend/app/approvals/service.py
# 큐 분기(derive_queue = 원본 approve.jsx initStatus) + 상태 전이.
from __future__ import annotations

from datetime import UTC, datetime

from sqlmodel import Session, select

from ..automation.event_bus import bus
from ..automation.events import APPROVAL_EXECUTED, APPROVAL_UNDONE
from ..models import (
    Approval,
    ApprovalLog,
    ApprovalStatus,
    AutonomyLevel,
    AutonomySetting,
    RiskLevel,
)

_LEVELS = (
    AutonomyLevel.approval_first.value,
    AutonomyLevel.mixed.value,
    AutonomyLevel.full_auto.value,
)


def get_autonomy(s: Session) -> str:
    row = s.get(AutonomySetting, "default")
    if not row:
        row = AutonomySetting(id="default", level=AutonomyLevel.mixed.value)
        s.add(row)
        s.commit()
        s.refresh(row)
    return row.level


def set_autonomy(s: Session, level: str) -> str:
    if level not in _LEVELS:
        raise ValueError("invalid autonomy level")
    row = s.get(AutonomySetting, "default") or AutonomySetting(id="default")
    row.level = level
    row.updated_at = datetime.now(UTC)
    s.add(row)
    s.commit()
    return level


def derive_status(level: str, risk: str) -> str:
    """원본 approve.jsx initStatus(autonomy) 이식.
    full_auto: 전부 executed. mixed: low=executed, high=pending. approval_first: 전부 pending."""
    if level == AutonomyLevel.full_auto.value:
        return ApprovalStatus.executed.value
    if level == AutonomyLevel.mixed.value:
        return (
            ApprovalStatus.executed.value
            if risk == RiskLevel.low.value
            else ApprovalStatus.pending.value
        )
    return ApprovalStatus.pending.value  # approval_first


def derive_queue(s: Session, user_id: str | None = None) -> dict:
    """자율성 레벨로 모든 approval 의 표시 상태를 파생.
    단, 사용자가 명시적으로 undone 한 것은 pending, 엔진이 실제 실행한 건은 executed 로 고정.
    user_id 가 주어지면 소유자 스코프(phase-15)."""
    level = get_autonomy(s)
    stmt = select(Approval).order_by(Approval.sort_order)
    if user_id is not None:
        stmt = stmt.where(Approval.user_id == user_id)
    rows = s.exec(stmt).all()
    pending, done = [], []
    for a in rows:
        if a.status == ApprovalStatus.undone.value:  # 사용자가 되돌림 → 대기로
            eff = ApprovalStatus.pending.value
        elif a.status == ApprovalStatus.executed.value and a.executed_at:
            eff = ApprovalStatus.executed.value  # 사용자/엔진이 실제 실행한 건은 고정
        else:
            eff = derive_status(level, a.risk)  # 시드/표시용은 레벨로 파생
        (done if eff == ApprovalStatus.executed.value else pending).append(a)
    return {"level": level, "pending": pending, "done": done}


def approve(s: Session, aid: str) -> Approval:
    a = s.get(Approval, aid)
    a.status = ApprovalStatus.executed.value
    a.executed_at = datetime.now(UTC)
    s.add(a)
    s.commit()
    s.refresh(a)
    s.add(ApprovalLog(id="alog-" + aid + "-x", time="방금", text=a.title, approval_id=aid))
    s.commit()
    bus.publish(APPROVAL_EXECUTED, {"approval_id": aid, "risk": a.risk, "title": a.title})
    return a


def undo(s: Session, aid: str) -> Approval:
    a = s.get(Approval, aid)
    a.status = ApprovalStatus.undone.value
    a.undone_at = datetime.now(UTC)
    s.add(a)
    s.commit()
    s.refresh(a)
    bus.publish(APPROVAL_UNDONE, {"approval_id": aid, "title": a.title})
    return a


def approve_all(s: Session) -> int:
    q = derive_queue(s)
    n = 0
    for a in q["pending"]:
        approve(s, a.id)
        n += 1
    return n
