#!/usr/bin/env python3
"""
Demo script for testing pipeline hot-rebuild and state preservation.

Usage:
    python scripts/demo_hot_rebuild.py
    python scripts/demo_hot_rebuild.py --viewport 40x15

This script:
1. Creates a small viewport (40x15) for easier capture
2. Uses NullDisplay with recording enabled
3. Runs the pipeline for N frames (capturing initial state)
4. Triggers a "hot-rebuild" (e.g., toggling an effect stage)
5. Runs the pipeline for M more frames
6. Verifies state preservation by comparing frames before/after rebuild
7. Prints visual comparison to stdout
"""

import sys
import time
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent))

from engine.display import DisplayRegistry
from engine.effects import get_registry
from engine.fetch import load_cache
from engine.pipeline import Pipeline, PipelineConfig, PipelineContext
from engine.pipeline.adapters import (
    EffectPluginStage,
    FontStage,
    SourceItemsToBufferStage,
    ViewportFilterStage,
    create_stage_from_display,
    create_stage_from_effect,
)
from engine.pipeline.params import PipelineParams


def run_demo(viewport_width: int = 40, viewport_height: int = 15):
    """Run the hot-rebuild demo."""
    print(f"\n{'=' * 60}")
    print(f"Pipeline Hot-Rebuild Demo")
    print(f"Viewport: {viewport_width}x{viewport_height}")
    print(f"{'=' * 60}\n")

    import engine.effects.plugins as effects_plugins

    effects_plugins.discover_plugins()

    print("[1/6] Loading source items...")
    items = load_cache()
    if not items:
        print("  ERROR: No fixture cache available")
        sys.exit(1)
    print(f"  Loaded {len(items)} items")

    print("[2/6] Creating NullDisplay with recording...")
    display = DisplayRegistry.create("null")
    display.init(viewport_width, viewport_height)
    display.start_recording()
    print("  Recording started")

    print("[3/6] Building pipeline...")
    params = PipelineParams()
    params.viewport_width = viewport_width
    params.viewport_height = viewport_height

    config = PipelineConfig(
        source="fixture",
        display="null",
        camera="scroll",
        effects=["noise", "fade"],
    )

    pipeline = Pipeline(config=config, context=PipelineContext())

    from engine.data_sources.sources import ListDataSource
    from engine.pipeline.adapters import DataSourceStage

    list_source = ListDataSource(items, name="fixture")
    pipeline.add_stage("source", DataSourceStage(list_source, name="fixture"))
    pipeline.add_stage("viewport_filter", ViewportFilterStage(name="viewport-filter"))
    pipeline.add_stage("font", FontStage(name="font"))

    effect_registry = get_registry()
    for effect_name in config.effects:
        effect = effect_registry.get(effect_name)
        if effect:
            pipeline.add_stage(
                f"effect_{effect_name}",
                create_stage_from_effect(effect, effect_name),
            )

    pipeline.add_stage("display", create_stage_from_display(display, "null"))
    pipeline.build()

    if not pipeline.initialize():
        print("  ERROR: Failed to initialize pipeline")
        sys.exit(1)

    print("  Pipeline built and initialized")

    ctx = pipeline.context
    ctx.params = params
    ctx.set("display", display)
    ctx.set("items", items)
    ctx.set("pipeline", pipeline)
    ctx.set("pipeline_order", pipeline.execution_order)
    ctx.set("camera_y", 0)

    print("[4/6] Running pipeline for 10 frames (before rebuild)...")
    frames_before = []
    for frame in range(10):
        params.frame_number = frame
        ctx.params = params
        result = pipeline.execute(items)
        if result.success:
            frames_before.append(display._last_buffer)
    print(f"  Captured {len(frames_before)} frames")

    print("[5/6] Triggering hot-rebuild (toggling 'fade' effect)...")
    fade_stage = pipeline.get_stage("effect_fade")
    if fade_stage and isinstance(fade_stage, EffectPluginStage):
        new_enabled = not fade_stage.is_enabled()
        fade_stage.set_enabled(new_enabled)
        fade_stage._effect.config.enabled = new_enabled
        print(f"  Fade effect enabled: {new_enabled}")
    else:
        print("  WARNING: Could not find fade effect stage")

    print("[6/6] Running pipeline for 10 more frames (after rebuild)...")
    frames_after = []
    for frame in range(10, 20):
        params.frame_number = frame
        ctx.params = params
        result = pipeline.execute(items)
        if result.success:
            frames_after.append(display._last_buffer)
    print(f"  Captured {len(frames_after)} frames")

    display.stop_recording()

    print("\n" + "=" * 60)
    print("RESULTS")
    print("=" * 60)

    print("\n[State Preservation Check]")
    if frames_before and frames_after:
        last_before = frames_before[-1]
        first_after = frames_after[0]

        if last_before == first_after:
            print("  PASS: Buffer state preserved across rebuild")
        else:
            print("  INFO: Buffer changed after rebuild (expected - effect toggled)")

    print("\n[Frame Continuity Check]")
    recorded_frames = display.get_frames()
    print(f"  Total recorded frames: {len(recorded_frames)}")
    print(f"  Frames before rebuild: {len(frames_before)}")
    print(f"  Frames after rebuild: {len(frames_after)}")

    if len(recorded_frames) == 20:
        print("  PASS: All frames recorded")
    else:
        print("  WARNING: Frame count mismatch")

    print("\n[Visual Comparison - First frame before vs after rebuild]")
    print("\n--- Before rebuild (frame 9) ---")
    for i, line in enumerate(frames_before[0][:viewport_height]):
        print(f"{i:2}: {line}")

    print("\n--- After rebuild (frame 10) ---")
    for i, line in enumerate(frames_after[0][:viewport_height]):
        print(f"{i:2}: {line}")

    print("\n[Recording Save/Load Test]")
    test_file = Path("/tmp/test_recording.json")
    display.save_recording(test_file)
    print(f"  Saved recording to: {test_file}")

    display2 = DisplayRegistry.create("null")
    display2.init(viewport_width, viewport_height)
    display2.load_recording(test_file)
    loaded_frames = display2.get_frames()
    print(f"  Loaded {len(loaded_frames)} frames from file")

    if len(loaded_frames) == len(recorded_frames):
        print("  PASS: Recording save/load works correctly")
    else:
        print("  WARNING: Frame count mismatch after load")

    test_file.unlink(missing_ok=True)

    pipeline.cleanup()
    display.cleanup()

    print("\n" + "=" * 60)
    print("Demo complete!")
    print("=" * 60 + "\n")


def main():
    viewport_width = 40
    viewport_height = 15

    if "--viewport" in sys.argv:
        idx = sys.argv.index("--viewport")
        if idx + 1 < len(sys.argv):
            vp = sys.argv[idx + 1]
            try:
                viewport_width, viewport_height = map(int, vp.split("x"))
            except ValueError:
                print("Error: Invalid viewport format. Use WxH (e.g., 40x15)")
                sys.exit(1)

    run_demo(viewport_width, viewport_height)


if __name__ == "__main__":
    main()
