"""Generate an animated terminal demo GIF for omega-memory.

Creates a simulated terminal session showing install, setup, and memory recall.
Each frame has an explicit duration -- no duplicate frames needed.
"""
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont

# --- Configuration ---
WIDTH, HEIGHT = 800, 450

# Colors
BG = (13, 17, 23)          # #0d1117
GREEN = (63, 185, 80)      # #3fb950 -- prompt $
WHITE = (255, 255, 255)
GRAY = (110, 118, 129)     # #6e7681 -- comments
ACCENT = (88, 101, 242)    # #5865f2 -- response indicator
DIM_WHITE = (200, 205, 212)
TITLE_BAR_BG = (22, 27, 34)
BORDER = (48, 54, 61)
DOT_RED = (255, 95, 86)
DOT_YELLOW = (255, 189, 46)
DOT_GREEN = (39, 201, 63)
CURSOR_COLOR = (200, 210, 220)

# Fonts
FONT_MONO = ImageFont.truetype("/System/Library/Fonts/SFNSMono.ttf", 16)
FONT_MONO_SM = ImageFont.truetype("/System/Library/Fonts/SFNSMono.ttf", 14)

# Layout
PADDING_X = 24
TITLE_BAR_H = 36
LINE_HEIGHT = 22
CURSOR_W = 9
CURSOR_H = 18

# Timing (milliseconds)
CHAR_DELAY = 70       # ms per typed character
PAUSE_SHORT = 600     # ms
PAUSE_MEDIUM = 1200   # ms
PAUSE_LONG = 2000     # ms
HOLD_END = 4000       # ms
OUTPUT_LINE_DELAY = 150  # ms between response lines appearing


def make_base_frame():
    """Create the terminal window background (cached)."""
    img = Image.new("RGB", (WIDTH, HEIGHT), BG)
    draw = ImageDraw.Draw(img)

    # Terminal window border
    draw.rounded_rectangle(
        [8, 8, WIDTH - 8, HEIGHT - 8],
        radius=12, fill=BG, outline=BORDER, width=1,
    )

    # Title bar
    draw.rounded_rectangle(
        [8, 8, WIDTH - 8, 8 + TITLE_BAR_H],
        radius=12, fill=TITLE_BAR_BG,
    )
    draw.rectangle(
        [8, 8 + TITLE_BAR_H - 12, WIDTH - 8, 8 + TITLE_BAR_H],
        fill=TITLE_BAR_BG,
    )
    draw.line(
        [(8, 8 + TITLE_BAR_H), (WIDTH - 8, 8 + TITLE_BAR_H)],
        fill=BORDER,
    )

    # Traffic light dots
    dot_y = 8 + TITLE_BAR_H // 2
    for i, color in enumerate([DOT_RED, DOT_YELLOW, DOT_GREEN]):
        cx = 28 + i * 22
        draw.ellipse([cx - 6, dot_y - 6, cx + 6, dot_y + 6], fill=color)

    # Title text
    title = "Terminal -- omega-memory demo"
    bbox = draw.textbbox((0, 0), title, font=FONT_MONO_SM)
    tw = bbox[2] - bbox[0]
    draw.text(((WIDTH - tw) // 2, dot_y - 7), title, fill=GRAY, font=FONT_MONO_SM)

    return img


def content_y(line_idx):
    """Y position for a given line index."""
    return 8 + TITLE_BAR_H + 14 + line_idx * LINE_HEIGHT


def render_frame(base, lines, cursor_pos=None):
    """Render a frame with the given lines and optional cursor.

    Args:
        base: base terminal image
        lines: list of (color, text, prefix_color, prefix_text) tuples
               prefix_color/prefix_text can be None for no colored prefix
        cursor_pos: (line_idx, after_text) or None for no cursor
    """
    frame = base.copy()
    draw = ImageDraw.Draw(frame)

    for i, line_data in enumerate(lines):
        y = content_y(i)
        if y > HEIGHT - 20:
            break

        if len(line_data) == 4:
            color, text, prefix_color, prefix_text = line_data
        else:
            color, text = line_data
            prefix_color, prefix_text = None, None

        # Draw the full text in its color
        draw.text((PADDING_X, y), text, fill=color, font=FONT_MONO)

        # Overdraw the prefix in its special color
        if prefix_color and prefix_text:
            draw.text((PADDING_X, y), prefix_text, fill=prefix_color, font=FONT_MONO)

    # Draw cursor if specified
    if cursor_pos is not None:
        line_idx, after_text = cursor_pos
        y = content_y(line_idx)
        bbox = draw.textbbox((PADDING_X, y), after_text, font=FONT_MONO)
        cx = bbox[2] + 1
        draw.rectangle(
            [cx, y + 2, cx + CURSOR_W, y + CURSOR_H + 2],
            fill=CURSOR_COLOR,
        )

    return frame


def generate_gif():
    """Generate all frames with their durations and save the GIF."""
    base = make_base_frame()
    frames = []     # list of PIL Images
    durations = []  # list of ms durations

    # Current terminal state: list of line tuples
    # Each line: (color, full_text, prefix_color, prefix_text)
    lines = []

    def add_frame(duration_ms, cursor_pos=None):
        """Add a frame showing current lines state."""
        # GIF minimum duration is ~20ms; enforce minimum
        duration_ms = max(20, duration_ms)
        frames.append(render_frame(base, lines, cursor_pos))
        durations.append(duration_ms)

    def type_command(text, prefix="$ ", prefix_color=GREEN):
        """Simulate typing a command character by character."""
        line_idx = len(lines)
        for ci in range(len(text) + 1):
            partial = prefix + text[:ci]
            if line_idx < len(lines):
                lines[line_idx] = (WHITE, partial, prefix_color, prefix)
            else:
                lines.append((WHITE, partial, prefix_color, prefix))
            add_frame(CHAR_DELAY, cursor_pos=(line_idx, partial))

        # Final frame without cursor (brief hold)
        add_frame(100)

    def type_prompt(text, prefix="> ", prefix_color=ACCENT):
        """Simulate typing a user prompt."""
        type_command(text, prefix=prefix, prefix_color=prefix_color)

    def add_output(output_lines, delay_between=0):
        """Add output lines (appear instantly or with delay between them)."""
        if delay_between > 0:
            for color, text in output_lines:
                lines.append((color, text, None, None))
                add_frame(delay_between)
        else:
            for color, text in output_lines:
                lines.append((color, text, None, None))
            add_frame(100)

    def add_comment(text):
        """Add a gray comment line."""
        lines.append((GRAY, text, None, None))
        add_frame(100)

    def pause(ms):
        """Hold the current frame for a duration."""
        add_frame(ms)

    # =====================================================================
    # SCENE 1: Initial blank terminal
    # =====================================================================
    pause(800)

    # =====================================================================
    # SCENE 2: pip install omega-memory
    # =====================================================================
    type_command("pip install omega-memory")
    pause(PAUSE_SHORT)

    add_output([
        (DIM_WHITE, "Collecting omega-memory"),
        (DIM_WHITE, "  Downloading omega_memory-0.8.0-py3-none-any.whl"),
        (GREEN, "Successfully installed omega-memory-0.8.0"),
    ])
    pause(PAUSE_MEDIUM)

    # =====================================================================
    # SCENE 3: omega setup
    # =====================================================================
    type_command("omega setup")
    pause(PAUSE_SHORT)

    add_output([
        (GREEN,     "\u2713 Configured for Claude Code"),
        (GREEN,     "\u2713 Memory system ready"),
        (DIM_WHITE, ""),
        (DIM_WHITE, "Run `claude` to start with persistent memory."),
    ])
    pause(PAUSE_MEDIUM)

    # =====================================================================
    # SCENE 4: Time skip
    # =====================================================================
    pause(PAUSE_SHORT)
    add_comment("# 3 weeks later, new session...")
    pause(PAUSE_LONG)

    # =====================================================================
    # SCENE 5: User asks a question
    # =====================================================================
    type_prompt('"What auth approach did we decide on?"')
    pause(PAUSE_SHORT)

    # =====================================================================
    # SCENE 6: OMEGA response appears line by line
    # =====================================================================
    add_output([
        (ACCENT,    "\u2192 Decision (Feb 1):"),
        (WHITE,     '  "JWT tokens over session cookies'),
        (WHITE,     '   -- API needs stateless auth for'),
        (WHITE,     '   horizontal scaling."'),
        (DIM_WHITE, ""),
        (GRAY,      "  relevance: 0.94  |  accessed 7\u00d7"),
    ], delay_between=OUTPUT_LINE_DELAY)

    # =====================================================================
    # SCENE 7: Hold the final result
    # =====================================================================
    pause(HOLD_END)

    return frames, durations


def main():
    print("Generating frames...")
    frames, durations = generate_gif()
    print(f"Generated {len(frames)} frames")

    if not frames:
        print("ERROR: No frames generated!")
        return

    total_ms = sum(durations)
    print(f"Total duration: {total_ms / 1000:.1f}s")

    output_path = str(Path(__file__).parent / "demo.gif")
    print(f"Saving GIF to {output_path}...")

    # Convert frames to P mode (palette) for better GIF compatibility
    # Use the first frame's palette for all frames
    palette_frames = []
    for f in frames:
        palette_frames.append(f.quantize(colors=128, method=Image.Quantize.MEDIANCUT))

    palette_frames[0].save(
        output_path,
        save_all=True,
        append_images=palette_frames[1:],
        duration=durations,
        loop=0,
        optimize=False,  # Don't let PIL mess with our durations
    )

    import os
    size_kb = os.path.getsize(output_path) / 1024
    print(f"Saved: {WIDTH}x{HEIGHT}, {len(frames)} frames, {size_kb:.0f} KB")


if __name__ == "__main__":
    main()
