# pip install pillow
"""
yjm_LINE_chat_thumbnail_96x74.py

LINE Creators Market 규격 대응 리사이즈 도구

공통 처리:
- PNG로 저장
- RGBA truecolor로 통일 (CMYK/P/JPG 등 입력도 RGBA로 변환)
- 투명 배경 유지
- 72 dpi 메타데이터 명시
- 원본 비율 유지
- 크롭 없음
- 찌그러짐 없음
- 작은 이미지는 확대하지 않음
- 저장 후 PNG / RGBA / DPI / 크기 / 1MB 이하 여부 검사
- UI는 3개 파일 모두 동일: 실행 시 폴더 전체 처리 여부를 예/아니오로 선택

용도:
대화방 탭 이미지: 정확히 96x74
"""

import sys
from pathlib import Path
from PIL import Image
import tkinter as tk
from tkinter import filedialog, messagebox

SUPPORTED = {".png", ".jpg", ".jpeg", ".webp"}
DPI = (72, 72)
MAX_BYTES = 1024 * 1024

TARGET_W = 96
TARGET_H = 74
OUT_FOLDER = "_LINE_chat_thumbnail_96x74"
WINDOW_TITLE = "LINE 채팅 탭 96x74 + RGBA + 72dpi"
SPEC_TEXT = "규격      : 정확히 96x74"


def resize_tab(im: Image.Image):
    rgba = im.convert("RGBA")
    src_w, src_h = rgba.size

    scale = min(TARGET_W / src_w, TARGET_H / src_h, 1.0)
    new_w = max(1, int(round(src_w * scale)))
    new_h = max(1, int(round(src_h * scale)))

    if (new_w, new_h) != rgba.size:
        rgba = rgba.resize((new_w, new_h), Image.Resampling.LANCZOS)
        was_resized = True
    else:
        was_resized = False

    canvas = Image.new("RGBA", (TARGET_W, TARGET_H), (0, 0, 0, 0))
    x = (TARGET_W - new_w) // 2
    y = (TARGET_H - new_h) // 2
    canvas.alpha_composite(rgba, (x, y))

    return canvas, was_resized, scale


def convert_one(src: Path):
    with Image.open(src) as im:
        before_mode = im.mode
        before_size = im.size

        result, was_resized, scale = resize_tab(im)

        out_dir = src.parent / OUT_FOLDER
        out_dir.mkdir(parents=True, exist_ok=True)
        dst = out_dir / f"{src.stem}.png"

        result.save(
            dst,
            format="PNG",
            optimize=True,
            compress_level=9,
            dpi=DPI,
        )

        return {
            "src": src,
            "dst": dst,
            "before_mode": before_mode,
            "before_size": before_size,
            "after_size": result.size,
            "was_resized": was_resized,
            "scale": scale,
        }


def validate_output(v):
    return {
        "RGBA": v["mode"] == "RGBA",
        "72dpi이상": v["dpi_ok"],
        "96x74": v["size"] == (96, 74),
        "1MB이하": v["under_1mb"],
    }

def normalize_path(text: str) -> str:
    text = text.strip().strip('"').strip("'")
    if text.startswith("{") and text.endswith("}"):
        text = text[1:-1]
    return text


def gather_images(paths):
    result = []
    seen = set()

    for raw in paths:
        p = Path(normalize_path(str(raw)))

        if not p.exists():
            print(f"[경고] 없는 경로: {p}")
            continue

        if p.is_file():
            if p.suffix.lower() in SUPPORTED:
                rp = p.resolve()
                if rp not in seen:
                    seen.add(rp)
                    result.append(rp)

        elif p.is_dir():
            for f in p.rglob("*"):
                if OUT_FOLDER in f.parts:
                    continue
                if f.is_file() and f.suffix.lower() in SUPPORTED:
                    rp = f.resolve()
                    if rp not in seen:
                        seen.add(rp)
                        result.append(rp)

    return sorted(result)


def choose_paths():
    root = tk.Tk()
    root.withdraw()

    folder_mode = messagebox.askyesno(
        WINDOW_TITLE,
        "폴더 전체를 처리할까요?\n\n"
        "예 : 폴더 지정\n"
        "아니오 : 이미지 파일 지정"
    )

    if folder_mode:
        selected = filedialog.askdirectory(title="이미지가 들어있는 폴더 선택")
        paths = [selected] if selected else []
    else:
        selected = filedialog.askopenfilenames(
            title="변환할 이미지 선택",
            filetypes=[
                ("이미지", "*.png;*.jpg;*.jpeg;*.webp"),
                ("PNG", "*.png"),
                ("JPG", "*.jpg;*.jpeg"),
                ("WEBP", "*.webp"),
            ]
        )
        paths = list(selected)

    root.destroy()
    return paths


def verify_png(dst: Path):
    size_bytes = dst.stat().st_size

    with Image.open(dst) as chk:
        mode = chk.mode
        size = chk.size
        dpi = chk.info.get("dpi")

    dpi_ok = False
    if dpi and len(dpi) >= 2:
        # PNG pHYs 반올림 때문에 72.00이 72.009 등으로 읽힐 수 있음.
        dpi_ok = dpi[0] >= 71.5 and dpi[1] >= 71.5

    return {
        "mode": mode,
        "size": size,
        "dpi": dpi,
        "dpi_ok": dpi_ok,
        "size_bytes": size_bytes,
        "under_1mb": size_bytes <= MAX_BYTES,
    }


def main():
    paths = sys.argv[1:]

    if not paths:
        paths = choose_paths()

    files = gather_images(paths)

    if not files:
        print("처리할 이미지가 없습니다.")
        try:
            root = tk.Tk()
            root.withdraw()
            messagebox.showwarning(WINDOW_TITLE, "처리할 이미지가 없습니다.")
            root.destroy()
        except Exception:
            pass
        return

    print("=" * 86)
    print(WINDOW_TITLE)
    print(SPEC_TEXT)
    print("PNG       : RGBA truecolor")
    print("DPI       : 72 dpi 명시")
    print("용량      : 파일당 1MB 이하 검사")
    print("크롭      : 없음")
    print("찌그러짐  : 없음")
    print("비율      : 원본 유지")
    print("작은이미지: 확대 안 함")
    print("=" * 86)

    ok = 0
    warning = 0
    fail = 0

    for src in files:
        try:
            r = convert_one(src)
            v = verify_png(r["dst"])
            checks = validate_output(v)

            if all(checks.values()):
                ok += 1
                status = "[완료]"
            else:
                warning += 1
                status = "[주의]"

            dpi_text = "없음" if not v["dpi"] else f"{v['dpi'][0]:.2f}x{v['dpi'][1]:.2f}"
            kb = v["size_bytes"] / 1024

            print(
                f"{status} {src.name}\n"
                f"       원본 : {r['before_size'][0]}x{r['before_size'][1]} / {r['before_mode']}\n"
                f"       결과 : {v['size'][0]}x{v['size'][1]} / {v['mode']}\n"
                f"       DPI  : {dpi_text}\n"
                f"       용량 : {kb:.1f} KB\n"
                f"       검사 : {checks}\n"
                f"       저장 : {r['dst']}"
            )

        except Exception as e:
            fail += 1
            print(f"[실패] {src.name} : {e}")

    print("-" * 86)
    print(f"정상: {ok}개 / 주의: {warning}개 / 실패: {fail}개")

    try:
        root = tk.Tk()
        root.withdraw()
        messagebox.showinfo(
            WINDOW_TITLE,
            f"정상: {ok}개\n"
            f"주의: {warning}개\n"
            f"실패: {fail}개\n\n"
            "PNG / RGBA / 72dpi / 크기 / 1MB 이하 검사를 완료했습니다."
        )
        root.destroy()
    except Exception:
        pass


if __name__ == "__main__":
    main()
