from __future__ import annotations

import argparse
import json
import math
import sys
from decimal import Decimal
from pathlib import Path
from typing import Any

import pymysql

SCRIPT_DIR = Path(__file__).resolve().parent
if str(SCRIPT_DIR) not in sys.path:
    sys.path.insert(0, str(SCRIPT_DIR))

from runtime_config import add_config_source_args, load_runtime_config
from sync_cep_site_traffic_entry_assets import (
    fetch_month_uv_series,
    lfl_series,
    month_label_to_number,
    normalize_month_label,
)
from sync_cep_traffic_assets import build_table_values, query_month_row
from sync_cep_traffic_summary_assets import ENTRY_ORDER, fetch_entry_uv_map


TABLE_HEADERS = [
    "Year",
    "In-site Traffic",
    "In-site Paid Traffic",
    "In-site Paid %",
    "In-site Organic Traffic",
    "In-site Organic %",
    "Off-site Traffic",
    "Off-site Paid Traffic",
    "Off-site Paid %",
    "Off-site Organic Traffic",
    "Off-site Organic %",
]


def round_up(value: int, step: int) -> int:
    if value <= 0:
        return step
    return ((value + step - 1) // step) * step


def choose_value_axis(values_a: list[Decimal], values_b: list[Decimal]) -> tuple[int, int]:
    max_volume = max(int(max(values_a or [Decimal("0")])), int(max(values_b or [Decimal("0")])))
    y_step = 5000 if max_volume > 15000 else 2000
    y_max = round_up(max_volume, y_step)
    return y_max, y_step


def choose_lfl_axis(values: list[Decimal]) -> tuple[int, int, int]:
    if not values:
        return -20, 60, 20
    min_v = float(min(values))
    max_v = float(max(values))
    span = max(abs(min_v), abs(max_v))
    if span <= 80:
        step = 20
    elif span <= 300:
        step = 50
    else:
        step = 100
    low = int(math.floor(min_v / step) * step)
    high = int(math.ceil(max_v / step) * step)
    if low > 0:
        low = 0
    if high < 0:
        high = 0
    if high == low:
        high += step
    return low, high, step


def build_entry_rows(
    cur_map: dict[str, Decimal], prev_map: dict[str, Decimal]
) -> list[dict[str, Any]]:
    union_entries = set(cur_map) | set(prev_map)
    ordered = [e for e in ENTRY_ORDER if e in union_entries]
    extras = sorted(
        [e for e in union_entries if e not in ordered],
        key=lambda k: (cur_map.get(k, Decimal("0")) + prev_map.get(k, Decimal("0"))),
        reverse=True,
    )
    ordered.extend(extras)
    rows: list[dict[str, Any]] = []
    for name in ordered:
        rows.append(
            {
                "name": name,
                "report_uv": int(cur_map.get(name, Decimal("0"))),
                "compare_uv": int(prev_map.get(name, Decimal("0"))),
            }
        )
    return rows


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Build editable PPT operations for slide 12 (UV LFL) without image rendering."
    )
    add_config_source_args(parser, default_config=Path(r"D:\projects\skills\vip-report\config.yaml"))
    parser.add_argument("--report-month", default="", help="Month override, e.g. Jan / 1")
    parser.add_argument("--report-year", type=int, default=0, help="Report year override")
    parser.add_argument("--compare-year", type=int, default=0, help="Compare year override")
    parser.add_argument("--output-ops", default="", help="Optional override output ops path")
    return parser.parse_args()


def execute(
    config: dict[str, Any],
    *,
    report_month: str = "",
    report_year: int = 0,
    compare_year: int = 0,
    output_ops: str = "",
) -> dict[str, str]:
    report_cfg = config.get("report", {})
    month_label = normalize_month_label(report_month or report_cfg.get("month_cn", "一月"))
    month_num = month_label_to_number(month_label)
    report_year = int(report_year or report_cfg.get("year", 2026))
    compare_year = int(compare_year or report_cfg.get("compare_year", report_year - 1))

    mysql_cfg = config["mysql"]
    conn = pymysql.connect(
        host=mysql_cfg["host"],
        port=int(mysql_cfg["port"]),
        user=mysql_cfg["username"],
        password=mysql_cfg["password"],
        database=mysql_cfg["database"],
        charset="utf8mb4",
        cursorclass=pymysql.cursors.DictCursor,
    )
    try:
        cur_month_row = query_month_row(conn, year=report_year, month_num=month_num)
        prev_month_row = query_month_row(conn, year=compare_year, month_num=month_num)
        table_current, table_previous, table_lfl = build_table_values(
            report_year=report_year,
            compare_year=compare_year,
            current=cur_month_row,
            previous=prev_month_row,
        )

        cur_uv = fetch_month_uv_series(conn, year=report_year, month=month_num)
        prev_uv = fetch_month_uv_series(conn, year=compare_year, month=month_num)
        free_lfl = lfl_series(cur_uv["free"], prev_uv["free"])
        paid_lfl = lfl_series(cur_uv["paid"], prev_uv["paid"])

        cur_entry_map = fetch_entry_uv_map(conn, daily_month=f"{report_year}-{month_num:02d}")
        prev_entry_map = fetch_entry_uv_map(conn, daily_month=f"{compare_year}-{month_num:02d}")
    finally:
        conn.close()

    free_axis_max, free_axis_step = choose_value_axis(cur_uv["free"], prev_uv["free"])
    paid_axis_max, paid_axis_step = choose_value_axis(cur_uv["paid"], prev_uv["paid"])
    free_lfl_min, free_lfl_max, free_lfl_step = choose_lfl_axis(free_lfl)
    paid_lfl_min, paid_lfl_max, paid_lfl_step = choose_lfl_axis(paid_lfl)

    vip_workdir = Path(config["paths"]["workdir"]).resolve()
    ops_path = Path(output_ops).resolve() if output_ops else (vip_workdir / "render-ops.uv-lfl-s12.editable.json")
    ops_path.parent.mkdir(parents=True, exist_ok=True)

    operations = {
        "replace_text": [],
        "replace_tables": [],
        "replace_charts": [],
        "replace_images": [],
        "replace_native_charts": [
            {
                "type": "slide12_traffic_table",
                "slide": 12,
                "shape_id": 3,
                "shape_name": "Table 17",
                "target_box": {
                    "left": 1835149,
                    "top": 1557195,
                    "width": 16160753,
                    "height": 1465080,
                },
                "headers": TABLE_HEADERS,
                "rows": [table_current, table_previous, table_lfl],
            },
            {
                "type": "slide12_dual_line_lfl",
                "slide": 12,
                "shape_id": 5,
                "shape_name": "Group 24",
                "target_box": {
                    "left": 1987549,
                    "top": 3062843,
                    "width": 15887698,
                    "height": 3109300,
                },
                "month": month_num,
                "report_year": report_year,
                "compare_year": compare_year,
                "panels": [
                    {
                        "title": "Organic Traffic LFL",
                        "current_name": f"{str(report_year)[-2:]} Organic Traffic",
                        "previous_name": f"{str(compare_year)[-2:]} Organic Traffic",
                        "current_values": [int(v) for v in cur_uv["free"]],
                        "previous_values": [int(v) for v in prev_uv["free"]],
                        "lfl_values": [round(float(v), 2) for v in free_lfl],
                        "value_axis_max": free_axis_max,
                        "value_axis_major_unit": free_axis_step,
                        "lfl_axis_min": free_lfl_min,
                        "lfl_axis_max": free_lfl_max,
                        "lfl_axis_major_unit": free_lfl_step,
                    },
                    {
                        "title": "Paid Traffic LFL",
                        "current_name": f"{str(report_year)[-2:]} Paid Traffic",
                        "previous_name": f"{str(compare_year)[-2:]} Paid Traffic",
                        "current_values": [int(v) for v in cur_uv["paid"]],
                        "previous_values": [int(v) for v in prev_uv["paid"]],
                        "lfl_values": [round(float(v), 2) for v in paid_lfl],
                        "value_axis_max": paid_axis_max,
                        "value_axis_major_unit": paid_axis_step,
                        "lfl_axis_min": paid_lfl_min,
                        "lfl_axis_max": paid_lfl_max,
                        "lfl_axis_major_unit": paid_lfl_step,
                    },
                ],
            },
            {
                "type": "slide12_entry_mix",
                "slide": 12,
                "shape_id": 8,
                "shape_name": "Group 25",
                "target_box": {
                    "left": 1987549,
                    "top": 6093856,
                    "width": 16160750,
                    "height": 2729245,
                },
                "report_year": report_year,
                "compare_year": compare_year,
                "entries": build_entry_rows(cur_entry_map, prev_entry_map),
            },
        ],
    }
    ops_path.write_text(json.dumps(operations, ensure_ascii=False, indent=2), encoding="utf-8")

    return {"operations_path": str(ops_path)}


def main() -> None:
    args = parse_args()
    config = load_runtime_config(args, default_config=Path(r"D:\projects\skills\vip-report\config.yaml"))
    result = execute(
        config,
        report_month=args.report_month,
        report_year=args.report_year,
        compare_year=args.compare_year,
        output_ops=args.output_ops,
    )
    print(json.dumps(result, ensure_ascii=False))


if __name__ == "__main__":
    main()
