from typing import Optional

import typer
from rich.console import Console
from rich.prompt import IntPrompt, Prompt

from .config import Config, MqttConfig, load_config, save_config
from .discovery import DeviceInfo, DiscoveryEntity
from .mqtt_client import MqttClient

app = typer.Typer(help="Home Assistant MQTT CLI")
console = Console()


@app.command()
def configure(
    host: Optional[str] = typer.Option(None, help="MQTT Broker Host"),
    port: Optional[int] = typer.Option(None, help="MQTT Broker Port"),
    username: Optional[str] = typer.Option(None, help="MQTT Username"),
    password: Optional[str] = typer.Option(None, help="MQTT Password"),
) -> None:
    """
    Configure the MQTT broker connection settings.

    Interactive prompts are used if no arguments are provided.
    Saved configuration is stored in `~/.config/ha-cli/config.json`.

    Args:
        host (Optional[str]): MQTT Broker Host.
        port (Optional[int]): MQTT Broker Port.
        username (Optional[str]): MQTT Username.
        password (Optional[str]): MQTT Password.
    """
    current_config = load_config()

    if not (host or port or username or password):
        # Interactive mode if no args provided
        console.print("[bold blue]Interactive Configuration[/bold blue]")
        host = Prompt.ask("MQTT Broker Host", default=current_config.mqtt.host)
        port = IntPrompt.ask("MQTT Broker Port", default=current_config.mqtt.port)
        username = Prompt.ask("MQTT Username", default=current_config.mqtt.username or "") or None
        password = (
            Prompt.ask(
                "MQTT Password",
                password=True,
                default=current_config.mqtt.password or "",
            )
            or None
        )

        # Ask to verify/test connection?
        # For now just save
    else:
        # Update only provided values
        host = host or current_config.mqtt.host
        port = port or current_config.mqtt.port
        if username is None:
            username = current_config.mqtt.username
        if password is None:
            password = current_config.mqtt.password

    new_config = Config(mqtt=MqttConfig(host=host, port=port, username=username, password=password))
    save_config(new_config)
    console.print("[green]Configuration saved successfully![/green]")


@app.command()
def register(
    unique_id: str = typer.Option(..., help="Unique ID for the entity"),
    name: str = typer.Option(..., help="Readable name for the entity"),
    device_class: Optional[str] = typer.Option(None, help="Device class (e.g., temperature, humidity)"),
    unit: Optional[str] = typer.Option(None, help="Unit of measurement"),
    component: str = typer.Option("sensor", help="HA Component (sensor, binary_sensor, etc.)"),
    state_topic: Optional[str] = typer.Option(None, help="Custom state topic. Defaults to ha-cli/<unique_id>/state"),
    device_name: Optional[str] = typer.Option(None, help="Device Name for grouping"),
    dry_run: bool = typer.Option(False, help="Print payload instead of sending"),
) -> None:
    """
    Register a new entity with Home Assistant via MQTT Discovery.

    Publishes a discovery configuration payload to the configured discovery topic.

    Args:
        unique_id (str): Unique ID for the entity.
        name (str): Readable name for the entity.
        device_class (Optional[str]): Device class (e.g., temperature, humidity).
        unit (Optional[str]): Unit of measurement.
        component (str): HA Component (sensor, binary_sensor, etc.).
        state_topic (Optional[str]): Custom state topic. Defaults to ha-cli/<unique_id>/state.
        device_name (Optional[str]): Device Name for grouping entities.
        dry_run (bool): If True, print payload instead of sending.
    """
    config = load_config()

    # Default state topic convention
    if not state_topic:
        state_topic = f"ha-cli/{unique_id}/state"

    device = None
    if device_name:
        device = DeviceInfo(
            identifiers=[f"ha-cli-{device_name}"],
            name=device_name,
            manufacturer="HA CLI",
            model="CLI Client",
        )

    entity = DiscoveryEntity(
        unique_id=unique_id,
        name=name,
        device_class=device_class,
        unit_of_measurement=unit,
        state_topic=state_topic,
        device=device,
    )

    discovery_topic = entity.get_topic(prefix=config.mqtt.discovery_prefix, component=component)
    payload = entity.model_dump_json(exclude_none=True)

    if dry_run:
        console.print(f"[bold]Topic:[/bold] {discovery_topic}")
        console.print_json(payload)
    else:
        try:
            with MqttClient(config.mqtt) as client:
                client.publish(discovery_topic, payload, retain=True)
            console.print(f"[green]Registered entity {name} ({unique_id})[/green]")
            console.print(f"State Topic: {state_topic}")
        except Exception as e:
            console.print(f"[red]Failed to register:[/red] {e}")


@app.command()
def send(
    unique_id: str = typer.Option(..., help="Unique ID of the entity"),
    state: str = typer.Option(..., help="State/Value to send"),
    topic: Optional[str] = typer.Option(None, help="Override state topic. Defaults to ha-cli/<unique_id>/state"),
    dry_run: bool = typer.Option(False, help="Print payload instead of sending"),
) -> None:
    """
    Send a state update for a registered entity.

    Args:
        unique_id (str): Unique ID of the entity.
        state (str): State/Value to send.
        topic (Optional[str]): Override state topic. Defaults to ha-cli/<unique_id>/state.
        dry_run (bool): If True, print payload instead of sending.
    """
    config = load_config()

    if not topic:
        topic = f"ha-cli/{unique_id}/state"

    if dry_run:
        console.print(f"[bold]Topic:[/bold] {topic}")
        console.print(f"[bold]Payload:[/bold] {state}")
    else:
        try:
            with MqttClient(config.mqtt) as client:
                client.publish(topic, state)
            console.print(f"[green]Sent state '{state}' to '{topic}'[/green]")
        except Exception as e:
            console.print(f"[red]Failed to send state:[/red] {e}")


if __name__ == "__main__":
    app()
