"""The ledger's commands (Stream K2): `decisions list`, `decisions undo-batch`, `decisions verify`;
`decisions export` and `decisions replay` join in K2.8. The route for a batch undo is K4's.

Sources of truth: this module, `services/decisions/undo.py`, `services/decisions/replay.py`,
`docs/RUNBOOK.md` (the after-deploy chain runs `decisions verify`).
"""

from __future__ import annotations

import argparse
import json
import uuid as _uuid
from datetime import datetime

from sqlalchemy import select

from app.db import SessionLocal
from app.models import Account, Decision, DecisionBatch, ProposalPass
from app.services.decisions import undo as undo_service
from app.services.decisions.writer import Refused


def _print(row: Decision, batch_uid: str | None = None) -> None:
    who = row.decided_by_username or ("replayed" if row.replayed_from else "?")
    what = "release" if row.effect == "release" else json.dumps(row.value)
    extra = []
    if row.reverses_id:
        extra.append(f"reverses #{row.reverses_id}")
    if row.restores_id:
        extra.append(f"restores #{row.restores_id}")
    if row.caused_by_id:
        extra.append(f"consequence of #{row.caused_by_id}")
    if row.pass_id:
        extra.append(f"pass {row.pass_id}")
    print(f"#{row.id} {row.decided_at:%Y-%m-%d %H:%M} {row.entity_type}:{row.entity_id} {row.field} = {what} "
          f"[{row.origin}/{row.mode}] by {who}" + (f" batch {batch_uid}" if batch_uid else "") +
          (f" ({', '.join(extra)})" if extra else "") + (f": {row.reason}" if row.reason else ""))


def cmd_list(args: argparse.Namespace) -> int:
    with SessionLocal() as db:
        stmt = select(Decision)
        if args.entity:
            stmt = stmt.where(Decision.entity_type == args.entity[0], Decision.entity_id == int(args.entity[1]))
        if args.batch:
            b = db.scalar(select(DecisionBatch).where(DecisionBatch.uid == _uuid.UUID(args.batch)))
            if b is None:
                print(f"no batch {args.batch}")
                return 2
            stmt = stmt.where(Decision.batch_id == b.id)
        if getattr(args, "pass_name", None):
            p = db.scalar(select(ProposalPass).where(ProposalPass.name == args.pass_name))
            if p is None:
                print(f"no pass {args.pass_name}")
                return 2
            stmt = stmt.where(Decision.pass_id == p.id)
        if args.since:
            stmt = stmt.where(Decision.decided_at >= datetime.fromisoformat(args.since))
        rows = list(db.scalars(stmt.order_by(Decision.decided_at.desc(), Decision.id.desc()).limit(args.limit)))
        batches = {b.id: str(b.uid) for b in db.scalars(select(DecisionBatch).where(DecisionBatch.id.in_({r.batch_id for r in rows})))} if rows else {}
        for row in rows:
            _print(row, batches.get(row.batch_id))
        print(f"{len(rows)} decision(s)")
    return 0


def cmd_undo_batch(args: argparse.Namespace) -> int:
    with SessionLocal() as db:
        account = db.scalar(select(Account).where(Account.username == args.by.strip().lower()))
        if account is None:
            print(f"undo-batch: no account named {args.by!r}")
            return 2
        try:
            row = undo_service.undo_batch(db, args.uid, account, args.reason, force=args.force)
        except Refused as exc:
            print(f"undo-batch: {exc.code}: {exc.summary}")
            return 2
        summary = row.summary or {}
        print(f"undo-batch: batch {args.uid} reversed by batch {row.uid}: {summary.get('decisions', 0)} row(s) written, "
              f"{len(summary.get('skipped', []))} skipped")
        for item in summary.get("skipped", []):
            print(f"  skipped {item}")
    return 0


def cmd_verify(args: argparse.Namespace) -> int:
    with SessionLocal() as db:
        report = undo_service.verify(db)
    print(f"decisions verify: {report['checked']} effective decision(s) checked, {report['drift']} drift")
    for item in report["examples"]:
        print(f"  {item}")
    return 1 if report["drift"] else 0


def register(sub: argparse._SubParsersAction) -> None:
    decisions = sub.add_parser("decisions", help="the ledger: list, undo a batch, verify the columns, export and replay")
    dsub = decisions.add_subparsers(dest="decisions_command", required=True)

    lst = dsub.add_parser("list", help="the tracked-changes view: newest first")
    lst.add_argument("--entity", nargs=2, metavar=("TYPE", "ID"), help="one entity, e.g. product_variant 17373")
    lst.add_argument("--batch", help="a batch uid")
    lst.add_argument("--pass", dest="pass_name", help="a pass name")
    lst.add_argument("--since", help="an ISO timestamp")
    lst.add_argument("--limit", type=int, default=200)
    lst.set_defaults(func=cmd_list)

    ub = dsub.add_parser("undo-batch", help="reverse a whole batch in one transaction, by uid")
    ub.add_argument("uid")
    ub.add_argument("--reason", required=True)
    ub.add_argument("--by", required=True, help="the account username the undo is recorded against")
    ub.add_argument("--force", action="store_true", help="run even while a collection is running")
    ub.set_defaults(func=cmd_undo_batch)

    vf = dsub.add_parser("verify", help="read-only: every effective decision against its column; non-zero on drift")
    vf.set_defaults(func=cmd_verify)

    try:
        from app.cli_replay import register_replay

        register_replay(dsub)
    except ImportError:
        pass
