from __future__ import annotations

import argparse
import json
import sys
import time
from typing import Any, Dict, List

from store_db.db import connect
from store_db.ids import next_id
from store_db.operation_log import commit_with_log, log_read, request_dict
from store_db.response import emit_error, emit_need_confirm, emit_ok

from store_order_lib.helpers import OrderError, query_product_price, query_service_price  # noqa: E402
from store_order_lib.settlement import PAY_MODES, refund_order, settle_order  # noqa: E402

SKILL = "store-order"


def _parse_order_lines(raw: str) -> List[Dict[str, Any]]:
    data = json.loads(raw)
    if not isinstance(data, list) or not data:
        raise OrderError("invalid_lines_json")
    return data


def cmd_order_create(args: argparse.Namespace) -> int:
    t0 = time.perf_counter()
    conn = connect()
    try:
        items = _parse_order_lines(args.lines_json)
    except OrderError as e:
        emit_error(e.code, e.detail if e.detail else None)
    except json.JSONDecodeError:
        emit_error("invalid_lines_json")

    oid = args.order_id or next_id(conn, "O", "store_orders")
    total = 0.0
    line_rows: List[Dict[str, Any]] = []

    conn.execute("BEGIN IMMEDIATE")
    try:
        conn.execute(
            """INSERT INTO store_orders (id, member_id, status, total_amount, remark)
               VALUES (?,?, 'draft', 0, ?)""",
            (oid, args.member_id, args.remark or ""),
        )

        for item in items:
            kind = str(item.get("kind") or "")
            name = str(item.get("name") or "")
            qty = float(item.get("qty", 1))
            if kind not in ("service", "product"):
                raise OrderError("invalid_line_kind", {"kind": kind})
            if kind == "service":
                unit = query_service_price(conn, name)
            else:
                unit = query_product_price(conn, name)
            line_amount = round(unit * qty, 2)
            total += line_amount
            cur = conn.execute(
                """INSERT INTO store_order_lines (order_id, kind, ref_name, qty, unit_price, line_amount)
                   VALUES (?,?,?,?,?,?)""",
                (oid, kind, name, qty, unit, line_amount),
            )
            line_id = int(cur.lastrowid)
            staff_id = item.get("staff_id")
            if staff_id:
                split = float(item.get("split", item.get("split_ratio", 1.0)))
                role = str(item.get("role", "primary"))
                conn.execute(
                    """INSERT INTO store_order_line_staff (line_id, staff_id, role, split_ratio)
                       VALUES (?,?,?,?)""",
                    (line_id, staff_id, role, split),
                )
            line_rows.append(
                {"line_id": line_id, "kind": kind, "name": name, "qty": qty, "line_amount": line_amount}
            )

        conn.execute(
            "UPDATE store_orders SET total_amount=?, updated_at=datetime('now','localtime') WHERE id=?",
            (total, oid),
        )
        commit_with_log(
            conn, args, skill=SKILL, command="order-create", op="create", table_name="store_orders",
            entity_id=oid, duration_ms=int((time.perf_counter() - t0) * 1000),
        )
    except Exception as e:
        conn.rollback()
        if isinstance(e, OrderError):
            emit_error(e.code, e.detail if e.detail else None)
        emit_error("internal_error", {"type": type(e).__name__})
    emit_ok({"order_id": oid, "total_amount": total, "lines": line_rows, "status": "draft"})
    return 0


def cmd_order_query(args: argparse.Namespace) -> int:
    t0 = time.perf_counter()
    conn = connect()
    if args.order_id:
        order = conn.execute("SELECT * FROM store_orders WHERE id=?", (args.order_id,)).fetchone()
        if not order:
            log_read(
                conn, args, skill=SKILL, command="order-query", table_name="store_orders",
                entity_id=args.order_id, duration_ms=int((time.perf_counter() - t0) * 1000),
            )
            emit_ok({"orders": [], "count": 0})
            return 0
        lines = [
            dict(r)
            for r in conn.execute(
                "SELECT * FROM store_order_lines WHERE order_id=? ORDER BY id",
                (args.order_id,),
            )
        ]
        for ln in lines:
            staff = conn.execute(
                "SELECT * FROM store_order_line_staff WHERE line_id=?",
                (ln["id"],),
            ).fetchall()
            ln["staff"] = [dict(s) for s in staff]
        log_read(
            conn, args, skill=SKILL, command="order-query", table_name="store_orders",
            entity_id=args.order_id, duration_ms=int((time.perf_counter() - t0) * 1000),
        )
        emit_ok({"orders": [{**dict(order), "lines": lines}], "count": 1})
        return 0

    q = "SELECT * FROM store_orders WHERE 1=1"
    params: List[Any] = []
    if not args.include_cancelled:
        q += " AND status NOT IN ('cancelled','refunded')"
    if args.status:
        q += " AND status=?"
        params.append(args.status)
    if args.member_id:
        q += " AND member_id=?"
        params.append(args.member_id)
    q += " ORDER BY created_at DESC LIMIT ?"
    params.append(args.limit or 100)
    rows = [dict(r) for r in conn.execute(q, params)]
    log_read(
        conn, args, skill=SKILL, command="order-query", table_name="store_orders",
        duration_ms=int((time.perf_counter() - t0) * 1000),
    )
    emit_ok({"orders": rows, "count": len(rows)})
    return 0


def cmd_order_cancel(args: argparse.Namespace) -> int:
    if not args.yes:
        emit_need_confirm({"action": "order-cancel", "order_id": args.order_id})
        return 0
    t0 = time.perf_counter()
    conn = connect()
    row = conn.execute("SELECT status FROM store_orders WHERE id=?", (args.order_id,)).fetchone()
    if not row:
        emit_error("not_found", {"order_id": args.order_id})
    if row["status"] not in ("draft", "confirmed", "in_progress"):
        emit_error("invalid_order", {"status": row["status"]})
    conn.execute(
        """UPDATE store_orders SET status='cancelled', updated_at=datetime('now','localtime')
           WHERE id=?""",
        (args.order_id,),
    )
    commit_with_log(
        conn, args, skill=SKILL, command="order-cancel", op="update", table_name="store_orders",
        entity_id=args.order_id, duration_ms=int((time.perf_counter() - t0) * 1000),
    )
    emit_ok({"order_id": args.order_id, "status": "cancelled"})
    return 0


def cmd_order_settle(args: argparse.Namespace) -> int:
    t0 = time.perf_counter()
    conn = connect()
    order = conn.execute("SELECT * FROM store_orders WHERE id=?", (args.order_id,)).fetchone()
    if not order:
        emit_error("not_found", {"order_id": args.order_id})
    if not args.yes:
        emit_need_confirm(
            {
                "action": "order-settle",
                "order_id": args.order_id,
                "member_id": order["member_id"],
                "total_amount": order["total_amount"],
                "pay": args.pay,
            }
        )
        return 0
    audit = {
        "session_id": getattr(args, "session_id", None),
        "skill": SKILL,
        "command": "order-settle",
        "request_json": request_dict(args),
        "duration_ms": int((time.perf_counter() - t0) * 1000),
    }
    try:
        summary = settle_order(
            conn,
            order_id=args.order_id,
            pay=args.pay,
            lines_json=args.lines_json,
            staff_lines_json=args.staff_lines_json,
            card_name=args.card_name,
            cash_amount=args.cash_amount,
            primary_staff_id=args.primary_staff_id,
            audit=audit,
        )
    except OrderError as e:
        emit_error(e.code, e.detail if e.detail else None)
    emit_ok(summary)
    return 0


def cmd_order_refund(args: argparse.Namespace) -> int:
    if not args.yes:
        emit_need_confirm({"action": "order-refund", "order_id": args.order_id})
        return 0
    t0 = time.perf_counter()
    conn = connect()
    audit = {
        "session_id": getattr(args, "session_id", None),
        "skill": SKILL,
        "command": "order-refund",
        "request_json": request_dict(args),
        "duration_ms": int((time.perf_counter() - t0) * 1000),
    }
    try:
        summary = refund_order(conn, order_id=args.order_id, audit=audit)
    except OrderError as e:
        emit_error(e.code, e.detail if e.detail else None)
    emit_ok(summary)
    return 0


def build_parser() -> argparse.ArgumentParser:
    p = argparse.ArgumentParser(description="Store order CLI")
    sub = p.add_subparsers(dest="cmd", required=True)

    s = sub.add_parser("order-create")
    s.add_argument("--member-id")
    s.add_argument("--lines-json", required=True)
    s.add_argument("--order-id")
    s.add_argument("--remark", default="")
    s.add_argument("--session-id")
    s.set_defaults(func=cmd_order_create)

    s = sub.add_parser("order-query")
    s.add_argument("--order-id")
    s.add_argument("--member-id")
    s.add_argument("--status")
    s.add_argument("--include-cancelled", action="store_true")
    s.add_argument("--limit", type=int)
    s.add_argument("--session-id")
    s.set_defaults(func=cmd_order_query)

    s = sub.add_parser("order-cancel")
    s.add_argument("--order-id", required=True)
    s.add_argument("--yes", action="store_true")
    s.add_argument("--session-id")
    s.set_defaults(func=cmd_order_cancel)

    s = sub.add_parser("order-settle")
    s.add_argument("--order-id", required=True)
    s.add_argument("--pay", default="stored_value", choices=list(PAY_MODES))
    s.add_argument("--lines-json")
    s.add_argument("--staff-lines-json")
    s.add_argument("--card-name")
    s.add_argument("--cash-amount", type=float)
    s.add_argument("--primary-staff-id")
    s.add_argument("--yes", action="store_true")
    s.add_argument("--session-id")
    s.set_defaults(func=cmd_order_settle)

    s = sub.add_parser("order-refund")
    s.add_argument("--order-id", required=True)
    s.add_argument("--yes", action="store_true")
    s.add_argument("--session-id")
    s.set_defaults(func=cmd_order_refund)

    return p


def main() -> int:
    args = build_parser().parse_args()
    return int(args.func(args))


if __name__ == "__main__":
    sys.exit(main())
