from __future__ import annotations

import argparse
import csv
import sqlite3
import sys
import time
from datetime import datetime
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Tuple, Union

from store_db.csv_safe import csv_safe_row
from store_db.db import connect
from store_db.operation_log import commit_with_log, log_read
from store_db.response import emit_error, emit_need_confirm, emit_ok

SKILL = "store-inventory"


def _get_item(conn: sqlite3.Connection, key: str):
    return conn.execute(
        "SELECT * FROM inv_items WHERE key=? AND status='active'", (key,)
    ).fetchone()


def _log(
    conn: sqlite3.Connection,
    *,
    key: str,
    direction: str,
    typ: str,
    qty: float,
    before: float,
    after: float,
    remark: str,
    ref_appt_id: Optional[str] = None,
    ref_order_id: Optional[str] = None,
) -> None:
    conn.execute(
        """INSERT INTO inv_logs (item_key, direction, type, quantity, before_qty, after_qty,
           ref_appt_id, ref_order_id, remark)
           VALUES (?,?,?,?,?,?,?,?,?)""",
        (key, direction, typ, float(qty), float(before), float(after), ref_appt_id, ref_order_id, remark or ""),
    )


def cmd_stock_query(args: argparse.Namespace) -> int:
    t0 = time.perf_counter()
    conn = connect()
    if args.key:
        row = _get_item(conn, args.key)
        if not row:
            emit_error("not_found", {"key": args.key})
        d = dict(row)
        alert = bool(float(d.get("stock_qty") or 0) <= float(d.get("low_stock_threshold") or 0))
        log_read(
            conn, args, skill=SKILL, command="stock-query", table_name="inv_items",
            entity_id=args.key, duration_ms=int((time.perf_counter() - t0) * 1000),
        )
        emit_ok({"item": d, "low_stock": alert})
        return 0

    q = "SELECT * FROM inv_items WHERE status='active'"
    if args.low_stock:
        q += " AND stock_qty <= low_stock_threshold"
    q += " ORDER BY stock_qty ASC, key ASC"
    rows = [dict(r) for r in conn.execute(q).fetchall()]
    log_read(
        conn, args, skill=SKILL, command="stock-query", table_name="inv_items",
        duration_ms=int((time.perf_counter() - t0) * 1000),
    )
    emit_ok({"items": rows, "count": len(rows)})
    return 0


def cmd_stock_in(args: argparse.Namespace) -> int:
    t0 = time.perf_counter()
    key = args.key
    qty = float(args.quantity)
    if qty <= 0:
        emit_error("invalid_quantity")
    conn = connect()
    row = _get_item(conn, key)
    if not row:
        emit_error("not_found", {"key": key})
    before = float(row["stock_qty"] or 0)
    after = before + qty
    conn.execute("BEGIN IMMEDIATE")
    try:
        conn.execute(
            "UPDATE inv_items SET stock_qty=?, updated_at=datetime('now','localtime') WHERE key=?",
            (after, key),
        )
        _log(
            conn,
            key=key,
            direction="in",
            typ=args.type or "purchase",
            qty=qty,
            before=before,
            after=after,
            remark=args.remark or "",
        )
        commit_with_log(
            conn, args, skill=SKILL, command="stock-in", op="update", table_name="inv_items",
            entity_id=key, duration_ms=int((time.perf_counter() - t0) * 1000),
        )
    except Exception:
        conn.rollback()
        raise
    emit_ok({"key": key, "before": before, "after": after})
    return 0


def cmd_stock_out(args: argparse.Namespace) -> int:
    key = args.key
    qty = float(args.quantity)
    if qty <= 0:
        emit_error("invalid_quantity")
    if not args.yes:
        emit_need_confirm({"action": "stock-out", "key": key, "quantity": qty, "type": args.type or "sale"})
        return 0
    t0 = time.perf_counter()
    conn = connect()
    row = _get_item(conn, key)
    if not row:
        emit_error("not_found", {"key": key})
    before = float(row["stock_qty"] or 0)
    if qty > before:
        emit_error("insufficient_stock", {"key": key, "have": before, "need": qty})
    after = before - qty
    conn.execute("BEGIN IMMEDIATE")
    try:
        conn.execute(
            "UPDATE inv_items SET stock_qty=?, updated_at=datetime('now','localtime') WHERE key=?",
            (after, key),
        )
        _log(
            conn,
            key=key,
            direction="out",
            typ=args.type or "sale",
            qty=qty,
            before=before,
            after=after,
            remark=args.remark or "",
            ref_order_id=args.ref_order_id,
        )
        commit_with_log(
            conn, args, skill=SKILL, command="stock-out", op="update", table_name="inv_items",
            entity_id=key, duration_ms=int((time.perf_counter() - t0) * 1000),
        )
    except Exception:
        conn.rollback()
        raise
    emit_ok({"key": key, "before": before, "after": after})
    return 0


def cmd_stock_log(args: argparse.Namespace) -> int:
    t0 = time.perf_counter()
    conn = connect()
    q = "SELECT * FROM inv_logs WHERE 1=1"
    params: List[Any] = []
    if args.key:
        q += " AND item_key=?"
        params.append(args.key)
    if args.direction:
        q += " AND direction=?"
        params.append(args.direction)
    if args.from_date:
        q += " AND created_at>=?"
        params.append(args.from_date)
    if args.to_date:
        q += " AND created_at<=?"
        params.append(args.to_date + " 23:59:59")
    q += " ORDER BY created_at DESC LIMIT 200"
    rows = [dict(r) for r in conn.execute(q, params).fetchall()]
    log_read(
        conn, args, skill=SKILL, command="stock-log", table_name="inv_logs",
        entity_id=args.key, duration_ms=int((time.perf_counter() - t0) * 1000),
    )
    emit_ok({"logs": rows, "count": len(rows)})
    return 0


def cmd_stock_export(args: argparse.Namespace) -> int:
    t0 = time.perf_counter()
    conn = connect()
    ts = datetime.now().strftime("%Y%m%d_%H%M%S")
    export_type = args.type or "items"
    out = args.path or args.output or f"inv_export_{export_type}_{ts}.csv"
    out_path = Path(out)
    out_path.parent.mkdir(parents=True, exist_ok=True)
    if export_type == "items":
        q = "SELECT * FROM inv_items WHERE status='active'"
        if args.low_stock_only:
            q += " AND stock_qty <= low_stock_threshold"
        q += " ORDER BY key"
        rows = conn.execute(q).fetchall()
        fields = [d[1] for d in conn.execute("PRAGMA table_info(inv_items)").fetchall()]
        with out_path.open("w", encoding="utf-8-sig", newline="") as f:
            w = csv.DictWriter(f, fieldnames=fields)
            w.writeheader()
            for r in rows:
                w.writerow(csv_safe_row(dict(r)))
        log_read(
            conn, args, skill=SKILL, command="stock-export", table_name="inv_items",
            duration_ms=int((time.perf_counter() - t0) * 1000),
        )
        emit_ok({"path": str(out_path), "output": str(out_path), "count": len(rows), "type": "items"})
        return 0
    if export_type == "logs":
        rows = conn.execute("SELECT * FROM inv_logs ORDER BY created_at DESC").fetchall()
        fields = [d[1] for d in conn.execute("PRAGMA table_info(inv_logs)").fetchall()]
        with out_path.open("w", encoding="utf-8-sig", newline="") as f:
            w = csv.DictWriter(f, fieldnames=fields)
            w.writeheader()
            for r in rows:
                w.writerow(csv_safe_row(dict(r)))
        log_read(
            conn, args, skill=SKILL, command="stock-export", table_name="inv_logs",
            duration_ms=int((time.perf_counter() - t0) * 1000),
        )
        emit_ok({"path": str(out_path), "output": str(out_path), "count": len(rows), "type": "logs"})
        return 0
    emit_error("invalid_type", {"allowed": ["items", "logs"]})


def cmd_replenish_suggest(args: argparse.Namespace) -> int:
    t0 = time.perf_counter()
    days = int(args.days or 7)
    if days <= 0:
        emit_error("invalid_days")
    conn = connect()
    items = conn.execute("SELECT * FROM inv_items WHERE status='active' ORDER BY key").fetchall()
    suggestions: List[Dict[str, Any]] = []
    for item in items:
        key = item["key"]
        current = float(item["stock_qty"] or 0)
        threshold = float(item["low_stock_threshold"] or 0)
        total_out = conn.execute(
            """SELECT COALESCE(SUM(quantity), 0) FROM inv_logs
               WHERE item_key=? AND direction='out'
               AND created_at >= date('now', ?)""",
            (key, f"-{days} days"),
        ).fetchone()[0]
        avg_daily_out = float(total_out) / days
        target = avg_daily_out * days * 1.2
        suggest_qty = max(0.0, target - current)
        suggestions.append(
            {
                "key": key,
                "current_stock": current,
                "avg_daily_out": round(avg_daily_out, 4),
                "suggest_qty": round(suggest_qty, 4),
                "low_stock": current <= threshold,
                "unit": item["unit"],
            }
        )
    log_read(
        conn, args, skill=SKILL, command="replenish-suggest", table_name="inv_items",
        duration_ms=int((time.perf_counter() - t0) * 1000),
    )
    emit_ok({"suggestions": suggestions, "days": days, "count": len(suggestions)})
    return 0


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

    s = sub.add_parser("stock-query")
    s.add_argument("--key", default=None)
    s.add_argument("--low-stock", action="store_true")
    s.add_argument("--session-id")
    s.set_defaults(func=cmd_stock_query)

    s = sub.add_parser("stock-in")
    s.add_argument("--key", required=True)
    s.add_argument("--quantity", type=float, required=True)
    s.add_argument("--type", default="purchase")
    s.add_argument("--remark", default="")
    s.add_argument("--session-id")
    s.set_defaults(func=cmd_stock_in)

    s = sub.add_parser("stock-out")
    s.add_argument("--key", required=True)
    s.add_argument("--quantity", type=float, required=True)
    s.add_argument("--type", default="sale")
    s.add_argument("--remark", default="")
    s.add_argument("--ref-order-id", default=None)
    s.add_argument("--yes", action="store_true")
    s.add_argument("--session-id")
    s.set_defaults(func=cmd_stock_out)

    s = sub.add_parser("stock-log")
    s.add_argument("--key", default=None)
    s.add_argument("--direction", default=None, choices=["in", "out"])
    s.add_argument("--from", dest="from_date", default=None)
    s.add_argument("--to", dest="to_date", default=None)
    s.add_argument("--session-id")
    s.set_defaults(func=cmd_stock_log)

    s = sub.add_parser("stock-export")
    s.add_argument("--path", default=None, help="导出 CSV 路径（PRD 推荐）")
    s.add_argument("--output", default=None, help="同 --path")
    s.add_argument("--type", choices=["items", "logs"], default="items")
    s.add_argument("--low-stock-only", action="store_true")
    s.add_argument("--session-id")
    s.set_defaults(func=cmd_stock_export)

    s = sub.add_parser("replenish-suggest")
    s.add_argument("--days", type=int, default=7)
    s.add_argument("--session-id")
    s.set_defaults(func=cmd_replenish_suggest)

    return p


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


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