"""
SoraCLI - Command Line Interface.

Main entry point for the SoraCLI application.
"""

import asyncio
import signal
import sys
from typing import Optional

import click

from soracli import __version__
from soracli.config import (
    load_config, save_config, init_config, load_theme, 
    list_themes, SoraConfig, get_api_key, ensure_config_dir
)
from soracli.api.weather_fetcher import (
    WeatherFetcher, WeatherFetchError, get_demo_weather
)
from soracli.engines import get_engine_for_condition
from soracli.engines.base import EngineConfig
from soracli.engines.rain import RainEngine
from soracli.engines.snow import SnowEngine
from soracli.engines.thunder import ThunderEngine
from soracli.engines.fog import FogEngine
from soracli.engines.clear import ClearEngine
from soracli.renderer.ascii_renderer import ANSIRenderer
from soracli.daemon.manager import DaemonManager


# Global state for signal handling
_current_engine = None
_renderer = ANSIRenderer()


def setup_signal_handlers():
    """Setup signal handlers for graceful shutdown."""
    def signal_handler(sig, frame):
        global _current_engine
        if _current_engine:
            _current_engine.stop()
        print(_renderer.reset_terminal())
        sys.exit(0)
    
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)


@click.group()
@click.version_option(version=__version__, prog_name='soracli')
def cli():
    """
    SoraCLI - Real-time terminal ambient environment engine.
    
    Synchronizes real-world weather conditions with animated terminal simulations.
    """
    pass


@cli.command()
@click.option('--location', '-l', default=None, 
              help='Location for weather data (city name)')
@click.option('--theme', '-t', default=None,
              help='Visual theme to use')
@click.option('--refresh', '-r', default=None, type=int,
              help='Weather refresh rate in seconds')
@click.option('--intensity', '-i', default=1.0, type=float,
              help='Animation intensity (0.1 to 2.0)')
@click.option('--no-status', is_flag=True,
              help='Hide status bar')
@click.option('--offline', is_flag=True,
              help='Run in offline mode (use cached data)')
def start(location: Optional[str], theme: Optional[str], 
          refresh: Optional[int], intensity: float,
          no_status: bool, offline: bool):
    """
    Start the weather simulation with live data.
    
    Fetches real-time weather data for the specified location and renders
    appropriate weather animations in the terminal.
    
    Examples:
    
        soracli start --location "Bangalore"
        
        soracli start -l "New York" --theme cyberpunk
        
        soracli start --offline
    """
    global _current_engine
    
    setup_signal_handlers()
    
    # Load configuration
    config = load_config()
    
    # Override with command line options
    location = location or config.location
    theme_name = theme or config.theme
    refresh_rate = refresh or config.refresh_rate
    
    # Load theme
    theme_data = load_theme(theme_name)
    
    # Get API key
    api_key = get_api_key()
    
    if not api_key and not offline:
        click.echo(click.style(
            "Error: No API key found. Set OPENWEATHERMAP_API_KEY environment variable "
            "or run 'soracli init' to configure.",
            fg='red'
        ))
        click.echo("\nYou can also use demo mode: soracli demo --rain")
        return
    
    click.echo(click.style(f"SoraCLI v{__version__}", fg='cyan', bold=True))
    click.echo(f"Location: {location}")
    click.echo(f"Theme: {theme_name}")
    click.echo(f"Refresh: every {refresh_rate} seconds")
    click.echo("\nFetching weather data...")
    
    try:
        asyncio.run(_run_weather_loop(
            location=location,
            theme_data=theme_data,
            refresh_rate=refresh_rate,
            intensity=intensity,
            show_status=not no_status,
            offline=offline,
            api_key=api_key,
            units=config.units,
        ))
    except KeyboardInterrupt:
        pass
    finally:
        print(_renderer.reset_terminal())


async def _run_weather_loop(
    location: str,
    theme_data: dict,
    refresh_rate: int,
    intensity: float,
    show_status: bool,
    offline: bool,
    api_key: Optional[str],
    units: str,
):
    """Main weather simulation loop."""
    global _current_engine
    
    fetcher = WeatherFetcher(api_key=api_key, units=units)
    
    # Clear screen and hide cursor
    print(_renderer.enter_alternate_buffer())
    print(_renderer.hide_cursor())
    
    last_condition = None
    weather_task = None
    
    try:
        while True:
            # Fetch weather data
            try:
                if offline:
                    weather = fetcher.get_offline_fallback(location)
                    if not weather:
                        weather = get_demo_weather('clear')
                else:
                    weather = await fetcher.fetch_async(location)
            except WeatherFetchError as e:
                # Try offline fallback
                weather = fetcher.get_offline_fallback(location)
                if not weather:
                    weather = get_demo_weather('clear')
            
            # Check if weather condition changed
            if weather.condition != last_condition:
                last_condition = weather.condition
                
                # Stop current engine
                if _current_engine:
                    _current_engine.stop()
                
                # Create new engine for current condition
                engine_class = get_engine_for_condition(weather.condition)
                engine_config = EngineConfig(
                    intensity=intensity * weather.get_intensity(),
                    theme=theme_data,
                    is_night=weather.is_night,
                    sound_enabled=False,
                )
                _current_engine = engine_class(engine_config)
                
                # Start engine loop
                if weather_task:
                    weather_task.cancel()
                    try:
                        await weather_task
                    except asyncio.CancelledError:
                        pass
                
                weather_task = asyncio.create_task(_current_engine.run_loop())
            
            # Wait for next refresh
            await asyncio.sleep(refresh_rate)
            
    finally:
        if _current_engine:
            _current_engine.stop()
        if weather_task:
            weather_task.cancel()


@cli.command()
@click.option('--rain', 'condition', flag_value='rain',
              help='Simulate rain')
@click.option('--snow', 'condition', flag_value='snow',
              help='Simulate snow')
@click.option('--storm', 'condition', flag_value='storm',
              help='Simulate thunderstorm')
@click.option('--fog', 'condition', flag_value='fog',
              help='Simulate fog')
@click.option('--clear', 'condition', flag_value='clear',
              help='Simulate clear weather (stars at night)')
@click.option('--night', is_flag=True,
              help='Use night mode (for clear weather)')
@click.option('--theme', '-t', default='default',
              help='Visual theme to use')
@click.option('--intensity', '-i', default=1.0, type=float,
              help='Animation intensity (0.1 to 2.0)')
@click.option('--duration', '-d', default=0, type=int,
              help='Run for specified seconds (0 = indefinite)')
def demo(condition: Optional[str], night: bool, theme: str, 
         intensity: float, duration: int):
    """
    Run weather simulations in demo mode (no API required).
    
    Perfect for testing or enjoying weather animations without an API key.
    
    Examples:
    
        soracli demo --rain
        
        soracli demo --snow --theme minimal
        
        soracli demo --storm --intensity 1.5
        
        soracli demo --clear --night
    """
    global _current_engine
    
    if not condition:
        click.echo("Please specify a weather condition: --rain, --snow, --storm, --fog, or --clear")
        return
    
    setup_signal_handlers()
    
    click.echo(click.style(f"SoraCLI Demo Mode", fg='cyan', bold=True))
    click.echo(f"Condition: {condition}")
    click.echo(f"Theme: {theme}")
    click.echo("\nPress Ctrl+C to exit.\n")
    
    # Small delay to let user read
    import time
    time.sleep(1)
    
    try:
        asyncio.run(_run_demo(
            condition=condition,
            night=night,
            theme=theme,
            intensity=intensity,
            duration=duration,
        ))
    except KeyboardInterrupt:
        pass
    finally:
        print(_renderer.reset_terminal())


async def _run_demo(condition: str, night: bool, theme: str, 
                    intensity: float, duration: int):
    """Run demo simulation."""
    global _current_engine
    
    theme_data = load_theme(theme)
    
    # Map condition to engine
    engines = {
        'rain': RainEngine,
        'snow': SnowEngine,
        'storm': ThunderEngine,
        'fog': FogEngine,
        'clear': ClearEngine,
    }
    
    engine_class = engines.get(condition, ClearEngine)
    
    engine_config = EngineConfig(
        intensity=intensity,
        theme=theme_data,
        is_night=night or (condition == 'clear'),
        sound_enabled=False,
    )
    
    _current_engine = engine_class(engine_config)
    
    # Clear screen and hide cursor
    print(_renderer.enter_alternate_buffer())
    print(_renderer.hide_cursor())
    
    try:
        if duration > 0:
            # Run for specified duration
            task = asyncio.create_task(_current_engine.run_loop())
            await asyncio.sleep(duration)
            _current_engine.stop()
            await task
        else:
            # Run indefinitely
            await _current_engine.run_loop()
    except asyncio.CancelledError:
        pass
    finally:
        _current_engine.stop()


@cli.command()
@click.option('--location', '-l', prompt='Default location',
              default='London', help='Default weather location')
@click.option('--theme', '-t', prompt='Theme',
              default='default', help='Default theme')
@click.option('--refresh', '-r', prompt='Refresh rate (seconds)',
              default=60, type=int, help='Weather refresh rate')
@click.option('--sound/--no-sound', prompt='Enable sound',
              default=False, help='Enable sound effects')
@click.option('--units', prompt='Temperature units',
              default='metric', type=click.Choice(['metric', 'imperial', 'standard']),
              help='Temperature units')
@click.option('--api-key', '-k', default=None,
              help='OpenWeatherMap API key')
def init(location: str, theme: str, refresh: int, 
         sound: bool, units: str, api_key: Optional[str]):
    """
    Initialize SoraCLI configuration.
    
    Creates a configuration file at ~/.soracli/config.yaml with your preferences.
    
    Example:
    
        soracli init
        
        soracli init --location "Tokyo" --theme cyberpunk
    """
    ensure_config_dir()
    
    config = init_config(
        location=location,
        theme=theme,
        refresh_rate=refresh,
        sound=sound,
        units=units,
        api_key=api_key,
    )
    
    click.echo(click.style("\n✓ Configuration saved!", fg='green', bold=True))
    click.echo(f"\nConfig file: ~/.soracli/config.yaml")
    click.echo(f"\nSettings:")
    click.echo(f"  Location:     {config.location}")
    click.echo(f"  Theme:        {config.theme}")
    click.echo(f"  Refresh rate: {config.refresh_rate}s")
    click.echo(f"  Sound:        {'enabled' if config.sound else 'disabled'}")
    click.echo(f"  Units:        {config.units}")
    
    if not config.api_key and not api_key:
        click.echo(click.style(
            "\nNote: No API key configured. Set OPENWEATHERMAP_API_KEY or use demo mode.",
            fg='yellow'
        ))


@cli.command('themes')
def list_themes_cmd():
    """
    List available themes.
    
    Shows all built-in and user-defined themes.
    """
    themes = list_themes()
    
    click.echo(click.style("Available Themes:", fg='cyan', bold=True))
    click.echo("")
    
    for theme_name in themes:
        theme_data = load_theme(theme_name)
        description = theme_data.get('description', 'No description')
        click.echo(f"  • {click.style(theme_name, fg='green', bold=True)}")
        click.echo(f"    {description}\n")


@cli.command()
def status():
    """
    Show current configuration status.
    
    Displays loaded configuration and system information.
    """
    config = load_config()
    api_key = get_api_key()
    
    click.echo(click.style(f"SoraCLI v{__version__}", fg='cyan', bold=True))
    click.echo("")
    click.echo(click.style("Configuration:", fg='yellow'))
    click.echo(f"  Location:     {config.location}")
    click.echo(f"  Theme:        {config.theme}")
    click.echo(f"  Refresh rate: {config.refresh_rate}s")
    click.echo(f"  Sound:        {'enabled' if config.sound else 'disabled'}")
    click.echo(f"  Units:        {config.units}")
    click.echo("")
    click.echo(click.style("API Status:", fg='yellow'))
    click.echo(f"  API Key:      {'configured' if api_key else 'not configured'}")
    click.echo("")
    click.echo(click.style("Paths:", fg='yellow'))
    click.echo(f"  Config:       ~/.soracli/config.yaml")
    click.echo(f"  Themes:       ~/.soracli/themes/")


@cli.command()
@click.argument('location')
def weather(location: str):
    """
    Fetch and display weather data for a location.
    
    Useful for testing API connectivity and seeing raw weather data.
    
    Example:
    
        soracli weather "Bangalore"
    """
    api_key = get_api_key()
    
    if not api_key:
        click.echo(click.style(
            "Error: No API key found. Set OPENWEATHERMAP_API_KEY environment variable.",
            fg='red'
        ))
        return
    
    config = load_config()
    fetcher = WeatherFetcher(api_key=api_key, units=config.units)
    
    click.echo(f"Fetching weather for {location}...")
    
    try:
        weather_data = fetcher.fetch(location)
        
        click.echo("")
        click.echo(click.style(f"Weather in {weather_data.location}", fg='cyan', bold=True))
        click.echo("")
        click.echo(f"  Condition:    {weather_data.condition}")
        click.echo(f"  Description:  {weather_data.description}")
        click.echo(f"  Temperature:  {weather_data.temperature}°")
        click.echo(f"  Humidity:     {weather_data.humidity}%")
        click.echo(f"  Wind:         {weather_data.wind_speed} m/s ({weather_data.wind_direction}°)")
        click.echo(f"  Visibility:   {weather_data.visibility}m")
        click.echo(f"  Clouds:       {weather_data.clouds}%")
        click.echo(f"  Time of day:  {'Night' if weather_data.is_night else 'Day'}")
        click.echo("")
        click.echo(f"  Animation intensity: {weather_data.get_intensity():.2f}")
        
        # Show which engine would be used
        engine_class = get_engine_for_condition(weather_data.condition)
        click.echo(f"  Weather engine: {engine_class.__name__}")
        
    except WeatherFetchError as e:
        click.echo(click.style(f"Error: {e}", fg='red'))


# ============================================================================
# Daemon Commands
# ============================================================================

@cli.group()
def daemon():
    """
    Manage background daemon mode with tmux.
    
    Daemon mode allows SoraCLI to run persistently in a tmux session,
    providing a split-screen view with weather animations in one pane
    and your shell in another.
    
    Requires tmux to be installed: https://github.com/tmux/tmux
    """
    pass


@daemon.command('start')
@click.option('--location', '-l', default=None,
              help='Weather location (uses config default if not specified)')
@click.option('--theme', '-t', default=None,
              help='Visual theme (uses config default if not specified)')
@click.option('--panel-size', '-p', default=5, type=int,
              help='Height of weather panel in lines (default: 5)')
@click.option('--position', default='top',
              type=click.Choice(['top', 'bottom']),
              help='Position of weather panel (default: top)')
@click.option('--transparency', default=0.85, type=float,
              help='Panel transparency 0.0-1.0 (default: 0.85)')
@click.option('--attach/--no-attach', default=True,
              help='Attach to session after starting (default: yes)')
def daemon_start(location: Optional[str], theme: Optional[str], 
                 panel_size: int, position: str, 
                 transparency: float, attach: bool):
    """
    Start daemon mode in a new tmux session.
    
    Creates a tmux session with a split layout: weather animations
    in one pane and your shell in another.
    
    Examples:
    
        soracli daemon start
        
        soracli daemon start --location "Tokyo" --theme cyberpunk
        
        soracli daemon start --panel-size 20 --position bottom
    """
    manager = DaemonManager()
    
    # Check tmux availability
    if not manager.check_tmux():
        click.echo(click.style("Error: tmux is not installed.", fg='red'))
        click.echo("\nInstall tmux to use daemon mode:")
        click.echo("  Ubuntu/Debian: sudo apt install tmux")
        click.echo("  macOS:         brew install tmux")
        click.echo("  Windows:       Use WSL or install via Chocolatey")
        return
    
    # Check if already running
    if manager.is_running():
        click.echo(click.style("SoraCLI daemon is already running.", fg='yellow'))
        click.echo("Use 'soracli daemon attach' to connect or 'soracli daemon stop' to stop.")
        return
    
    # Load config for defaults
    config = load_config()
    api_key = get_api_key()
    
    final_location = location or config.location
    final_theme = theme or config.theme
    
    click.echo(click.style("Starting SoraCLI daemon...", fg='cyan', bold=True))
    click.echo(f"  Location:    {final_location}")
    click.echo(f"  Theme:       {final_theme}")
    click.echo(f"  Panel size:  {panel_size} lines")
    click.echo(f"  Position:    {position}")
    click.echo("")
    
    success = manager.start(
        location=final_location,
        theme=final_theme,
        api_key=api_key,
        panel_height=panel_size,
        panel_position=position,
        transparency=transparency,
    )
    
    if success:
        click.echo(click.style("✓ Daemon started successfully!", fg='green', bold=True))
        
        if attach:
            click.echo("\nAttaching to session (detach with Ctrl+B, D)...")
            import time
            time.sleep(1)
            manager.attach()
    else:
        click.echo(click.style("✗ Failed to start daemon.", fg='red'))


@daemon.command('stop')
def daemon_stop():
    """
    Stop the running daemon.
    
    Terminates the tmux session and all weather animations.
    """
    manager = DaemonManager()
    
    if not manager.is_running():
        click.echo(click.style("SoraCLI daemon is not running.", fg='yellow'))
        return
    
    click.echo("Stopping SoraCLI daemon...")
    
    if manager.stop():
        click.echo(click.style("✓ Daemon stopped.", fg='green'))
    else:
        click.echo(click.style("✗ Failed to stop daemon.", fg='red'))


@daemon.command('status')
def daemon_status():
    """
    Show daemon status.
    
    Displays whether the daemon is running and session details.
    """
    manager = DaemonManager()
    status = manager.get_status()
    
    click.echo(click.style("SoraCLI Daemon Status", fg='cyan', bold=True))
    click.echo("")
    
    if status['running']:
        click.echo(f"  Status:      {click.style('Running', fg='green', bold=True)}")
        click.echo(f"  Session:     {status.get('session_name', 'N/A')}")
        click.echo(f"  Started:     {status.get('start_time', 'N/A')}")
        click.echo(f"  Location:    {status.get('location', 'N/A')}")
        click.echo(f"  Theme:       {status.get('theme', 'N/A')}")
        click.echo("")
        click.echo("  Commands:")
        click.echo("    soracli daemon attach  - Connect to session")
        click.echo("    soracli daemon stop    - Stop daemon")
    else:
        click.echo(f"  Status:      {click.style('Not running', fg='yellow')}")
        click.echo("")
        click.echo("  Start daemon with: soracli daemon start")


@daemon.command('attach')
def daemon_attach():
    """
    Attach to running daemon session.
    
    Reconnects to the tmux session. Detach with Ctrl+B, D.
    """
    manager = DaemonManager()
    
    if not manager.is_running():
        click.echo(click.style("SoraCLI daemon is not running.", fg='yellow'))
        click.echo("Start it with: soracli daemon start")
        return
    
    click.echo("Attaching to SoraCLI session (detach with Ctrl+B, D)...")
    manager.attach()


@daemon.command('setup')
@click.option('--autostart/--no-autostart', default=True,
              help='Configure automatic startup')
@click.option('--shell', default=None,
              help='Shell profile to modify (.bashrc, .zshrc, etc.)')
def daemon_setup(autostart: bool, shell: Optional[str]):
    """
    Setup daemon for automatic startup.
    
    Generates startup scripts and optionally adds to shell profile.
    
    Examples:
    
        soracli daemon setup
        
        soracli daemon setup --shell .zshrc
    """
    manager = DaemonManager()
    
    click.echo(click.style("SoraCLI Daemon Setup", fg='cyan', bold=True))
    click.echo("")
    
    # Generate startup script
    script_path = manager.generate_startup_script()
    
    if script_path:
        click.echo(click.style("✓ Startup script generated:", fg='green'))
        click.echo(f"  {script_path}")
        click.echo("")
        
        if autostart and shell:
            # Add to shell profile
            from pathlib import Path
            shell_path = Path.home() / shell
            
            if shell_path.exists():
                startup_line = f"\n# SoraCLI auto-start\n[ -n \"$PS1\" ] && command -v tmux &>/dev/null && {script_path}\n"
                
                with open(shell_path, 'a') as f:
                    f.write(startup_line)
                
                click.echo(click.style(f"✓ Added to {shell}", fg='green'))
            else:
                click.echo(click.style(f"Warning: {shell} not found", fg='yellow'))
        else:
            click.echo("To auto-start, add this to your shell profile:")
            click.echo(f"  {script_path}")
    
    # Generate systemd service (Linux only)
    if sys.platform.startswith('linux'):
        click.echo("")
        service_path = manager.generate_systemd_service()
        if service_path:
            click.echo(click.style("✓ Systemd service generated:", fg='green'))
            click.echo(f"  {service_path}")
            click.echo("")
            click.echo("To enable as a user service:")
            click.echo(f"  cp {service_path} ~/.config/systemd/user/")
            click.echo("  systemctl --user enable soracli")
            click.echo("  systemctl --user start soracli")


def main():
    """Main entry point."""
    cli()


if __name__ == '__main__':
    main()
