"""Insurance product analysis CLI."""
from __future__ import annotations

import argparse
import json
import sqlite3
import sys
from datetime import datetime, timedelta
from typing import Any

from insurance_product_cli import db, orchestrator

try:
    sys.stdout.reconfigure(encoding="utf-8")
    sys.stderr.reconfigure(encoding="utf-8")
except (AttributeError, OSError):
    pass

CACHE_TTL_DAYS = 7


def print_json(obj: Any) -> None:
    print(json.dumps(obj, ensure_ascii=False, indent=2))


# ---------- product CRUD ----------

def cmd_product_add(conn: sqlite3.Connection, args: argparse.Namespace) -> int:
    highlights = []
    if args.highlights:
        if "|" in args.highlights:
            highlights = [s.strip() for s in args.highlights.split("|") if s.strip()]
        else:
            try:
                highlights = json.loads(args.highlights)
            except json.JSONDecodeError:
                highlights = [args.highlights]
    cov_min, cov_max = _parse_range(args.coverage_range)
    pre_min, pre_max = _parse_range(args.premium_range)
    age_min, age_max = _parse_age_range(args.age_range)
    cur = conn.cursor()
    try:
        cur.execute(
            """INSERT INTO products
               (name, company, product_type, coverage_min, coverage_max,
                premium_min, premium_max, age_min, age_max,
                waiting_period, deductible, renewal_terms,
                highlights_json, target_audience, file_path, raw_text)
               VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
            (
                args.name,
                args.company,
                args.product_type,
                cov_min,
                cov_max,
                pre_min,
                pre_max,
                age_min,
                age_max,
                args.waiting_period or "",
                args.deductible or "",
                args.renewal_terms or "",
                json.dumps(highlights, ensure_ascii=False),
                args.target_audience or "",
                args.file or "",
                args.raw_text or "",
            ),
        )
    except sqlite3.IntegrityError:
        print_json({"ok": False, "error": "duplicate_product"})
        return 1
    conn.commit()
    print_json({"ok": True, "id": cur.lastrowid, "name": args.name})
    return 0


def _parse_range(s: str | None) -> tuple[float | None, float | None]:
    if not s:
        return None, None
    s2 = s.replace("万", "*10000").replace("元", "").replace(" ", "")
    if "-" not in s2:
        return None, None
    a, b = s2.split("-", 1)
    try:
        return float(eval(a)), float(eval(b))  # noqa: S307 - controlled input
    except (ValueError, SyntaxError, NameError):
        return None, None


def _parse_age_range(s: str | None) -> tuple[int | None, int | None]:
    if not s:
        return None, None
    s2 = s.replace("岁", "").replace(" ", "")
    if "-" not in s2:
        return None, None
    a, b = s2.split("-", 1)
    try:
        return int(a), int(b)
    except ValueError:
        return None, None


def _row_product(row: sqlite3.Row) -> dict:
    d = dict(row)
    if "highlights_json" in d and isinstance(d["highlights_json"], str):
        try:
            d["highlights"] = json.loads(d["highlights_json"])
        except json.JSONDecodeError:
            d["highlights"] = []
    return d


def cmd_product_query(conn: sqlite3.Connection, args: argparse.Namespace) -> int:
    cur = conn.cursor()
    rows: list[sqlite3.Row]
    if args.name:
        rows = list(cur.execute("SELECT * FROM products WHERE name=? AND status='active'", (args.name,)))
    elif args.product_type:
        rows = list(cur.execute("SELECT * FROM products WHERE product_type=? AND status='active' ORDER BY name", (args.product_type,)))
    elif args.company:
        rows = list(cur.execute("SELECT * FROM products WHERE company=? AND status='active' ORDER BY name", (args.company,)))
    else:
        rows = list(cur.execute("SELECT * FROM products WHERE status='active' ORDER BY name"))
    print_json({"ok": True, "products": [_row_product(r) for r in rows], "count": len(rows)})
    return 0


def cmd_product_update(conn: sqlite3.Connection, args: argparse.Namespace) -> int:
    try:
        payload = json.loads(args.set)
    except json.JSONDecodeError as e:
        print_json({"ok": False, "error": "bad_json", "hint": str(e)})
        return 1
    row = conn.execute("SELECT id FROM products WHERE name=? AND status='active'", (args.name,)).fetchone()
    if not row:
        print_json({"ok": False, "error": "product_not_found"})
        return 1
    mapping = {
        "name": "name",
        "company": "company",
        "product_type": "product_type",
        "coverage_min": "coverage_min",
        "coverage_max": "coverage_max",
        "premium_min": "premium_min",
        "premium_max": "premium_max",
        "age_min": "age_min",
        "age_max": "age_max",
        "waiting_period": "waiting_period",
        "deductible": "deductible",
        "renewal_terms": "renewal_terms",
        "target_audience": "target_audience",
        "file_path": "file_path",
        "raw_text": "raw_text",
    }
    sets, vals = [], []
    for k, v in payload.items():
        col = mapping.get(k)
        if col is not None:
            sets.append(f"{col}=?")
            vals.append(v)
        elif k == "highlights":
            sets.append("highlights_json=?")
            vals.append(json.dumps(v, ensure_ascii=False))
    if sets:
        sets.append("updated_at=datetime('now','localtime')")
        vals.append(row["id"])
        conn.execute(f"UPDATE products SET {', '.join(sets)} WHERE id=?", vals)
        conn.commit()
        # invalidate cache
        conn.execute("DELETE FROM product_analysis_cache WHERE product_id=?", (row["id"],))
        conn.commit()
    print_json({"ok": True, "id": row["id"]})
    return 0


def cmd_product_delete(conn: sqlite3.Connection, args: argparse.Namespace) -> int:
    row = conn.execute("SELECT id FROM products WHERE name=? AND status='active'", (args.name,)).fetchone()
    if not row:
        print_json({"ok": False, "error": "product_not_found"})
        return 1
    if not args.yes:
        print_json({"ok": False, "error": "need_confirm", "hint": "请加 --yes 确认删除"})
        return 1
    conn.execute(
        "UPDATE products SET status='deleted', updated_at=datetime('now','localtime') WHERE id=?",
        (row["id"],),
    )
    conn.commit()
    print_json({"ok": True})
    return 0


# ---------- product-analyze ----------

def cmd_product_analyze(conn: sqlite3.Connection, args: argparse.Namespace) -> int:
    row = conn.execute(
        "SELECT * FROM products WHERE name=? AND status='active'", (args.product,)
    ).fetchone()
    if not row:
        print_json({"ok": False, "error": "product_not_found"})
        return 1
    pid = row["id"]
    cache = conn.execute(
        "SELECT * FROM product_analysis_cache WHERE product_id=?", (pid,)
    ).fetchone()
    if cache and not args.refresh:
        try:
            analyzed = datetime.strptime(cache["analyzed_at"][:19], "%Y-%m-%d %H:%M:%S")
            fresh = (datetime.now() - analyzed) < timedelta(days=CACHE_TTL_DAYS)
        except (ValueError, TypeError):
            fresh = False
        if fresh:
            result = {
                "key_terms": json.loads(cache["key_terms_json"] or "[]"),
                "selling_points": json.loads(cache["selling_points_json"] or "[]"),
                "fit_audience": json.loads(cache["fit_audience_json"] or "[]"),
                "pitch_script": cache["pitch_script"] or "",
                "analyzed_at": cache["analyzed_at"],
                "from_cache": True,
            }
            payload: dict[str, Any] = {"ok": True, "product": _row_product(row), "result": result}
            if args.match_customers:
                payload["customer_matches"] = _match_customers_for_product(_row_product(row))
            print_json(payload)
            return 0
    # No fresh cache → return needs_llm hook (script does not call LLM directly)
    print_json(
        {
            "ok": False,
            "error": "needs_llm",
            "hint": "解读未命中缓存，请由上层 agent 用 LLM 填空后调 analysis-write 写回。",
            "product": _row_product(row),
            "raw_text": row["raw_text"] or "",
            "prompt_template": _analyze_prompt_template(_row_product(row)),
        }
    )
    return 1


def _analyze_prompt_template(product: dict) -> str:
    return (
        "请把下列保险产品资料解读为 4 段：\n"
        "1) 条款要点（保什么/不保什么/等待期/免赔额/续保条件）\n"
        "2) 核心卖点（3-5 个，相对同类产品的优势）\n"
        "3) 适用人群（年龄/收入/家庭结构/已有保单维度）\n"
        "4) 话术建议（怎么向客户开口，避免理赔承诺/夸大）\n\n"
        f"产品名：{product.get('name')}\n"
        f"公司：{product.get('company')}\n"
        f"类型：{product.get('product_type')}\n"
        f"亮点：{product.get('highlights')}\n"
        f"原始资料：{product.get('raw_text', '')}\n"
        "请返回 JSON：{key_terms:[...], selling_points:[...], fit_audience:[...], pitch_script:'...'}"
    )


def cmd_analysis_write(conn: sqlite3.Connection, args: argparse.Namespace) -> int:
    row = conn.execute("SELECT id FROM products WHERE name=? AND status='active'", (args.product,)).fetchone()
    if not row:
        print_json({"ok": False, "error": "product_not_found"})
        return 1
    pid = row["id"]
    try:
        if args.result_file:
            with open(args.result_file, encoding="utf-8") as f:
                result = json.load(f)
        else:
            result = json.loads(args.result)
    except (json.JSONDecodeError, OSError) as e:
        print_json({"ok": False, "error": "bad_json", "hint": str(e)})
        return 1
    conn.execute(
        """INSERT OR REPLACE INTO product_analysis_cache
           (product_id, key_terms_json, selling_points_json, fit_audience_json, pitch_script, analyzed_at)
           VALUES (?,?,?,?,?, datetime('now','localtime'))""",
        (
            pid,
            json.dumps(result.get("key_terms") or [], ensure_ascii=False),
            json.dumps(result.get("selling_points") or [], ensure_ascii=False),
            json.dumps(result.get("fit_audience") or [], ensure_ascii=False),
            result.get("pitch_script") or "",
        ),
    )
    conn.commit()
    print_json({"ok": True, "product_id": pid})
    return 0


# ---------- gap-analysis (forward to customer-policy) ----------

def cmd_gap_analysis(conn: sqlite3.Connection, args: argparse.Namespace) -> int:
    data = orchestrator.gap_analysis(
        customer_id=args.customer_id,
        gap_type=args.gap_type,
        all_customers=bool(args.all),
    )
    print_json(data)
    return 0 if data.get("ok") else 1


# ---------- customer-match ----------

def _match_customers_for_product(product: dict) -> list[dict]:
    seg = orchestrator.customer_segment()
    if not seg.get("ok"):
        return []
    candidates: list[dict] = []
    seen: set[str] = set()
    for bucket in ("hi_value", "active", "new", "dormant"):
        for c in seg.get(bucket) or []:
            cid = c.get("customer_id")
            if not cid or cid in seen:
                continue
            seen.add(cid)
            cust_data = orchestrator.customer_query(customer_id=cid)
            cust = (cust_data.get("customers") or [{}])[0]
            age = cust.get("age")
            income = cust.get("income")
            if product.get("age_min") is not None and age is not None and age < product["age_min"]:
                continue
            if product.get("age_max") is not None and age is not None and age > product["age_max"]:
                continue
            gap = orchestrator.gap_analysis(customer_id=cid)
            gaps = (gap.get("result") or {}).get("gaps") or []
            score = 0
            if product.get("product_type") in gaps:
                score += 100
            if bucket == "hi_value":
                score += 50
            if bucket == "active":
                score += 20
            if bucket == "new":
                score += 10
            if income and product.get("premium_max") and income > 0:
                if product["premium_max"] / income < 0.2:
                    score += 5
            candidates.append({
                "customer_id": cid,
                "name": c.get("name"),
                "age": age,
                "income": income,
                "bucket": bucket,
                "matches_gap": product.get("product_type") in gaps,
                "score": score,
            })
    candidates.sort(key=lambda r: r["score"], reverse=True)
    return candidates


def _match_products_for_customer(conn: sqlite3.Connection, customer_id: str) -> list[dict]:
    cust = orchestrator.customer_query(customer_id=customer_id)
    rows = cust.get("customers") or []
    if not rows:
        return []
    cust_row = rows[0]
    age = cust_row.get("age")
    income = cust_row.get("income")
    gap = orchestrator.gap_analysis(customer_id=customer_id)
    gaps = (gap.get("result") or {}).get("gaps") or []
    cur = conn.cursor()
    products = list(cur.execute("SELECT * FROM products WHERE status='active'"))
    matches: list[dict] = []
    for p in products:
        prod = _row_product(p)
        if prod.get("age_min") is not None and age is not None and age < prod["age_min"]:
            continue
        if prod.get("age_max") is not None and age is not None and age > prod["age_max"]:
            continue
        score = 0
        if prod.get("product_type") in gaps:
            score += 100
        if income and prod.get("premium_max") and prod["premium_max"] / income < 0.2:
            score += 10
        matches.append({
            "product_id": prod["id"],
            "name": prod["name"],
            "company": prod["company"],
            "product_type": prod["product_type"],
            "matches_gap": prod.get("product_type") in gaps,
            "score": score,
        })
    matches.sort(key=lambda r: r["score"], reverse=True)
    return matches


def cmd_customer_match(conn: sqlite3.Connection, args: argparse.Namespace) -> int:
    if args.product:
        row = conn.execute(
            "SELECT * FROM products WHERE name=? AND status='active'", (args.product,)
        ).fetchone()
        if not row:
            print_json({"ok": False, "error": "product_not_found"})
            return 1
        product = _row_product(row)
        print_json({"ok": True, "product": product["name"], "matches": _match_customers_for_product(product)})
        return 0
    if args.customer_id:
        print_json(
            {
                "ok": True,
                "customer_id": args.customer_id,
                "matches": _match_products_for_customer(conn, args.customer_id),
            }
        )
        return 0
    print_json({"ok": False, "error": "need_product_or_customer"})
    return 1


# ---------- product-compare ----------

COMPARE_FIELDS = [
    ("company", "公司"),
    ("product_type", "产品类型"),
    ("coverage_min", "保额下限"),
    ("coverage_max", "保额上限"),
    ("premium_min", "保费下限"),
    ("premium_max", "保费上限"),
    ("age_min", "投保年龄下限"),
    ("age_max", "投保年龄上限"),
    ("waiting_period", "等待期"),
    ("deductible", "免赔额"),
    ("renewal_terms", "续保条件"),
]


def cmd_product_compare(conn: sqlite3.Connection, args: argparse.Namespace) -> int:
    cur = conn.cursor()
    a = cur.execute("SELECT * FROM products WHERE name=? AND status='active'", (args.product_a,)).fetchone()
    b = cur.execute("SELECT * FROM products WHERE name=? AND status='active'", (args.product_b,)).fetchone()
    if not a or not b:
        print_json({"ok": False, "error": "product_not_found",
                    "missing": [n for n, r in [(args.product_a, a), (args.product_b, b)] if not r]})
        return 1
    pa = _row_product(a)
    pb = _row_product(b)
    rows = []
    for field, label in COMPARE_FIELDS:
        rows.append({"field": label, "a": pa.get(field), "b": pb.get(field)})
    rows.append({"field": "亮点", "a": pa.get("highlights"), "b": pb.get("highlights")})
    print_json({"ok": True, "a": pa["name"], "b": pb["name"], "compare": rows})
    return 0


# ---------- dashboard ----------

def cmd_dashboard(conn: sqlite3.Connection, args: argparse.Namespace) -> int:
    n = int(conn.execute("SELECT COUNT(*) AS n FROM products WHERE status='active'").fetchone()["n"])
    types = [
        dict(r)
        for r in conn.execute(
            "SELECT product_type, COUNT(*) AS n FROM products WHERE status='active' GROUP BY product_type ORDER BY n DESC"
        )
    ]
    print_json({"ok": True, "skill": "insurance-product-analysis", "products_total": n, "types": types})
    return 0


# ---------- argparse ----------

def build_parser() -> argparse.ArgumentParser:
    p = argparse.ArgumentParser(prog="insurance_product_cli")
    sub = p.add_subparsers(dest="cmd", required=True)

    a = sub.add_parser("product-add")
    a.add_argument("--name", required=True)
    a.add_argument("--company", required=True)
    a.add_argument("--product-type", "--type", dest="product_type", required=True)
    a.add_argument("--coverage-range")
    a.add_argument("--premium-range")
    a.add_argument("--age-range")
    a.add_argument("--waiting-period")
    a.add_argument("--deductible")
    a.add_argument("--renewal-terms")
    a.add_argument("--highlights")
    a.add_argument("--target-audience")
    a.add_argument("--file")
    a.add_argument("--raw-text")
    a.set_defaults(func=cmd_product_add)

    a = sub.add_parser("product-query")
    a.add_argument("--name")
    a.add_argument("--product-type", "--type", dest="product_type")
    a.add_argument("--company")
    a.set_defaults(func=cmd_product_query)

    a = sub.add_parser("product-update")
    a.add_argument("--name", required=True)
    a.add_argument("--set", required=True)
    a.set_defaults(func=cmd_product_update)

    a = sub.add_parser("product-delete")
    a.add_argument("--name", required=True)
    a.add_argument("--yes", action="store_true")
    a.set_defaults(func=cmd_product_delete)

    a = sub.add_parser("product-analyze")
    a.add_argument("--product", required=True)
    a.add_argument("--match-customers", action="store_true")
    a.add_argument("--refresh", action="store_true")
    a.set_defaults(func=cmd_product_analyze)

    a = sub.add_parser("analysis-write")
    a.add_argument("--product", required=True)
    a.add_argument("--result", help="JSON 字符串")
    a.add_argument("--result-file", help="包含分析结果 JSON 的文件路径")
    a.set_defaults(func=cmd_analysis_write)

    a = sub.add_parser("gap-analysis")
    a.add_argument("--customer-id")
    a.add_argument("--all", action="store_true")
    a.add_argument("--gap-type")
    a.set_defaults(func=cmd_gap_analysis)

    a = sub.add_parser("customer-match")
    a.add_argument("--product")
    a.add_argument("--customer-id")
    a.set_defaults(func=cmd_customer_match)

    a = sub.add_parser("product-compare")
    a.add_argument("--product-a", required=True)
    a.add_argument("--product-b", required=True)
    a.set_defaults(func=cmd_product_compare)

    a = sub.add_parser("dashboard")
    a.add_argument("--json", action="store_true")
    a.set_defaults(func=cmd_dashboard)

    return p


def main(argv: list[str] | None = None) -> int:
    parser = build_parser()
    args = parser.parse_args(argv)
    conn = db.connect()
    try:
        return int(args.func(conn, args) or 0)
    except sqlite3.Error as e:
        print_json({"ok": False, "error": "sqlite_error", "hint": str(e)})
        return 1
    finally:
        conn.close()


if __name__ == "__main__":
    raise SystemExit(main())
