from __future__ import annotations

import argparse
import math
import re
from pathlib import Path
from typing import Any

import matplotlib.pyplot as plt
from matplotlib.ticker import PercentFormatter
import pymysql
import yaml


SCENE_ORDER = [
    "7天无理由",
    "尺码问题",
    "商品问题",
    "主观原因-不想买了",
    "价格问题",
    "不划算问题",
    "物流问题",
    "超7天个人原因退货",
    "其他",
    "发货问题",
]


def parse_month_number(raw: Any) -> int:
    if raw is None:
        return 1
    if isinstance(raw, int):
        return min(12, max(1, raw))
    text = str(raw).strip()
    m = re.search(r"(1[0-2]|0?[1-9])", text)
    if m:
        return int(m.group(1))
    return 1


def resolve_period(config_path: Path, report_year_arg: int, report_month_arg: str) -> tuple[int, int]:
    cfg = yaml.safe_load(config_path.read_text(encoding="utf-8"))
    report_cfg = cfg.get("report", {})
    year = int(report_year_arg or report_cfg.get("year", 2026))
    month = parse_month_number(report_month_arg or report_cfg.get("month_cn", "1"))
    return year, month


def month_to_char7(year: int, month: int) -> str:
    return f"{year}-{month:02d}"


def pick_font() -> str | None:
    candidates = [
        r"C:\Windows\Fonts\msyh.ttc",
        r"C:\Windows\Fonts\simhei.ttf",
        r"C:\Windows\Fonts\simsun.ttc",
    ]
    for c in candidates:
        if Path(c).exists():
            return c
    return None


def fetch_rows(conn: pymysql.connections.Connection, data_month: str) -> dict[str, dict[str, float]]:
    sql = """
SELECT
  scene_name,
  reject_return_item_count,
  reject_return_item_count_2,
  reject_return_rate_2
FROM cep_rejection_scene_analysis
WHERE data_month=%s
  AND scene_name_2='全部'
  AND scene_name_3='全部'
"""
    rows: dict[str, dict[str, float]] = {}
    with conn.cursor() as cur:
        cur.execute(sql, (data_month,))
        for scene, cur_cnt, cnt_lfl, rate_lfl in cur.fetchall():
            if not scene:
                continue
            rows[str(scene)] = {
                "cur_count": float(cur_cnt or 0),
                "count_lfl": float(cnt_lfl or 0),
                "rate_lfl": float(rate_lfl or 0),
            }
    return rows


def compute_prev_count(cur_count: float, lfl_pct: float) -> float:
    base = 1.0 + lfl_pct / 100.0
    if base <= 0:
        return 0.0
    return cur_count / base


def render_chart(
    rows: dict[str, dict[str, float]],
    output_path: Path,
) -> None:
    font_path = pick_font()
    if font_path:
        from matplotlib import font_manager

        font_manager.fontManager.addfont(font_path)
        font_name = font_manager.FontProperties(fname=font_path).get_name()
        plt.rcParams["font.sans-serif"] = [font_name, "Arial"]
    plt.rcParams["axes.unicode_minus"] = False

    cats = SCENE_ORDER
    cur_counts = [rows.get(c, {}).get("cur_count", 0.0) for c in cats]
    count_lfl = [rows.get(c, {}).get("count_lfl", 0.0) for c in cats]
    rate_lfl = [rows.get(c, {}).get("rate_lfl", 0.0) for c in cats]
    prev_counts = [compute_prev_count(cur_counts[i], count_lfl[i]) for i in range(len(cats))]

    x = list(range(len(cats)))
    width = 0.31

    fig = plt.figure(figsize=(11.5, 6.4), dpi=220, facecolor="white")
    ax1 = fig.add_subplot(111, facecolor="white")
    ax2 = ax1.twinx()

    c26 = "#7a7bff"
    c25 = "#f3a0df"
    cdot = "#8e3cc0"
    cx = "#ff00ff"
    tick = "#4a4a4a"

    b1 = ax1.bar([i - width / 2 for i in x], cur_counts, width=width, color=c26, label="26拒退件数")
    b2 = ax1.bar([i + width / 2 for i in x], prev_counts, width=width, color=c25, label="25拒退件数")
    s1 = ax2.scatter(x, count_lfl, s=42, marker="o", color=cdot, label="拒退件数LFL", zorder=5)
    s2 = ax2.scatter(x, rate_lfl, s=52, marker="x", color=cx, linewidths=1.1, label="拒退率LFL", zorder=6)

    y1_max = max(max(cur_counts), max(prev_counts), 1.0)
    y1_top = int(math.ceil((y1_max * 1.15) / 500.0) * 500)
    ax1.set_ylim(0, max(2000, y1_top))
    ax2.set_ylim(-40, 120)
    ax2.yaxis.set_major_formatter(PercentFormatter(xmax=100, decimals=0))

    ax1.set_xticks(x)
    ax1.set_xticklabels(cats, rotation=45, ha="right", color=tick, fontsize=9)
    ax1.tick_params(axis="y", colors=tick, length=0)
    ax2.tick_params(axis="y", colors=tick, length=0)

    for sp in ["left", "right", "top"]:
        ax1.spines[sp].set_visible(False)
    ax1.spines["bottom"].set_color("#c8c8c8")
    for sp in ["left", "right", "top", "bottom"]:
        ax2.spines[sp].set_visible(False)

    ax1.set_title("拒退LFL", color=tick, fontsize=18, pad=20, weight="bold")
    ax1.grid(False)
    ax2.grid(False)

    handles = [b1, b2, s1, s2]
    labels = ["26拒退件数", "25拒退件数", "拒退件数LFL", "拒退率LFL"]
    leg = ax1.legend(
        handles,
        labels,
        loc="upper center",
        bbox_to_anchor=(0.5, -0.52),
        ncol=4,
        frameon=False,
        fontsize=9,
        handlelength=1.0,
        handletextpad=0.35,
        columnspacing=1.2,
        borderaxespad=0.0,
        scatterpoints=1,
        markerscale=0.9,
    )
    for t in leg.get_texts():
        t.set_color("#333333")

    fig.subplots_adjust(left=0.06, right=0.93, top=0.83, bottom=0.50)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    fig.savefig(output_path, facecolor=fig.get_facecolor())
    plt.close(fig)


def parse_args() -> argparse.Namespace:
    p = argparse.ArgumentParser(description="Render rejection LFL chart from cep_rejection_scene_analysis table.")
    p.add_argument("--config", default=r"D:\projects\skills\vip-report\config.yaml")
    p.add_argument("--report-year", type=int, default=0)
    p.add_argument("--report-month", default="")
    p.add_argument("--output", default="")
    return p.parse_args()


def main() -> None:
    args = parse_args()
    config_path = Path(args.config).resolve()
    cfg = yaml.safe_load(config_path.read_text(encoding="utf-8"))
    year, month = resolve_period(config_path, args.report_year, args.report_month)
    data_month = month_to_char7(year, month)

    mysql_cfg = cfg["mysql"]
    conn = pymysql.connect(
        host=mysql_cfg["host"],
        port=int(mysql_cfg.get("port", 3306)),
        user=mysql_cfg.get("username") or mysql_cfg.get("user"),
        password=mysql_cfg["password"],
        database=mysql_cfg["database"],
        charset="utf8mb4",
        autocommit=False,
    )
    try:
        rows = fetch_rows(conn, data_month)
    finally:
        conn.close()

    if not rows:
        raise RuntimeError(f"No rows found in cep_rejection_scene_analysis for data_month={data_month}")

    workdir = Path(cfg["paths"]["workdir"]).resolve()
    default_output = workdir / "assets" / "cep-rejection-analysis" / f"cep-rejection-lfl-{year}{month:02d}.png"
    output_path = Path(args.output).resolve() if args.output else default_output
    render_chart(rows, output_path)
    print(str(output_path))


if __name__ == "__main__":
    main()
