from __future__ import annotations

import argparse
import subprocess
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Any

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 load_runtime_config


@dataclass(frozen=True)
class MonthKey:
    year: int
    month: int


def parse_args() -> argparse.Namespace:
    p = argparse.ArgumentParser(description="Run flow15 live-goods-effect pipeline in one command.")
    p.add_argument("--python", default=sys.executable, help="Python executable path")
    p.add_argument("--config-source", choices=["yaml", "db"], default="db")
    p.add_argument("--config", default="", help="yaml path when config-source=yaml")
    p.add_argument("--config-table", default="cepconfiguration")
    p.add_argument("--config-key", default="vip-report")
    p.add_argument("--mysql-host", default="192.168.138.182")
    p.add_argument("--mysql-port", type=int, default=3306)
    p.add_argument("--mysql-user", default="ceptest")
    p.add_argument("--mysql-password", default="ck@123456")
    p.add_argument("--mysql-database", default="ckc_cep_db_test")
    p.add_argument("--download-dir", default=r"C:\vipdata")
    p.add_argument("--report-year", type=int, default=0)
    p.add_argument("--report-month", default="")
    p.add_argument("--skip-compose", action="store_true", help="Deprecated. Compose preview is disabled by default.")
    p.add_argument("--skip-editable-ops", action="store_true", help="Skip generating slide15 editable ops.")
    return p.parse_args()


def build_common_args(args: argparse.Namespace) -> list[str]:
    if args.config_source == "yaml":
        config_path = args.config or str((SCRIPT_DIR.parent / "config.yaml").resolve())
        return ["--config-source", "yaml", "--config", config_path]
    return [
        "--config-source",
        args.config_source,
        "--config-table",
        args.config_table,
        "--config-key",
        args.config_key,
        "--mysql-host",
        args.mysql_host,
        "--mysql-port",
        str(args.mysql_port),
        "--mysql-user",
        args.mysql_user,
        "--mysql-password",
        args.mysql_password,
        "--mysql-database",
        args.mysql_database,
    ]


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()
    import re

    m = re.search(r"(1[0-2]|0?[1-9])", text)
    if m:
        return int(m.group(1))
    return 1


def previous_month(m: MonthKey) -> MonthKey:
    if m.month == 1:
        return MonthKey(m.year - 1, 12)
    return MonthKey(m.year, m.month - 1)


def same_month_last_year(m: MonthKey) -> MonthKey:
    return MonthKey(m.year - 1, m.month)


def resolve_report_month(args: argparse.Namespace, runtime_cfg: dict[str, Any]) -> MonthKey:
    report_cfg = runtime_cfg.get("report", {})
    year = int(args.report_year or report_cfg.get("year", 2026))
    month = parse_month_number(args.report_month or report_cfg.get("month_cn", "1"))
    return MonthKey(year, month)


def with_report_period(cmd: list[str], month: MonthKey) -> list[str]:
    return [*cmd, "--report-year", str(month.year), "--report-month", str(month.month)]


def run_step(step_index: int, total: int, cmd: list[str]) -> None:
    print(f"[STEP {step_index}/{total}] {' '.join(cmd)}")
    result = subprocess.run(cmd, text=True, capture_output=True)
    if result.stdout:
        print(result.stdout)
    if result.returncode != 0:
        err = (result.stderr or "").strip()
        out = (result.stdout or "").strip()
        err_tail = err[-4000:] if len(err) > 4000 else err
        out_tail = out[-2000:] if len(out) > 2000 else out
        raise RuntimeError(
            "STEP_FAILED\n"
            f"step={step_index}\n"
            f"command={' '.join(cmd)}\n"
            f"returncode={result.returncode}\n"
            f"stdout_tail=\n{out_tail}\n"
            f"stderr_tail=\n{err_tail}\n"
        )


def main() -> None:
    args = parse_args()
    root = SCRIPT_DIR
    common = build_common_args(args)
    runtime_cfg = load_runtime_config(
        args,
        default_config=Path(args.config) if args.config else (root.parent / "config.yaml"),
    )
    workdir = Path(runtime_cfg["paths"]["workdir"]).resolve()
    report_month = resolve_report_month(args, runtime_cfg)
    current_month = report_month
    compare_month = same_month_last_year(report_month)

    steps: list[list[str]] = [
        with_report_period(
            [
                args.python,
                str(root / "sync_vis_vendor_goods_effect_download.py"),
                *common,
                "--download-dir",
                args.download_dir,
            ],
            current_month,
        ),
        with_report_period(
            [
                args.python,
                str(root / "sync_vis_vendor_goods_effect_download.py"),
                *common,
                "--download-dir",
                args.download_dir,
            ],
            compare_month,
        ),
        with_report_period([args.python, str(root / "sync_cep_live_goods_effect_summary_assets.py"), *common], report_month),
        with_report_period([args.python, str(root / "sync_cep_live_goods_effect_pivot_assets.py"), *common], current_month),
        with_report_period([args.python, str(root / "sync_cep_live_goods_effect_top5_assets.py"), *common], current_month),
    ]

    if not args.skip_editable_ops:
        steps.append(
            [
                args.python,
                str(root / "build_slide14_15_editable_ops.py"),
                *common,
                "--slides",
                "15",
                "--report-year",
                str(report_month.year),
                "--report-month",
                str(report_month.month),
            ]
        )

    total = len(steps)
    for i, cmd in enumerate(steps, start=1):
        run_step(i, total, cmd)

    print(f"FINAL_OPS={workdir / 'render-ops.flow14-15.editable.json'}")
    print("FLOW15_PIPELINE_OK")


if __name__ == "__main__":
    main()
