from __future__ import annotations

import json
import sqlite3
from datetime import datetime
from typing import Any, Dict, List, Optional

from store_db.operation_log import write_operation_log  # noqa: E402
from store_settlement.catalog import DATE_FMT, query_service_materials, query_service_price_and_duration
from store_settlement.errors import SettlementError
from store_settlement.inventory import inventory_stock_out
from store_settlement.money import amounts_equal
from store_settlement.payment import (
    PAY_MODES,
    apply_payment_line,
    resolve_payment_lines,
    validate_payment_lines,
)

__all__ = ["PAY_MODES", "complete_appointment"]


def apply_staff_performance(
    conn: sqlite3.Connection,
    *,
    appt_id: str,
    service_name: str,
    total_amount: float,
    technician_id: str,
    staff_lines_json: Optional[str],
    ymd: str,
) -> List[Dict[str, Any]]:
    if staff_lines_json:
        raw = json.loads(staff_lines_json)
        if not isinstance(raw, list) or not raw:
            raise SettlementError("invalid_staff_lines_json")
        lines = raw
    else:
        lines = [
            {"line": 0, "staff_id": technician_id, "role": "primary", "split": 1.0},
        ]

    splits: Dict[int, float] = {}
    created: List[Dict[str, Any]] = []
    for item in lines:
        split = float(item.get("split", item.get("split_ratio", 1.0)))
        line_no = int(item.get("line", 0))
        splits[line_no] = splits.get(line_no, 0.0) + split
        staff_id = str(item["staff_id"])
        role = str(item.get("role", "primary"))
        amt = round(total_amount * split, 2)
        conn.execute(
            """INSERT INTO staff_performance
               (staff_id, ymd, kind, target_name, amount, source_appt_id, line_id)
               VALUES (?,?,?,?,?,?,?)""",
            (staff_id, ymd, "service", service_name, amt, appt_id, line_no),
        )
        created.append({"staff_id": staff_id, "role": role, "amount": amt, "split": split})

    for line_no, total_split in splits.items():
        if total_split > 0 and not amounts_equal(total_split, 1.0):
            raise SettlementError("split_ratio_invalid", {"line": line_no, "sum": total_split})
    return created


def complete_appointment(
    conn: sqlite3.Connection,
    *,
    appt_id: str,
    pay: str,
    lines_json: Optional[str],
    staff_lines_json: Optional[str],
    card_name: Optional[str],
    cash_amount: Optional[float],
    audit: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
    row = conn.execute("SELECT * FROM store_appointments WHERE id=?", (appt_id,)).fetchone()
    if not row or row["status"] != "scheduled":
        raise SettlementError("invalid_appt", {"appt_id": appt_id})

    member_id = row["member_id"]
    service_name = row["service_name"]
    technician_id = row["technician_id"]
    price, _ = query_service_price_and_duration(conn, service_name)
    total = float(price)

    pay_lines = resolve_payment_lines(
        pay=pay,
        total=total,
        lines_json=lines_json,
        card_name=card_name,
        cash_amount=cash_amount,
    )
    validate_payment_lines(pay_lines, total, pay)

    today = datetime.now().strftime(DATE_FMT)
    perf_ymd = (row["start_dt"] or "")[:10] or today

    conn.execute("BEGIN IMMEDIATE")
    try:
        for pl in pay_lines:
            apply_payment_line(
                conn,
                appt_id=appt_id,
                member_id=member_id,
                staff_id=technician_id,
                service_name=service_name,
                line=pl,
                card_name=card_name,
            )

        mats = query_service_materials(conn, service_name)
        for pname, qty in mats.items():
            inventory_stock_out(conn, pname, qty, appt_id=appt_id, log_type="service")

        performances = apply_staff_performance(
            conn,
            appt_id=appt_id,
            service_name=service_name,
            total_amount=total,
            technician_id=technician_id,
            staff_lines_json=staff_lines_json,
            ymd=perf_ymd,
        )

        conn.execute(
            """INSERT INTO store_consumption_log (member_id, last_visit_at, fav_service, visit_count, updated_at)
               VALUES (?,?,?,?,datetime('now','localtime'))
               ON CONFLICT(member_id) DO UPDATE SET
                 last_visit_at=excluded.last_visit_at,
                 fav_service=excluded.fav_service,
                 visit_count=store_consumption_log.visit_count+1,
                 updated_at=datetime('now','localtime')""",
            (member_id, today, service_name, 1),
        )

        conn.execute(
            """UPDATE store_appointments SET status='completed', updated_at=datetime('now','localtime')
               WHERE id=?""",
            (appt_id,),
        )
        if audit:
            write_operation_log(
                conn,
                session_id=audit.get("session_id"),
                skill=str(audit.get("skill", "store-appointment")),
                command=str(audit.get("command", "appt-complete")),
                op="update",
                table_name="store_appointments",
                entity_id=appt_id,
                request_json=audit.get("request_json"),
                before_json=None,
                after_json={"appt_id": appt_id, "status": "completed"},
                result="ok",
                error_code=None,
                confirmed=True,
                duration_ms=int(audit.get("duration_ms", 0)),
            )
        conn.commit()
    except Exception:
        conn.rollback()
        raise

    return {
        "appt_id": appt_id,
        "pay": pay,
        "total_amount": total,
        "payments": pay_lines,
        "performances": performances,
    }
