from __future__ import annotations

import argparse
import re
import json
import sys
from pathlib import Path
from typing import Any

import yaml

SCRIPT_DIR = Path(__file__).resolve().parent
if str(SCRIPT_DIR) not in sys.path:
    sys.path.insert(0, str(SCRIPT_DIR))
for dep_dir in [SCRIPT_DIR / ".pydeps", SCRIPT_DIR.parent / ".pydeps", Path.cwd() / ".pydeps"]:
    if dep_dir.exists() and str(dep_dir) not in sys.path:
        sys.path.insert(0, str(dep_dir))

from PIL import Image, ImageDraw, ImageFont
from runtime_config import add_config_source_args, load_runtime_config


def load_font(size: int, *, bold: bool = False):
    candidates: list[str] = []
    if bold:
        candidates.extend([r"C:\Windows\Fonts\msyhbd.ttc", r"C:\Windows\Fonts\simhei.ttf", r"C:\Windows\Fonts\arialbd.ttf"])
    candidates.extend([r"C:\Windows\Fonts\msyh.ttc", r"C:\Windows\Fonts\simsun.ttc", r"C:\Windows\Fonts\arial.ttf"])
    for path in candidates:
        try:
            return ImageFont.truetype(path, size=size)
        except OSError:
            continue
    return ImageFont.load_default()


def resize_to_width(img: Image.Image, width: int) -> Image.Image:
    if img.width == width:
        return img
    h = int(img.height * width / img.width)
    return img.resize((width, h), Image.Resampling.LANCZOS)


def resize_to_height(img: Image.Image, height: int) -> Image.Image:
    if img.height == height:
        return img
    w = int(img.width * height / img.height)
    return img.resize((w, height), Image.Resampling.LANCZOS)


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Compose membership report image from 4 source images.")
    add_config_source_args(parser, default_config=Path(r"D:\projects\skills\vip-report\config.yaml"))
    parser.add_argument("--metrics-image", default="")
    parser.add_argument("--conversion-image", default="")
    parser.add_argument("--order-image", default="")
    parser.add_argument("--channel-image", default="")
    parser.add_argument("--output", default="")
    parser.add_argument("--content-width", type=int, default=1060)
    parser.add_argument("--metrics-crop-top", type=int, default=0)
    return parser.parse_args()


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 default_paths_from_config(config: dict[str, Any]) -> dict[str, Path]:
    workdir = Path(config["paths"]["workdir"]).resolve()
    assets = workdir / "assets" / "cep-member-overview"
    report_cfg = config.get("report", {})
    year = int(report_cfg.get("year", 2026))
    month = parse_month_number(report_cfg.get("month_cn", "1"))
    ym = f"{year}{month:02d}"
    return {
        "metrics": assets / f"member-overview-metrics-{ym}.png",
        "conversion": assets / f"member-consumption-convert-{ym}.png",
        "order": assets / f"member-order-distribution-{ym}.png",
        "channel": assets / f"member-channel-trend-{ym}.png",
        "output": assets / f"member-overview-combined-{ym}.png",
    }


def resolve_path(arg_value: str, default: Path) -> Path:
    return Path(arg_value).resolve() if arg_value else default


def main() -> None:
    args = parse_args()
    config_path = Path(args.config or r"D:\projects\skills\vip-report\config.yaml").resolve()
    cfg = load_runtime_config(args, default_config=config_path)
    defaults = default_paths_from_config(cfg)

    metrics_path = resolve_path(args.metrics_image, defaults["metrics"])
    conversion_path = resolve_path(args.conversion_image, defaults["conversion"])
    order_path = resolve_path(args.order_image, defaults["order"])
    channel_path = resolve_path(args.channel_image, defaults["channel"])
    output_path = resolve_path(args.output, defaults["output"])

    for p in [metrics_path, conversion_path, order_path, channel_path]:
        if not p.exists():
            raise FileNotFoundError(f"missing source image: {p}")

    metrics_img = Image.open(metrics_path).convert("RGB")
    if 0 < args.metrics_crop_top < metrics_img.height:
        metrics_img = metrics_img.crop((0, args.metrics_crop_top, metrics_img.width, metrics_img.height))
    conversion_img = Image.open(conversion_path).convert("RGB")
    order_img = Image.open(order_path).convert("RGB")
    channel_img = Image.open(channel_path).convert("RGB")

    metrics_img = resize_to_width(metrics_img, args.content_width)
    conversion_img = resize_to_width(conversion_img, args.content_width)

    bottom_gap = 12
    left_target_w = int((args.content_width - bottom_gap) * 0.36)
    right_target_w = args.content_width - bottom_gap - left_target_w
    order_img = resize_to_width(order_img, left_target_w)
    channel_img = resize_to_width(channel_img, right_target_w)
    target_bottom_h = min(order_img.height, channel_img.height)
    order_img = resize_to_height(order_img, target_bottom_h)
    channel_img = resize_to_height(channel_img, target_bottom_h)

    pad_x = 34
    top_h = 56
    gap = 12
    bottom_pad = 20

    canvas_w = args.content_width + pad_x * 2
    canvas_h = top_h + metrics_img.height + gap + conversion_img.height + gap + target_bottom_h + bottom_pad
    canvas = Image.new("RGB", (canvas_w, canvas_h), "#e7e7e7")
    draw = ImageDraw.Draw(canvas)

    title_font = load_font(36, bold=True)
    brand_font = load_font(24, bold=True)
    draw.text((pad_x, 14), "MEMBERSHIP", fill="#111111", font=title_font)
    draw.text((canvas_w - 180, 18), "CHARLES & KEITH", fill="#c9c9c9", font=brand_font)

    y = top_h
    canvas.paste(metrics_img, (pad_x, y))
    y += metrics_img.height + gap
    canvas.paste(conversion_img, (pad_x, y))
    y += conversion_img.height + gap
    canvas.paste(order_img, (pad_x, y))
    canvas.paste(channel_img, (pad_x + order_img.width + bottom_gap, y))

    output_path.parent.mkdir(parents=True, exist_ok=True)
    canvas.save(output_path)
    print(json.dumps({"ok": True, "image_path": str(output_path)}, ensure_ascii=False))


if __name__ == "__main__":
    main()
