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_order_lib.helpers import DATE_FMT, OrderError, query_service_materials
from store_settlement.inventory import inventory_stock_in, 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", "settle_order", "refund_order"]


def _load_order_lines(conn: sqlite3.Connection, order_id: str) -> List[sqlite3.Row]:
    return list(
        conn.execute(
            "SELECT * FROM store_order_lines WHERE order_id=? ORDER BY id",
            (order_id,),
        ).fetchall()
    )


def _apply_line_performances(
    conn: sqlite3.Connection,
    *,
    order_id: str,
    order_lines: List[sqlite3.Row],
    staff_lines_json: Optional[str],
    primary_staff_id: Optional[str],
    ymd: str,
) -> List[Dict[str, Any]]:
    staff_map: Dict[int, List[Dict[str, Any]]] = {}
    if staff_lines_json:
        for item in json.loads(staff_lines_json):
            lid = int(item.get("line", item.get("line_id", 0)))
            staff_map.setdefault(lid, []).append(item)
    else:
        for ol in order_lines:
            lid = int(ol["id"])
            rows = conn.execute(
                "SELECT * FROM store_order_line_staff WHERE line_id=?",
                (lid,),
            ).fetchall()
            if rows:
                staff_map[lid] = [
                    {
                        "staff_id": r["staff_id"],
                        "role": r["role"],
                        "split": float(r["split_ratio"]),
                    }
                    for r in rows
                ]
            elif primary_staff_id:
                staff_map[lid] = [
                    {"staff_id": primary_staff_id, "role": "primary", "split": 1.0},
                ]

    created: List[Dict[str, Any]] = []
    for ol in order_lines:
        lid = int(ol["id"])
        kind = ol["kind"]
        target = ol["ref_name"]
        line_amount = float(ol["line_amount"])
        assignments = staff_map.get(lid) or []
        if not assignments:
            continue
        split_sum = sum(float(a.get("split", a.get("split_ratio", 1.0))) for a in assignments)
        if assignments and not amounts_equal(split_sum, 1.0):
            raise OrderError("split_ratio_invalid", {"line_id": lid, "sum": split_sum})
        for a in assignments:
            split = float(a.get("split", a.get("split_ratio", 1.0)))
            staff_id = str(a["staff_id"])
            amt = round(line_amount * split, 2)
            conn.execute(
                """INSERT INTO staff_performance
                   (staff_id, ymd, kind, target_name, amount, source_order_id, line_id)
                   VALUES (?,?,?,?,?,?,?)""",
                (staff_id, ymd, kind, target, amt, order_id, lid),
            )
            created.append(
                {"line_id": lid, "staff_id": staff_id, "kind": kind, "amount": amt, "split": split}
            )
    return created


def settle_order(
    conn: sqlite3.Connection,
    *,
    order_id: str,
    pay: str,
    lines_json: Optional[str],
    staff_lines_json: Optional[str],
    card_name: Optional[str],
    cash_amount: Optional[float],
    primary_staff_id: Optional[str],
    audit: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
    order = conn.execute("SELECT * FROM store_orders WHERE id=?", (order_id,)).fetchone()
    if not order or order["status"] not in ("draft", "confirmed", "in_progress"):
        raise OrderError("invalid_order", {"order_id": order_id, "status": order["status"] if order else None})

    member_id = order["member_id"]
    total = float(order["total_amount"])
    order_lines = _load_order_lines(conn, order_id)
    if not order_lines:
        raise OrderError("empty_order", {"order_id": order_id})

    first_service = next((ol["ref_name"] for ol in order_lines if ol["kind"] == "service"), None)
    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)
    staff_for_pay = primary_staff_id
    if not staff_for_pay:
        row = conn.execute(
            "SELECT staff_id FROM store_order_line_staff ols "
            "JOIN store_order_lines ol ON ol.id=ols.line_id WHERE ol.order_id=? LIMIT 1",
            (order_id,),
        ).fetchone()
        staff_for_pay = row["staff_id"] if row else None

    conn.execute("BEGIN IMMEDIATE")
    try:
        for pl in pay_lines:
            apply_payment_line(
                conn,
                order_id=order_id,
                member_id=member_id,
                staff_id=staff_for_pay,
                service_name=first_service if pl["type"] == "card_use" else None,
                line=pl,
                card_name=card_name,
            )

        for ol in order_lines:
            if ol["kind"] == "service":
                mats = query_service_materials(conn, ol["ref_name"])
                for pname, qty in mats.items():
                    inventory_stock_out(conn, pname, qty, order_id=order_id, log_type="sale")
            elif ol["kind"] == "product":
                inventory_stock_out(conn, ol["ref_name"], float(ol["qty"]), order_id=order_id, log_type="sale")

        performances = _apply_line_performances(
            conn,
            order_id=order_id,
            order_lines=order_lines,
            staff_lines_json=staff_lines_json,
            primary_staff_id=primary_staff_id,
            ymd=today,
        )

        if member_id:
            fav = first_service or order_lines[0]["ref_name"]
            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, fav, 1),
            )

        conn.execute(
            """UPDATE store_orders SET status='completed', updated_at=datetime('now','localtime')
               WHERE id=?""",
            (order_id,),
        )
        if audit:
            write_operation_log(
                conn,
                session_id=audit.get("session_id"),
                skill=str(audit.get("skill", "store-order")),
                command=str(audit.get("command", "order-settle")),
                op="update",
                table_name="store_orders",
                entity_id=order_id,
                request_json=audit.get("request_json"),
                before_json=None,
                after_json={"order_id": order_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 {
        "order_id": order_id,
        "pay": pay,
        "total_amount": total,
        "payments": pay_lines,
        "performances": performances,
        "lines": [dict(ol) for ol in order_lines],
    }


def refund_order(conn: sqlite3.Connection, *, order_id: str, audit: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
    order = conn.execute("SELECT * FROM store_orders WHERE id=?", (order_id,)).fetchone()
    if not order or order["status"] != "completed":
        raise OrderError("invalid_order", {"order_id": order_id, "status": order["status"] if order else None})

    member_id = order["member_id"]
    payments = conn.execute(
        "SELECT * FROM store_payments WHERE order_id=? ORDER BY id",
        (order_id,),
    ).fetchall()

    conn.execute("BEGIN IMMEDIATE")
    try:
        for p in payments:
            typ = p["type"]
            amount = float(p["amount"])
            meta = json.loads(p["meta_json"] or "{}")
            if typ == "consume" and member_id:
                conn.execute(
                    """UPDATE store_wallet SET balance=balance+?, total_consume=total_consume-?,
                       updated_at=datetime('now','localtime') WHERE member_id=?""",
                    (amount, amount, member_id),
                )
                conn.execute(
                    """INSERT INTO store_transactions (member_id, type, amount, ref_order_id, meta_json)
                       VALUES (?,?,?,?,?)""",
                    (
                        member_id,
                        "refund_consume",
                        amount,
                        order_id,
                        json.dumps({"refund_of_payment": p["id"]}, ensure_ascii=False),
                    ),
                )
            elif typ == "cash" and member_id:
                conn.execute(
                    """INSERT INTO store_transactions (member_id, type, amount, ref_order_id, meta_json)
                       VALUES (?,?,?,?,?)""",
                    (
                        member_id,
                        "refund_cash",
                        amount,
                        order_id,
                        json.dumps({"refund_of_payment": p["id"]}, ensure_ascii=False),
                    ),
                )
            elif typ == "card_use" and member_id:
                card_id = meta.get("card_id")
                if card_id:
                    conn.execute(
                        """UPDATE store_cards SET used_times=CASE WHEN used_times>0 THEN used_times-1 ELSE 0 END
                           WHERE id=?""",
                        (card_id,),
                    )
                conn.execute(
                    """INSERT INTO store_transactions (member_id, type, amount, ref_order_id, meta_json)
                       VALUES (?,?,?,?,?)""",
                    (
                        member_id,
                        "refund_card",
                        0,
                        order_id,
                        json.dumps({"refund_of_payment": p["id"]}, ensure_ascii=False),
                    ),
                )
            conn.execute(
                "INSERT INTO store_payments (order_id, type, amount, meta_json) VALUES (?,?,?,?)",
                (
                    order_id,
                    f"refund_{typ}",
                    -amount,
                    json.dumps({"refund_of": p["id"]}, ensure_ascii=False),
                ),
            )

        logs = conn.execute(
            "SELECT * FROM inv_logs WHERE ref_order_id=? AND direction='out' ORDER BY id",
            (order_id,),
        ).fetchall()
        for log in logs:
            inventory_stock_in(conn, log["item_key"], float(log["quantity"]), order_id)

        conn.execute("DELETE FROM staff_performance WHERE source_order_id=?", (order_id,))

        if member_id:
            conn.execute(
                """UPDATE store_consumption_log SET visit_count=CASE WHEN visit_count>0 THEN visit_count-1 ELSE 0 END,
                   updated_at=datetime('now','localtime') WHERE member_id=?""",
                (member_id,),
            )

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

    return {
        "order_id": order_id,
        "status": "refunded",
        "payments_reversed": len(payments),
        "inventory_restored": len(logs),
    }
