"""Daemon process management with forking and signal handling."""

import asyncio
import os
import sys
import signal
import logging
import json
from pathlib import Path
from datetime import datetime
from mcpbundles_proxy.config import CONFIG_DIR, save_status
from mcpbundles_proxy.tunnel import TunnelClient
from mcpbundles_proxy.auth import refresh_token_if_needed
from mcpbundles_proxy.service_manager import ServiceManager

# File paths
LOG_FILE = CONFIG_DIR / 'tunnel.log'
PID_FILE = CONFIG_DIR / 'tunnel.pid'
STATUS_FILE = CONFIG_DIR / 'status.json'


def setup_logging():
    """Setup file logging for daemon."""
    CONFIG_DIR.mkdir(exist_ok=True)
    
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s [%(levelname)s] %(message)s',
        handlers=[
            logging.FileHandler(LOG_FILE),
            logging.StreamHandler(sys.stdout)  # Also log to stdout initially
        ]
    )


def get_pid_file():
    """Get PID file path."""
    return PID_FILE


def start_daemon(token_data):
    """Start tunnel as background daemon."""
    setup_logging()
    logger = logging.getLogger(__name__)
    
    # Fork to background
    try:
        pid = os.fork()
        if pid > 0:
            # Parent process: write PID and exit
            CONFIG_DIR.mkdir(exist_ok=True)
            PID_FILE.write_text(str(pid))
            return
    except OSError as e:
        logger.error(f"Fork failed: {e}")
        raise SystemExit(1)
    
    # Child process: detach from parent
    os.setsid()
    os.chdir('/')
    
    # Close standard file descriptors
    sys.stdout.flush()
    sys.stderr.flush()
    
    # Reopen stdin/stdout/stderr to /dev/null
    with open('/dev/null', 'r') as f:
        os.dup2(f.fileno(), sys.stdin.fileno())
    with open('/dev/null', 'a+') as f:
        os.dup2(f.fileno(), sys.stdout.fileno())
    with open('/dev/null', 'a+') as f:
        os.dup2(f.fileno(), sys.stderr.fileno())
    
    # Reconfigure logging to file only
    for handler in logging.root.handlers[:]:
        logging.root.removeHandler(handler)
    
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s [%(levelname)s] %(message)s',
        handlers=[logging.FileHandler(LOG_FILE)],
        force=True
    )
    
    logger = logging.getLogger(__name__)
    logger.info("=== Tunnel daemon started ===")
    
    # Setup signal handlers
    def signal_handler(signum, frame):
        logger.info(f"Received signal {signum}, shutting down...")
        cleanup_and_exit()
    
    signal.signal(signal.SIGTERM, signal_handler)
    signal.signal(signal.SIGINT, signal_handler)
    
    # Run tunnel
    try:
        asyncio.run(run_tunnel(token_data))
    except Exception as e:
        logger.error(f"Fatal error: {e}", exc_info=True)
        cleanup_and_exit()


async def run_tunnel(token_data):
    """Main tunnel loop with token refresh and service management."""
    logger = logging.getLogger(__name__)
    start_time = datetime.now()
    
    # Refresh token if needed
    token_data = await refresh_token_if_needed(token_data)
    if not token_data:
        logger.error("Token refresh failed. Run 'mcpbundles-proxy login' again.")
        cleanup_and_exit()
    
    # Update status
    save_status({
        'running': True,
        'pid': os.getpid(),
        'started_at': start_time.isoformat(),
        'tunnel_status': 'connecting'
    })
    
    # Initialize service manager
    service_manager = ServiceManager()
    
    # Load service config from token data or use defaults
    service_config = token_data.get('service_config', {})
    await service_manager.initialize(service_config)
    
    # Create tunnel client with service manager
    client = TunnelClient(token_data['access_token'], service_manager=service_manager)
    
    # Token refresh task (every hour)
    async def refresh_loop():
        nonlocal token_data
        while True:
            await asyncio.sleep(3600)  # 1 hour
            logger.info("Refreshing token...")
            new_token = await refresh_token_if_needed(token_data)
            if new_token:
                token_data = new_token
                # Reconnect with new token
                await client.stop()
                await asyncio.sleep(1)
                client.token = new_token['access_token']
                client.running = True
                asyncio.create_task(client.connect())
            else:
                logger.error("Token refresh failed, shutting down")
                await service_manager.stop_all()
                cleanup_and_exit()
    
    # Start both tasks
    try:
        await asyncio.gather(
            client.connect(),
            refresh_loop()
        )
    except Exception as e:
        logger.error(f"Error in tunnel loop: {e}", exc_info=True)
        await service_manager.stop_all()
        cleanup_and_exit()


def stop_daemon():
    """Stop running daemon."""
    if not PID_FILE.exists():
        return
    
    try:
        pid = int(PID_FILE.read_text())
        os.kill(pid, signal.SIGTERM)
        
        # Wait for process to stop (max 5 seconds)
        import time
        for _ in range(50):
            try:
                os.kill(pid, 0)
                time.sleep(0.1)
            except ProcessLookupError:
                break
        
        # Force kill if still running
        try:
            os.kill(pid, signal.SIGKILL)
        except ProcessLookupError:
            pass
            
    except (ValueError, ProcessLookupError):
        pass
    finally:
        PID_FILE.unlink(missing_ok=True)
        STATUS_FILE.unlink(missing_ok=True)


def cleanup_and_exit():
    """Cleanup before exit."""
    PID_FILE.unlink(missing_ok=True)
    STATUS_FILE.unlink(missing_ok=True)
    sys.exit(0)


def get_status():
    """Get current daemon status."""
    if not PID_FILE.exists():
        return {'running': False}
    
    try:
        pid = int(PID_FILE.read_text())
        os.kill(pid, 0)  # Check if running
        
        # Load status file
        if STATUS_FILE.exists():
            with open(STATUS_FILE) as f:
                status = json.load(f)
                # Calculate uptime
                started_at = datetime.fromisoformat(status['started_at'])
                uptime = datetime.now() - started_at
                status['uptime'] = str(uptime).split('.')[0]  # Remove microseconds
                return status
        
        return {'running': True, 'pid': pid}
    except (ValueError, ProcessLookupError):
        PID_FILE.unlink(missing_ok=True)
        return {'running': False}

