"""Main CLI for VoiceMQTT."""

import configparser
import os
import select
import subprocess
import sys
import termios
import threading
import time
import tty

import click
from rich.console import Console

from jusfltuls.voicemqtt.audio import AudioRecorder
from jusfltuls.voicemqtt.clipboard import ClipboardManager
from jusfltuls.voicemqtt.mqtt_client import MQTTActivator
from jusfltuls.voicemqtt.transcriber import Transcriber


console = Console()
VERSION = "0.1.0"


def check_system_dependencies():
    """Check if required system libraries are installed.

    Returns:
        tuple: (bool: all installed, list: missing packages)
    """
    required_packages = {
        "portaudio19-dev": "portaudio19-dev",
        "libsdl2-dev": "libsdl2-dev",
        "libasound2-dev": "libasound2-dev"
    }

    missing = []

    for package, name in required_packages.items():
        try:
            result = subprocess.run(
                ["dpkg", "-l", package],
                capture_output=True,
                text=True,
                check=False
            )
            if result.returncode != 0 or package not in result.stdout:
                missing.append(name)
        except Exception:
            missing.append(name)

    return len(missing) == 0, missing


def print_install_advice():
    """Print installation advice for missing dependencies."""
    console.print("\n[red]Missing system libraries detected![/red]")
    console.print("\n[bold]Install required packages:[/bold]")
    console.print("[yellow]  sudo apt install libsdl2-dev portaudio19-dev libasound2-dev[/yellow]")
    console.print("\n[dim]These libraries are required for audio recording.[/dim]")
    console.print("")


def load_public_mqtt_config():
    """Load public MQTT configuration from ~/.config/influxdb/totalconfig.conf.

    Returns:
        tuple: (host, port, username, password, use_tls) or None if config not found
    """
    config_path = os.path.expanduser("~/.config/influxdb/totalconfig.conf")

    if not os.path.exists(config_path):
        return None

    try:
        config = configparser.ConfigParser()
        config.read(config_path)

        if "mqtt public" not in config:
            return None

        server = config.get("mqtt public", "server")
        username = config.get("mqtt public", "username")
        password = config.get("mqtt public", "password")

        # Strip URL scheme (e.g., https://, mqtt://, mqtts://)
        if "://" in server:
            server = server.split("://", 1)[1]

        if ":" in server:
            host, port_str = server.rsplit(":", 1)
            port = int(port_str)
        else:
            host = server
            port = 8883

        use_tls = (port == 8883)

        return host, port, username, password, use_tls
    except Exception as e:
        console.print(f"[red]Error reading MQTT config: {e}[/red]")
        return None


def drain_stdin():
    """Drain any pending input from stdin to prevent blocking."""
    try:
        if sys.stdin.isatty():
            old_settings = termios.tcgetattr(sys.stdin)
            tty.setcbreak(sys.stdin.fileno())
            try:
                while select.select([sys.stdin], [], [], 0)[0]:
                    if not sys.stdin.read(1):
                        break
            finally:
                termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
    except (termios.error, OSError, AttributeError):
        pass


class ParallelInputHandler:
    """Handles keyboard input in parallel with MQTT operations."""

    def __init__(self):
        self.key_pressed = None
        self._stop_event = threading.Event()
        self._thread = None
        self._running = False

    def start(self):
        """Start the input handling thread."""
        if not self._running:
            self._running = True
            self._stop_event.clear()
            self._thread = threading.Thread(target=self._input_loop, daemon=True)
            self._thread.start()

    def stop(self):
        """Stop the input handling thread."""
        self._stop_event.set()
        self._running = False
        if self._thread:
            self._thread.join(timeout=0.5)

    def _input_loop(self):
        """Main input loop running in separate thread."""
        try:
            if sys.stdin.isatty():
                old_settings = termios.tcgetattr(sys.stdin)
                tty.setcbreak(sys.stdin.fileno())
                try:
                    while not self._stop_event.is_set():
                        if select.select([sys.stdin], [], [], 0.1)[0]:
                            char = sys.stdin.read(1)
                            if char:
                                self.key_pressed = char.lower()
                finally:
                    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
        except (termios.error, OSError, AttributeError):
            pass

    def get_key(self):
        """Get and clear the last key pressed.

        Returns:
            str or None: The key pressed, or None if no key pressed
        """
        key = self.key_pressed
        self.key_pressed = None
        return key

    def has_key(self, key):
        """Check if specific key was pressed and clear it.

        Args:
            key: The key to check for

        Returns:
            bool: True if the key was pressed
        """
        if self.key_pressed == key.lower():
            self.key_pressed = None
            return True
        return False


def show_menu(console, threshold, paste_enabled, mqtt_mode, mqtt_connected):
    """Display the interactive menu with current threshold, paste mode, and MQTT status."""
    paste_status = "[green]ON[/green]" if paste_enabled else "[red]OFF[/red]"
    
    # MQTT status display
    if mqtt_mode == "off":
        mqtt_status = "[red]OFF[/red]"
    elif mqtt_mode == "local":
        mqtt_status = "[green]LOCAL[/green]" if mqtt_connected else "[yellow]LOCAL[/yellow]"
    else:  # remote
        mqtt_status = "[green]REMOTE[/green]" if mqtt_connected else "[yellow]REMOTE[/yellow]"
    
    console.print(f"[dim]Menu: [cyan]q[/cyan]uit | [cyan]r[/cyan]ecord | [cyan]n[/cyan]oise | [cyan]m[/cyan]qtt={mqtt_status} | [cyan]c[/cyan]lipboard/paste={paste_status}[/dim]")
    console.print(f"[dim]       threshold=[cyan]{threshold:.4f}[/cyan] [cyan]d[/cyan]own/[cyan]u[/cyan]p[/dim]")


@click.command()
@click.option(
    "--mqtt-host",
    default="localhost",
    help="MQTT broker host (default: localhost)",
    show_default=True
)
@click.option(
    "--mqtt-port",
    default=1883,
    help="MQTT broker port (default: 1883)",
    show_default=True
)
@click.option(
    "--mqtt-topic",
    default="voicemqtt/activate",
    help="MQTT topic for activation (default: voicemqtt/activate)",
    show_default=True
)
@click.option(
    "--mqtt-user",
    default=None,
    help="MQTT username for authentication",
    show_default=True
)
@click.option(
    "--mqtt-pass",
    default=None,
    help="MQTT password for authentication",
    show_default=True
)
@click.option(
    "--mqtt-tls",
    is_flag=True,
    help="Enable TLS/SSL encryption for MQTT",
    default=False
)
@click.option(
    "--mqtt-ca-certs",
    default=None,
    help="Path to CA certificates file for TLS",
    show_default=True
)
@click.option(
    "--public-mqtt",
    is_flag=True,
    help="Use public MQTT broker from ~/.config/influxdb/totalconfig.conf",
    default=False
)
@click.option(
    "--recording-status-topic",
    default="voicemqtt/recording",
    help="MQTT topic for recording status (2=sound, 1=silence, 0=idle)",
    show_default=True
)
@click.option(
    "--model",
    default="base",
    help="Whisper model size (tiny, base, small, medium, large-v1/v2/v3)",
    show_default=True
)
@click.option(
    "--device",
    default="cpu",
    help="Device for Whisper (cpu, cuda)",
    show_default=True
)
@click.option(
    "--language",
    default="en",
    help="Language for transcription (e.g., en, cs, de, auto)",
    show_default=True
)
@click.option(
    "--silence-threshold",
    default=0.15,
    help="Audio level threshold for silence detection",
    show_default=True
)
@click.option(
    "--silence-duration",
    default=3.0,
    help="Seconds of silence before stopping recording",
    show_default=True
)
@click.option(
    "--single/--continuous",
    default=False,
    help="Run once and exit (single) or wait for multiple activations (continuous)",
    show_default=True
)
@click.option(
    "--calibrate",
    is_flag=True,
    help="Calibrate silence threshold (records 10s, use last 3s for silence)",
    default=False
)
@click.option(
    "--paste",
    is_flag=True,
    help="Also paste the transcription into the active window using ydotool",
    default=False
)
@click.option(
    "--no-mqtt",
    is_flag=True,
    help="Run without MQTT - use keyboard commands only",
    default=False
)
@click.version_option(version=VERSION, prog_name="voicemqtt")
@click.help_option()
def main(
    mqtt_host,
    mqtt_port,
    mqtt_topic,
    mqtt_user,
    mqtt_pass,
    mqtt_tls,
    mqtt_ca_certs,
    public_mqtt,
    recording_status_topic,
    model,
    device,
    language,
    silence_threshold,
    silence_duration,
    single,
    calibrate,
    paste,
    no_mqtt
):
    """VoiceMQTT - MQTT-activated voice transcription to clipboard.

    Listens for activation messages on MQTT topic, records audio with
    real-time level display, transcribes using local Whisper, and copies
    text to Wayland clipboard.

    \b
    Examples:
        voicemqtt                           # Use default settings
        voicemqtt --mqtt-host 192.168.1.5   # Custom MQTT broker
        voicemqtt --model small             # Use better quality model
        voicemqtt --single                  # Run once and exit
        voicemqtt --public-mqtt             # Use public MQTT from config
        voicemqtt --no-mqtt                 # Keyboard control only
        voicemqtt --mqtt-host mqtt.example.com --mqtt-port 8883 --mqtt-tls --mqtt-user myuser --mqtt-pass mypass
    """
    console.print("[bold cyan]VoiceMQTT[/bold cyan]", style="bold")
    console.print(f"[dim]Version {VERSION}[/dim]\n")

    # Check for system dependencies
    all_installed, missing_packages = check_system_dependencies()
    if not all_installed:
        print_install_advice()

    # Check for additional audio libraries and import sounddevice
    try:
        import sounddevice as sd
    except OSError as e:
        if "PortAudio" in str(e) or "audio" in str(e).lower():
            print_install_advice()
            console.print("[red]Error: Audio libraries not found! Please install the packages listed above.[/red]")
            sys.exit(1)
        raise

    if public_mqtt:
        config = load_public_mqtt_config()
        if config is None:
            console.print("[red]Failed to load public MQTT configuration.[/red]")
            console.print("[dim]Ensure ~/.config/influxdb/totalconfig.conf exists with [mqtt public] section.[/dim]")
            sys.exit(1)
        mqtt_host, mqtt_port, mqtt_user, mqtt_pass, mqtt_tls = config
        console.print(f"[green]Loaded public MQTT config: {mqtt_host}:{mqtt_port}[/green]")

    if calibrate:
        audio_recorder = AudioRecorder()
        recommended_threshold = audio_recorder.calibrate_silence()

        if recommended_threshold:
            console.print(f"\n[dim]Use this threshold with: --silence-threshold {recommended_threshold:.6f}[/dim]")
        else:
            console.print("[red]Calibration failed[/red]")
            sys.exit(1)
        return

    mqtt_activator = MQTTActivator(
        host=mqtt_host,
        port=mqtt_port,
        topic=mqtt_topic,
        username=mqtt_user,
        password=mqtt_pass,
        use_tls=mqtt_tls,
        tls_ca_certs=mqtt_ca_certs
    )

    audio_recorder = AudioRecorder(
        silence_threshold=silence_threshold,
        silence_duration=silence_duration
    )

    transcriber = Transcriber(
        model_size=model,
        device=device,
        language=None if language == "auto" else language
    )

    clipboard = ClipboardManager()
    
    # Check if all required tools are installed
    if not clipboard.check_requirements(require_paste=paste):
        console.print("[red]Please install missing tools and try again.[/red]")
        sys.exit(1)

    # Initialize parallel input handler
    input_handler = ParallelInputHandler()
    input_handler.start()

    # Track MQTT mode: "off", "local", or "remote"
    if no_mqtt:
        mqtt_mode = "off"
    elif public_mqtt:
        mqtt_mode = "remote"
    else:
        mqtt_mode = "local"
    
    # Track connection status
    mqtt_connected = False
    
    # Store public MQTT config for switching
    public_mqtt_config = None
    if public_mqtt:
        public_mqtt_config = load_public_mqtt_config()
    
    def connect_mqtt(current_activator, mode):
        """Connect to MQTT broker based on mode.
        
        Args:
            current_activator: Current MQTTActivator instance
            mode: "local", "remote", or "off"
            
        Returns:
            tuple: (new_activator, connected) - new MQTTActivator instance and connection status
        """
        nonlocal mqtt_connected
        
        # Disconnect current connection if any
        if mqtt_connected:
            current_activator.disconnect()
            mqtt_connected = False
        
        if mode == "off":
            console.print("[dim]MQTT disabled - keyboard-only mode[/dim]")
            return current_activator, False
        
        if mode == "remote":
            config = public_mqtt_config if public_mqtt_config else load_public_mqtt_config()
            if config is None:
                console.print("[red]Failed to load public MQTT configuration.[/red]")
                console.print("[dim]Ensure ~/.config/influxdb/totalconfig.conf exists with [mqtt public] section.[/dim]")
                return current_activator, False
            host, port, user, passwd, use_tls = config
            # Create new MQTT activator with remote settings
            new_activator = MQTTActivator(
                host=host,
                port=port,
                topic=mqtt_topic,
                username=user,
                password=passwd,
                use_tls=use_tls
            )
            console.print(f"[bold]Connecting to remote MQTT broker {host}:{port}...[/bold]")
        else:  # local
            # Create new MQTT activator with local settings
            new_activator = MQTTActivator(
                host=mqtt_host,
                port=mqtt_port,
                topic=mqtt_topic,
                username=mqtt_user,
                password=mqtt_pass,
                use_tls=mqtt_tls,
                tls_ca_certs=mqtt_ca_certs
            )
            console.print(f"[bold]Connecting to local MQTT broker {mqtt_host}:{mqtt_port}...[/bold]")
        
        if new_activator.connect():
            mqtt_connected = True
            return new_activator, True
        else:
            console.print("[red]Failed to connect to MQTT broker.[/red]")
            return new_activator, False
    
    # Initial connection
    if mqtt_mode != "off":
        mqtt_activator, _ = connect_mqtt(mqtt_activator, mqtt_mode)
        if not mqtt_connected and not public_mqtt and not no_mqtt:
            # If initial local connection fails, don't exit - let user switch modes
            console.print("[dim]Use 'm' key to switch MQTT modes or disable MQTT.[/dim]")
    
    # Track paste/clipboard mode (can be toggled via menu)
    paste_enabled = paste
    
    try:
        while True:
            # Show menu before waiting
            show_menu(console, audio_recorder.silence_threshold, paste_enabled, mqtt_mode, mqtt_connected)
            
            # Wait for activation (either MQTT or keyboard 'r')
            activated = False
            console.print("[cyan]Waiting for activation...[/cyan]")
            
            while not activated:
                # Check for keyboard input
                key = input_handler.get_key()
                if key == 'q':
                    console.print("[yellow]Quit requested by user[/yellow]")
                    raise KeyboardInterrupt
                elif key == 'r':
                    console.print("[bold green]Activation triggered via keyboard![/bold green]")
                    activated = True
                elif key == 'd':
                    # Decrease threshold (more sensitive)
                    audio_recorder.silence_threshold = max(0.0001, audio_recorder.silence_threshold * 0.8)
                    console.print(f"[dim]Threshold decreased to {audio_recorder.silence_threshold:.4f}[/dim]")
                    show_menu(console, audio_recorder.silence_threshold, paste_enabled, mqtt_mode, mqtt_connected)
                elif key == 'u':
                    # Increase threshold (less sensitive)
                    audio_recorder.silence_threshold = min(0.5, audio_recorder.silence_threshold * 1.25)
                    console.print(f"[dim]Threshold increased to {audio_recorder.silence_threshold:.4f}[/dim]")
                    show_menu(console, audio_recorder.silence_threshold, paste_enabled, mqtt_mode, mqtt_connected)
                elif key == 'n':
                    # Measure noise and suggest threshold
                    recommended = audio_recorder.measure_noise_quick()
                    if recommended:
                        console.print(f"\n[dim]Press [cyan]d[/cyan] or [cyan]u[/cyan] to adjust, or current threshold will be used[/dim]")
                    show_menu(console, audio_recorder.silence_threshold, paste_enabled, mqtt_mode, mqtt_connected)
                elif key == 'c':
                    # Toggle clipboard/paste mode
                    paste_enabled = not paste_enabled
                    status = "enabled" if paste_enabled else "disabled"
                    console.print(f"[dim]Clipboard/paste mode {status}[/dim]")
                    show_menu(console, audio_recorder.silence_threshold, paste_enabled, mqtt_mode, mqtt_connected)
                elif key == 'm':
                    # Cycle through MQTT modes: local -> remote -> off -> local
                    if mqtt_mode == "local":
                        mqtt_mode = "remote"
                        console.print("[dim]Switching to remote MQTT...[/dim]")
                        mqtt_activator, _ = connect_mqtt(mqtt_activator, "remote")
                    elif mqtt_mode == "remote":
                        mqtt_mode = "off"
                        console.print("[dim]Switching to keyboard-only mode (MQTT off)...[/dim]")
                        mqtt_activator, _ = connect_mqtt(mqtt_activator, "off")
                    else:  # off
                        mqtt_mode = "local"
                        console.print("[dim]Switching to local MQTT...[/dim]")
                        mqtt_activator, _ = connect_mqtt(mqtt_activator, "local")
                    show_menu(console, audio_recorder.silence_threshold, paste_enabled, mqtt_mode, mqtt_connected)
                
                # Check for MQTT activation (if connected)
                if mqtt_connected and mqtt_activator.is_activated():
                    activated = True
                
                if not activated:
                    time.sleep(0.05)
            
            # Recording phase
            def audio_status_callback(status):
                if mqtt_connected:
                    mqtt_activator.publish(recording_status_topic, status)

            audio_data = audio_recorder.record(status_callback=audio_status_callback)

            if mqtt_connected:
                mqtt_activator.publish(recording_status_topic, "0")
            
            if audio_data is not None:
                transcript = transcriber.transcribe(audio_data)
                
                if transcript:
                    console.print(f"\n[bold]Transcript:[/bold] {transcript}")
                    clipboard.copy_text(transcript)
                    if paste_enabled:
                        clipboard.type_text(transcript)
                else:
                    console.print("[yellow]No transcription produced.[/yellow]")
            
            console.print()
            sys.stdout.flush()

            if single:
                break
            
            time.sleep(0.1)
            
    except KeyboardInterrupt:
        console.print("\n[yellow]Interrupted by user[/yellow]")
    finally:
        input_handler.stop()
        if mqtt_connected:
            mqtt_activator.disconnect()
        console.print("[dim]Goodbye![/dim]")


if __name__ == "__main__":
    main()
