"""Generate placeholder GIF animations for OpenCat states."""

from __future__ import annotations

from pathlib import Path
from typing import Iterable

from PIL import Image, ImageDraw

SIZE = 220
CENTER = SIZE // 2
R = 64
BG = (0, 0, 0, 0)
BODY = (74, 74, 90, 255)
EYE = (127, 224, 160, 255)
TEXT = (190, 196, 230, 255)
PINK = (255, 143, 163, 255)
RED = (255, 107, 107, 255)
YELLOW = (240, 192, 64, 255)


def _frame_base() -> tuple[Image.Image, ImageDraw.ImageDraw]:
    img = Image.new("RGBA", (SIZE, SIZE), BG)
    draw = ImageDraw.Draw(img)
    return img, draw


def _draw_cat(draw: ImageDraw.ImageDraw, y_off: int = 0, eye_mode: str = "open", mouth: str = "normal"):
    cx = CENTER
    cy = CENTER + 2 + y_off
    # body + ears
    draw.ellipse([cx - R, cy - R, cx + R, cy + R], fill=BODY)
    draw.polygon([(cx - 44, cy - 36), (cx - 24, cy - 72), (cx - 12, cy - 38)], fill=BODY)
    draw.polygon([(cx + 44, cy - 36), (cx + 24, cy - 72), (cx + 12, cy - 38)], fill=BODY)

    # eyes
    lx, rx, ey = cx - 22, cx + 22, cy - 8
    if eye_mode == "open":
        draw.ellipse([lx - 8, ey - 8, lx + 8, ey + 8], fill=EYE)
        draw.ellipse([rx - 8, ey - 8, rx + 8, ey + 8], fill=EYE)
    elif eye_mode == "closed":
        draw.arc([lx - 10, ey - 4, lx + 10, ey + 8], 0, 180, fill=TEXT, width=2)
        draw.arc([rx - 10, ey - 4, rx + 10, ey + 8], 0, 180, fill=TEXT, width=2)
    elif eye_mode == "x":
        draw.line([(lx - 6, ey - 6), (lx + 6, ey + 6)], fill=RED, width=2)
        draw.line([(lx - 6, ey + 6), (lx + 6, ey - 6)], fill=RED, width=2)
        draw.line([(rx - 6, ey - 6), (rx + 6, ey + 6)], fill=RED, width=2)
        draw.line([(rx - 6, ey + 6), (rx + 6, ey - 6)], fill=RED, width=2)
    elif eye_mode == "half":
        draw.arc([lx - 10, ey - 2, lx + 10, ey + 10], 200, 340, fill=EYE, width=2)
        draw.arc([rx - 10, ey - 2, rx + 10, ey + 10], 200, 340, fill=EYE, width=2)

    # nose + mouth
    draw.polygon([(cx - 5, cy + 10), (cx + 5, cy + 10), (cx, cy + 16)], fill=PINK)
    if mouth == "normal":
        draw.arc([cx - 12, cy + 16, cx, cy + 26], 0, 180, fill=TEXT, width=2)
        draw.arc([cx, cy + 16, cx + 12, cy + 26], 0, 180, fill=TEXT, width=2)
    elif mouth == "open":
        draw.ellipse([cx - 8, cy + 16, cx + 8, cy + 28], fill=(55, 35, 45, 255))
    elif mouth == "sad":
        draw.arc([cx - 12, cy + 20, cx + 12, cy + 30], 180, 360, fill=TEXT, width=2)


def _save(path: Path, frames: Iterable[Image.Image], duration: int = 110):
    frame_list = list(frames)
    frame_list[0].save(
        path,
        format="GIF",
        save_all=True,
        append_images=frame_list[1:],
        duration=duration,
        loop=0,
        disposal=2,
        transparency=0,
    )


def make_sleeping(out: Path):
    frames = []
    for y in (0, -1, -2, -1):
        img, d = _frame_base()
        _draw_cat(d, y_off=y, eye_mode="closed")
        d.text((172, 24), "z", fill=TEXT)
        frames.append(img)
    _save(out, frames, 150)


def make_connecting(out: Path):
    frames = []
    for i in range(4):
        img, d = _frame_base()
        eye = "half" if i % 2 == 0 else "open"
        _draw_cat(d, eye_mode=eye)
        d.text((100, 24), "." * (i + 1), fill=YELLOW)
        frames.append(img)
    _save(out, frames, 140)


def make_idle(out: Path):
    frames = []
    for i in range(6):
        img, d = _frame_base()
        eye = "closed" if i in (4, 5) else "open"
        _draw_cat(d, eye_mode=eye)
        tail_y = 150 + (2 if i % 2 == 0 else -2)
        d.arc([168, tail_y - 20, 205, tail_y + 20], 270, 90, fill=BODY, width=4)
        frames.append(img)
    _save(out, frames, 120)


def make_thinking(out: Path):
    frames = []
    for i in range(4):
        img, d = _frame_base()
        _draw_cat(d, eye_mode="open")
        for j in range((i % 3) + 1):
            x = 92 + j * 14
            d.ellipse([x, 34, x + 6, 40], fill=EYE)
        frames.append(img)
    _save(out, frames, 120)


def make_talking(out: Path):
    frames = []
    for i, y in enumerate((0, -2, 0, -1)):
        img, d = _frame_base()
        mouth = "open" if i % 2 == 0 else "normal"
        _draw_cat(d, y_off=y, eye_mode="half", mouth=mouth)
        frames.append(img)
    _save(out, frames, 110)


def make_done(out: Path):
    frames = []
    for i in range(4):
        img, d = _frame_base()
        _draw_cat(d, y_off=-1 if i % 2 else 0, eye_mode="half")
        d.text((164, 152), "poof!", fill=TEXT)
        d.ellipse([168, 168, 186, 186], fill=(120, 85, 55, 255))
        frames.append(img)
    _save(out, frames, 130)


def make_error(out: Path):
    frames = []
    for x in (0, 2, 0, 2):
        img, d = _frame_base()
        # quick shiver effect by offsetting cat center manually
        cx = CENTER + x
        cy = CENTER + 2
        d.ellipse([cx - R, cy - R, cx + R, cy + R], fill=BODY)
        d.polygon([(cx - 44, cy - 36), (cx - 24, cy - 72), (cx - 12, cy - 38)], fill=BODY)
        d.polygon([(cx + 44, cy - 36), (cx + 24, cy - 72), (cx + 12, cy - 38)], fill=BODY)
        lx, rx, ey = cx - 22, cx + 22, cy - 8
        d.line([(lx - 6, ey - 6), (lx + 6, ey + 6)], fill=RED, width=2)
        d.line([(lx - 6, ey + 6), (lx + 6, ey - 6)], fill=RED, width=2)
        d.line([(rx - 6, ey - 6), (rx + 6, ey + 6)], fill=RED, width=2)
        d.line([(rx - 6, ey + 6), (rx + 6, ey - 6)], fill=RED, width=2)
        d.polygon([(cx - 5, cy + 10), (cx + 5, cy + 10), (cx, cy + 16)], fill=PINK)
        d.arc([cx - 12, cy + 20, cx + 12, cy + 30], 180, 360, fill=TEXT, width=2)
        frames.append(img)
    _save(out, frames, 120)


def main():
    assets_dir = Path(__file__).resolve().parent / "ui" / "assets"
    assets_dir.mkdir(parents=True, exist_ok=True)

    make_sleeping(assets_dir / "sleeping.gif")
    make_connecting(assets_dir / "connecting.gif")
    make_idle(assets_dir / "idle.gif")
    make_thinking(assets_dir / "thinking.gif")
    make_talking(assets_dir / "talking.gif")
    make_done(assets_dir / "done.gif")
    make_error(assets_dir / "error.gif")

    print(f"Generated placeholder GIFs in: {assets_dir}")


if __name__ == "__main__":
    main()
