#!/usr/bin/env python3
"""Standalone MIDI test tool — loopback test, clock monitor, and CC debug.

Tests MIDI UART and/or USB ports without requiring the full pedal
application. Works headless (no display needed).

Usage:
    # Default (UART on /dev/ttyAMA0)
    python scripts/midi_test.py

    # Custom UART port
    python scripts/midi_test.py --uart /dev/ttyUSB0

    # USB-MIDI only
    python scripts/midi_test.py --no-uart --usb

    # Display clock BPM in real-time
    python scripts/midi_test.py --clock-monitor

    # Check all available MIDI ports (discovery mode)
    python scripts/midi_test.py --discover
"""

from __future__ import annotations

import argparse
import logging
import sys
import time
from typing import NoReturn

# ── Add src to path ─────────────────────────────────────────────────
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))

from midi.handler import (
    MIDIHandler,
    UARTMIDI,
    USBMIDI,
    CC_EXPRESSION,
    CC_VOLUME,
)

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    datefmt="%H:%M:%S",
)
logger = logging.getLogger("midi-test")


def discover_ports() -> None:
    """List available MIDI ports without opening them."""
    print("=" * 50)
    print("MIDI Port Discovery")
    print("=" * 50)

    # USBMIDI port listing
    try:
        import rtmidi  # type: ignore[import-untyped]

        midi_in = rtmidi.MidiIn()
        midi_out = rtmidi.MidiOut()
        midi_in.ignore_types(timing=True, sysex=False)

        ports_in = midi_in.get_ports()
        ports_out = midi_out.get_ports()

        print(f"\nUSB-MIDI Input Ports ({len(ports_in)}):")
        for i, p in enumerate(ports_in):
            print(f"  {i}: {p}")

        print(f"\nUSB-MIDI Output Ports ({len(ports_out)}):")
        for i, p in enumerate(ports_out):
            print(f"  {i}: {p}")

        midi_in.close_port()
    except ImportError:
        print("\npython-rtmidi not installed — cannot scan USB ports")

    # UART port check
    print("\nUART MIDI Ports:")
    for port in ["/dev/ttyAMA0", "/dev/ttyUSB0", "/dev/ttyS0"]:
        p = Path(port)
        if p.exists():
            print(f"  {port} — EXISTS")
        else:
            print(f"  {port} — not found")

    print()
    print("On Raspberry Pi, also check:")
    print("  /dev/serial0  (symlink to active UART)")
    print("  dmesg | grep tty")
    print("  dtoverlay=disable-bt  (if using UART for MIDI)")
    print("  core_freq=250         (for stable UART baud rate)")


def clock_monitor(handler: MIDIHandler) -> None:
    """Monitor MIDI clock BPM in real-time."""

    def on_bpm(bpm: float) -> None:
        logger.info("MIDI Clock BPM: %.1f", bpm)

    handler.set_clock_callback(on_bpm)
    logger.info("Clock monitor active — send MIDI clock to see BPM")


def dispatch_example(handler: MIDIHandler) -> None:
    """Subscribe to all MIDI events and print them."""

    def on_pc(channel: int, program: int) -> None:
        logger.info("  PC: ch=%d → program=%d", channel, program)

    def on_cc(cc_number: int, value: int, channel: int) -> None:
        logger.info("  CC: ch=%d, cc=%d → %d", channel, cc_number, value)

    def on_note(note: int, velocity: int, channel: int) -> None:
        logger.info("  Note: ch=%d, note=%d, vel=%d (on=%s)",
                     channel, note, velocity, velocity > 0)

    def on_learn(mapping: object) -> None:
        logger.info("  MIDI Learned: %s", mapping)

    handler.set_pc_callback(on_pc)
    handler.register_cc(CC_EXPRESSION, lambda v, c: on_cc(CC_EXPRESSION, v, c))
    handler.register_cc(CC_VOLUME, lambda v, c: on_cc(CC_VOLUME, v, c))
    handler.set_note_callback(on_note)
    handler.set_midi_learn_callback(on_learn)


def send_test_messages(handler: MIDIHandler) -> None:
    """Send test MIDI messages out all open ports."""
    logger.info("Sending test MIDI messages...")

    handler.send(MIDIHandler.send_note_on(60, velocity=100))
    time.sleep(0.1)
    handler.send(MIDIHandler.send_note_off(60))
    time.sleep(0.1)
    handler.send(MIDIHandler.send_cc(7, 100))
    time.sleep(0.1)
    handler.send(MIDIHandler.send_pc(1))
    time.sleep(0.1)
    handler.send(MIDIHandler.send_pitch_bend(8192))

    logger.info("Test messages sent (5 messages)")


def main() -> None:
    parser = argparse.ArgumentParser(
        description="MIDI test tool for Pi Multi-FX Pedal",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=__doc__,
    )
    parser.add_argument("--uart", default="/dev/ttyAMA0",
                        help="UART device path (default: /dev/ttyAMA0)")
    parser.add_argument("--no-uart", action="store_true",
                        help="Skip UART MIDI")
    parser.add_argument("--no-usb", action="store_true",
                        help="Skip USB MIDI")
    parser.add_argument("--usb-port", default="",
                        help="Filter for USB-MIDI port name")
    parser.add_argument("--clock-monitor", action="store_true",
                        help="Display MIDI clock BPM in real-time")
    parser.add_argument("--discover", action="store_true",
                        help="Discover available MIDI ports and exit")
    parser.add_argument("--send-test", action="store_true",
                        help="Send test MIDI messages after startup")
    parser.add_argument("--verbose", "-v", action="store_true",
                        help="Enable debug logging")
    args = parser.parse_args()

    if args.verbose:
        logging.getLogger("midi").setLevel(logging.DEBUG)

    if args.discover:
        discover_ports()
        return

    handler = MIDIHandler()

    uart_port = None if args.no_uart else args.uart
    usb_enabled = not args.no_usb

    print("=" * 50)
    print("Pi Multi-FX Pedal — MIDI Test Tool")
    print("=" * 50)
    print(f"  UART MIDI:  {uart_port or 'disabled'}")
    print(f"  USB MIDI:   {'enabled' if usb_enabled else 'disabled'}")
    print(f"  Clock mon:  {'yes' if args.clock_monitor else 'no'}")
    print(f"  Send test:  {'yes' if args.send_test else 'no'}")
    print("=" * 50)

    if args.clock_monitor:
        clock_monitor(handler)

    dispatch_example(handler)

    print("\nStarting MIDI handler...")
    handler.start(uart_port=uart_port, usb=usb_enabled, usb_port_name=args.usb_port)

    print(f"\nActive interfaces: {handler.interface_names or 'none (software-only)'}")

    if args.send_test:
        send_test_messages(handler)

    print("\nListening for MIDI messages...")
    print("Press Ctrl+C to stop.\n")

    try:
        while True:
            time.sleep(0.1)
    except KeyboardInterrupt:
        print("\nShutting down...")
    finally:
        handler.stop()
        print("MIDI handler stopped.")


if __name__ == "__main__":
    main()