from __future__ import annotations

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

from PIL import Image, ImageDraw, ImageFont
import yaml


def load_font(size: int, *, bold: bool = False) -> ImageFont.ImageFont:
    candidates: list[str] = []
    if bold:
        candidates.extend([r"C:\Windows\Fonts\arialbd.ttf", r"C:\Windows\Fonts\msyhbd.ttc", r"C:\Windows\Fonts\simhei.ttf"])
    candidates.extend([r"C:\Windows\Fonts\arial.ttf", r"C:\Windows\Fonts\msyh.ttc", r"C:\Windows\Fonts\simsun.ttc"])
    for p in candidates:
        try:
            return ImageFont.truetype(p, 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 = max(1, 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 = max(1, int(img.width * height / img.height))
    return img.resize((w, height), Image.Resampling.LANCZOS)


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Compose RETURN report image from three source images.")
    parser.add_argument("--config", default=r"D:\projects\skills\vip-report\config.yaml")
    parser.add_argument("--core-image", default="")
    parser.add_argument("--lfl-image", default="")
    parser.add_argument("--top-image", default="")
    parser.add_argument("--output", default="")
    parser.add_argument("--content-width", type=int, default=1088)
    parser.add_argument("--bottom-left-ratio", type=float, default=0.42)
    parser.add_argument("--core-crop-bottom", type=int, default=185)
    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 defaults_from_config(config_path: Path) -> dict[str, Path]:
    cfg = yaml.safe_load(config_path.read_text(encoding="utf-8"))
    workdir = Path(cfg["paths"]["workdir"]).resolve()
    base = workdir / "assets" / "cep-rejection-analysis"
    report_cfg = cfg.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 {
        "core": base / f"rejection-analysis-core-{ym}.png",
        "lfl": base / f"cep-rejection-lfl-{ym}.png",
        "top": base / "cep-rejection-goods-top5.png",
        "output": base / f"cep-rejection-combined-{ym}.png",
    }


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


def main() -> None:
    args = parse_args()
    defaults = defaults_from_config(Path(args.config).resolve())
    core_path = resolve(args.core_image, defaults["core"])
    lfl_path = resolve(args.lfl_image, defaults["lfl"])
    top_path = resolve(args.top_image, defaults["top"])
    output_path = resolve(args.output, defaults["output"])

    for p in [core_path, lfl_path, top_path]:
        if not p.exists():
            raise FileNotFoundError(f"missing source image: {p}")

    core_img = Image.open(core_path).convert("RGB")
    if args.core_crop_bottom > 0 and args.core_crop_bottom < core_img.height:
        core_img = core_img.crop((0, 0, core_img.width, core_img.height - args.core_crop_bottom))
    lfl_img = Image.open(lfl_path).convert("RGB")
    top_img = Image.open(top_path).convert("RGB")

    content_w = args.content_width
    core_img = resize_to_width(core_img, content_w)

    gap = 12
    left_w = int((content_w - gap) * args.bottom_left_ratio)
    right_w = content_w - gap - left_w
    lfl_img = resize_to_width(lfl_img, left_w)
    top_img = resize_to_width(top_img, right_w)
    bottom_h = min(lfl_img.height, top_img.height)
    lfl_img = resize_to_height(lfl_img, bottom_h)
    top_img = resize_to_height(top_img, bottom_h)

    pad_x = 18
    top_h = 52
    row_gap = 12
    bottom_pad = 16
    canvas_w = content_w + pad_x * 2
    canvas_h = top_h + core_img.height + row_gap + bottom_h + bottom_pad

    canvas = Image.new("RGB", (canvas_w, canvas_h), "#e7e7e7")
    draw = ImageDraw.Draw(canvas)
    draw.text((pad_x + 10, 12), "RETURN", fill="#111111", font=load_font(36, bold=True))
    draw.text((canvas_w - 220, 16), "CHARLES & KEITH", fill="#c9c9c9", font=load_font(24, bold=True))

    y = top_h
    canvas.paste(core_img, (pad_x, y))
    y += core_img.height + row_gap
    canvas.paste(lfl_img, (pad_x, y))
    canvas.paste(top_img, (pad_x + left_w + gap, y))

    output_path.parent.mkdir(parents=True, exist_ok=True)
    canvas.save(output_path)
    print(str(output_path))


if __name__ == "__main__":
    main()
