#!/usr/bin/env python3
"""Standalone UI test tool for the Pi Multi-FX Pedal.

Tests the hardware UI layer without physical hardware:
  - Simulates footswitch presses and verifies callbacks
  - Toggles LED states and logs output
  - Cycles through display modes
  - Interactive menu when run directly

Usage:
    python scripts/ui_test.py                  # Interactive mode
    python scripts/ui_test.py --all            # Run all automated tests
    python scripts/ui_test.py --footswitch     # Footswitch tests only
    python scripts/ui_test.py --leds           # LED tests only
    python scripts/ui_test.py --display        # Display tests only
"""

from __future__ import annotations

import logging
import sys
import time

# Ensure src is importable
sys.path.insert(0, ".")

logging.basicConfig(
    level=logging.DEBUG,
    format="%(levelname)-7s | %(message)s",
)
logger = logging.getLogger("ui_test")

from src.ui.footswitch import (
    DEBOUNCE_MS,
    LONG_PRESS_MS,
    FootSwitch,
    FootswitchController,
    SwitchAction,
)
from src.ui.leds import LEDController, LEDDriver, LEDPattern
from src.ui.display import DisplayController, DisplayState


# ============================================================
# Automated test functions
# ============================================================


def test_footswitch_basic() -> list[str]:
    """Test basic footswitch press/release."""
    results = []
    ctrl = FootswitchController()
    actions = []

    ctrl.register_callback(SwitchAction.BYPASS, lambda: actions.append("bypass"))
    ctrl.start()
    time.sleep(0.1)

    # Simulate press via direct trigger
    ctrl.simulate_press(SwitchAction.BYPASS)
    time.sleep(0.05)

    if "bypass" in actions:
        results.append("  ✓ BYPASS callback fired on simulate_press")
    else:
        results.append("  ✗ BYPASS callback NOT fired")

    ctrl.stop()
    return results


def test_footswitch_debounce() -> list[str]:
    """Test GPIO debounce engine with virtual pins."""
    results = []
    ctrl = FootswitchController()
    actions = []

    ctrl.register_callback(SwitchAction.PRESET_UP, lambda: actions.append("up"))
    ctrl.register_callback(SwitchAction.TAP_TEMPO, lambda: actions.append("tap"))
    ctrl.start()

    try:
        pin = ctrl._switches[0].gpio_pin  # Pin 17

        # --- Short press test ---
        ctrl.simulate_gpio_change(pin, True)
        time.sleep((DEBOUNCE_MS + 20) / 1000)  # Past debounce
        ctrl.simulate_gpio_change(pin, False)
        time.sleep((DEBOUNCE_MS + 20) / 1000)  # Let poll loop process

        if "up" in actions:
            results.append("  ✓ Short press → PRESET_UP fired")
        else:
            results.append("  ✗ Short press — PRESET_UP not fired")

        # --- Long press test (FS1: PRESET_UP default, TAP_TEMPO long) ---
        ctrl.simulate_gpio_change(pin, True)
        time.sleep((LONG_PRESS_MS + 100) / 1000)  # Hold past threshold

        if "tap" in actions:
            results.append("  ✓ Long press → TAP_TEMPO fired")
        else:
            results.append("  ✗ Long press — TAP_TEMPO not fired")

        # Release
        ctrl.simulate_gpio_change(pin, False)
        time.sleep((DEBOUNCE_MS + 20) / 1000)

    finally:
        ctrl.stop()

    return results


def test_leds() -> list[str]:
    """Test LED controller features."""
    results = []
    ctrl = LEDController(4, driver=LEDDriver.MOCK)
    ctrl.initialize()

    # Set a pixel
    ctrl.set_pixel(0, (255, 0, 0))
    r, g, b = ctrl._pixels[0]
    results.append(f"  ✓ Pixel 0 set: RGB({r},{g},{b}) (expected ~127,0,0)")

    # Bypass LED
    ctrl.set_bypass_led(1, bypassed=True)
    results.append(f"  ✓ Bypass LED (bypassed): RGB{ctrl._pixels[1]} (expected red)")

    ctrl.set_bypass_led(2, bypassed=False)
    results.append(f"  ✓ Bypass LED (active): RGB{ctrl._pixels[2]} (expected green)")

    # Clear
    ctrl.set_all((128, 128, 128))
    ctrl.clear_all()
    if all(px == (0, 0, 0) for px in ctrl._pixels):
        results.append("  ✓ Clear all → all (0,0,0)")
    else:
        results.append("  ✗ Clear all — pixels not all black")

    # Brightness
    ctrl.set_brightness(0.8)
    results.append(f"  ✓ Brightness set to {ctrl._global_brightness}")

    # Animations (should not crash)
    ctrl.preset_animate("up")
    results.append("  ✓ Preset up animation (no crash)")
    ctrl.preset_animate("down")
    results.append("  ✓ Preset down animation (no crash)")
    ctrl.tap_tempo_blip()
    results.append("  ✓ Tap tempo blip (no crash)")

    return results


def test_display() -> list[str]:
    """Test display controller state rendering."""
    results = []
    dc = DisplayController()
    init_ok = dc.initialize()
    results.append(f"  ✓ Initialize returned {init_ok} (expected False — no HW)")

    # Preset mode
    dc.update(DisplayState(
        mode="preset",
        preset_name="Metal Zone",
        bank_name="B3",
        bypassed=False,
        fx_active=["noise_gate", "distortion", "delay", "reverb"],
    ))
    results.append("  ✓ Preset mode: Metal Zone")

    # Tuner mode
    dc.update(DisplayState(
        mode="tuner",
        tuner_note="E",
        tuner_cents=-5,
    ))
    results.append("  ✓ Tuner mode: E, -5 cents")

    # FX edit mode
    dc.update(DisplayState(
        mode="fx_edit",
        param_name="Gain",
        param_value=0.67,
    ))
    results.append("  ✓ FX edit mode: Gain = 0.67")

    # Settings mode
    dc.update(DisplayState(mode="settings", param_name="MIDI Channel"))
    results.append("  ✓ Settings mode: MIDI Channel")

    # Default state
    dc.update(DisplayState())
    results.append("  ✓ Default (empty) state")

    dc.clear()
    results.append("  ✓ Clear (no crash)")

    return results


# ============================================================
# Interactive menu
# ============================================================


def interactive_menu():
    """Run an interactive test session."""
    ctrl = FootswitchController()
    led_ctrl = LEDController(4, driver=LEDDriver.MOCK)
    disp = DisplayController()
    disp.initialize()

    actions = {
        "1": ("Simulate PRESET_UP", lambda: ctrl.simulate_press(SwitchAction.PRESET_UP)),
        "2": ("Simulate PRESET_DOWN", lambda: ctrl.simulate_press(SwitchAction.PRESET_DOWN)),
        "3": ("Simulate BYPASS toggle", lambda: ctrl.simulate_press(SwitchAction.BYPASS)),
        "4": ("Simulate TAP_TEMPO", lambda: ctrl.simulate_press(SwitchAction.TAP_TEMPO)),
        "5": ("Simulate TUNER", lambda: ctrl.simulate_press(SwitchAction.TUNER)),
        "l": ("LED: set test colors", lambda: (
            led_ctrl.set_pixel(0, (255, 0, 0)),
            led_ctrl.set_pixel(1, (0, 255, 0)),
            led_ctrl.set_pixel(2, (0, 0, 255)),
            led_ctrl.set_pixel(3, (255, 255, 0)),
            logger.info("LEDs: RGB pixels 0-3 set"),
        )),
        "a": ("LED: preset animate up", lambda: led_ctrl.preset_animate("up")),
        "d": ("LED: preset animate down", lambda: led_ctrl.preset_animate("down")),
        "t": ("LED: tap tempo blip", lambda: led_ctrl.tap_tempo_blip()),
        "c": ("LED: clear all", lambda: led_ctrl.clear_all()),
        "p": ("DISP: preset mode", lambda: disp.update(DisplayState(
            mode="preset", preset_name="Clean", bank_name="A1",
            fx_active=["compressor", "chorus", "delay"],
        ))),
        "u": ("DISP: tuner mode (A -12c)", lambda: disp.update(DisplayState(
            mode="tuner", tuner_note="A", tuner_cents=-12,
        ))),
        "e": ("DISP: fx_edit (Drive 0.75)", lambda: disp.update(DisplayState(
            mode="fx_edit", param_name="Drive", param_value=0.75,
        ))),
        "s": ("DISP: settings mode", lambda: disp.update(DisplayState(
            mode="settings", param_name="Brightness",
        ))),
        "q": ("Quit", None),
    }

    print("\n=== Pi Multi-FX Pedal — UI Test Console ===\n")
    print("Actions:")
    for key, (desc, _) in actions.items():
        print(f"  [{key}] {desc}")

    while True:
        try:
            choice = input("\n> ").strip().lower()
        except (EOFError, KeyboardInterrupt):
            print()
            break

        if choice == "q":
            break
        if choice in actions:
            _, fn = actions[choice]
            if fn:
                fn()
            else:
                break
        else:
            print(f"Unknown: '{choice}'")

    print("UI test console stopped.")


# ============================================================
# Main
# ============================================================


def main():
    import argparse

    parser = argparse.ArgumentParser(description="Pi Multi-FX Pedal — UI Test Tool")
    parser.add_argument("--all", action="store_true", help="Run all automated tests")
    parser.add_argument("--footswitch", action="store_true", help="Footswitch tests")
    parser.add_argument("--leds", action="store_true", help="LED tests")
    parser.add_argument("--display", action="store_true", help="Display tests")
    parser.add_argument("--interactive", "-i", action="store_true", help="Interactive menu")
    args = parser.parse_args()

    # Default to interactive if no flags
    if not any([args.all, args.footswitch, args.leds, args.display, args.interactive]):
        args.interactive = True

    if args.interactive:
        interactive_menu()
        return

    if args.all or args.footswitch:
        print("\n--- Footswitch Tests ---")
        for r in test_footswitch_basic():
            print(r)
        for r in test_footswitch_debounce():
            print(r)

    if args.all or args.leds:
        print("\n--- LED Tests ---")
        for r in test_leds():
            print(r)

    if args.all or args.display:
        print("\n--- Display Tests ---")
        for r in test_display():
            print(r)

    print("\nDone.")


if __name__ == "__main__":
    main()