"""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.config import CONFIG_DIR, save_status
from mcpbundles.tunnel import TunnelClient
from mcpbundles.auth import refresh_token_if_needed
from mcpbundles.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()
    logger.info(
        "Tunnel init: env=%s token_has_refresh=%s token_has_client=%s",
        os.getenv("ENVIRONMENT", "production"),
        bool(token_data.get("refresh_token")),
        bool(token_data.get("client_id")),
    )
    
    # Refresh token if needed
    token_data = await refresh_token_if_needed(token_data)
    if not token_data:
        logger.error("Token refresh failed. Run 'mcpbundles login' again.")
        cleanup_and_exit()
    logger.info("Token refresh ok; expires_at=%s", token_data.get("expires_at"))
    
    # 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)
    logger.info("Service manager initialized: services=%s", list(service_config.keys()))
    
    # 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}

