"""Test daemon process management."""

import pytest
import os
import json
from unittest.mock import Mock, patch, MagicMock
from datetime import datetime
from mcpbundles.daemon import get_status, get_pid_file


def test_get_pid_file(tmp_path, monkeypatch):
    """Test PID file path."""
    test_dir = tmp_path / ".mcpbundles"
    monkeypatch.setattr("mcpbundles.daemon.CONFIG_DIR", test_dir)
    monkeypatch.setattr("mcpbundles.daemon.PID_FILE", test_dir / "tunnel.pid")
    
    pid_file = get_pid_file()
    assert pid_file == test_dir / "tunnel.pid"


def test_get_status_not_running(tmp_path, monkeypatch):
    """Test status when daemon is not running."""
    test_dir = tmp_path / ".mcpbundles"
    monkeypatch.setattr("mcpbundles.daemon.PID_FILE", test_dir / "tunnel.pid")
    
    status = get_status()
    assert status["running"] is False


def test_get_status_running(tmp_path, monkeypatch):
    """Test status when daemon is running."""
    test_dir = tmp_path / ".mcpbundles"
    test_dir.mkdir(parents=True)
    
    pid_file = test_dir / "tunnel.pid"
    status_file = test_dir / "status.json"
    
    monkeypatch.setattr("mcpbundles.daemon.PID_FILE", pid_file)
    monkeypatch.setattr("mcpbundles.daemon.STATUS_FILE", status_file)
    
    # Write PID file with current process (so it's "running")
    current_pid = os.getpid()
    pid_file.write_text(str(current_pid))
    
    # Write status file
    started_at = datetime.now()
    status_data = {
        "running": True,
        "pid": current_pid,
        "started_at": started_at.isoformat(),
        "tunnel_status": "connected"
    }
    with open(status_file, "w") as f:
        json.dump(status_data, f)
    
    status = get_status()
    assert status["running"] is True
    assert status["pid"] == current_pid
    assert "uptime" in status


def test_get_status_stale_pid(tmp_path, monkeypatch):
    """Test status with stale PID file."""
    test_dir = tmp_path / ".mcpbundles"
    test_dir.mkdir(parents=True)
    
    pid_file = test_dir / "tunnel.pid"
    monkeypatch.setattr("mcpbundles.daemon.PID_FILE", pid_file)
    
    # Write PID of non-existent process
    pid_file.write_text("999999")
    
    status = get_status()
    assert status["running"] is False
    # Stale PID file should be cleaned up
    assert not pid_file.exists()


def test_stop_daemon_not_running(tmp_path, monkeypatch):
    """Test stopping daemon when not running."""
    from mcpbundles.daemon import stop_daemon
    
    test_dir = tmp_path / ".mcpbundles"
    monkeypatch.setattr("mcpbundles.daemon.PID_FILE", test_dir / "tunnel.pid")
    monkeypatch.setattr("mcpbundles.daemon.STATUS_FILE", test_dir / "status.json")
    
    # Should not raise error
    stop_daemon()


def test_cleanup_and_exit(tmp_path, monkeypatch):
    """Test cleanup function."""
    from mcpbundles.daemon import cleanup_and_exit
    
    test_dir = tmp_path / ".mcpbundles"
    test_dir.mkdir(parents=True)
    
    pid_file = test_dir / "tunnel.pid"
    status_file = test_dir / "status.json"
    
    monkeypatch.setattr("mcpbundles.daemon.PID_FILE", pid_file)
    monkeypatch.setattr("mcpbundles.daemon.STATUS_FILE", status_file)
    
    # Create files
    pid_file.write_text("12345")
    status_file.write_text("{}")
    
    # Cleanup should remove files
    with pytest.raises(SystemExit):
        cleanup_and_exit()
    
    assert not pid_file.exists()
    assert not status_file.exists()


@pytest.mark.asyncio
async def test_run_tunnel_token_refresh_failed(monkeypatch):
    """Test tunnel run when token refresh fails."""
    from mcpbundles.daemon import run_tunnel
    from mcpbundles import daemon
    
    token_data = {
        "access_token": "test_token",
        "refresh_token": "test_refresh",
        "expires_at": "2024-01-01T00:00:00"
    }
    
    with patch('mcpbundles.daemon.refresh_token_if_needed', return_value=None):
        # Mock the async cleanup function
        async def mock_cleanup():
            raise SystemExit()
        
        daemon.cleanup_and_exit_async = mock_cleanup
        
        with pytest.raises(SystemExit):
            await run_tunnel(token_data)


def test_setup_logging(tmp_path, monkeypatch):
    """Test logging setup."""
    from mcpbundles.daemon import setup_logging
    
    test_dir = tmp_path / ".mcpbundles"
    log_file = test_dir / "tunnel.log"
    
    monkeypatch.setattr("mcpbundles.daemon.CONFIG_DIR", test_dir)
    monkeypatch.setattr("mcpbundles.daemon.LOG_FILE", log_file)
    
    setup_logging()
    
    # Config dir should be created
    assert test_dir.exists()


def test_stop_daemon_with_running_process(tmp_path, monkeypatch):
    """Test stopping a running daemon."""
    from mcpbundles.daemon import stop_daemon
    
    test_dir = tmp_path / ".mcpbundles"
    test_dir.mkdir(parents=True)
    
    pid_file = test_dir / "tunnel.pid"
    status_file = test_dir / "status.json"
    
    monkeypatch.setattr("mcpbundles.daemon.PID_FILE", pid_file)
    monkeypatch.setattr("mcpbundles.daemon.STATUS_FILE", status_file)
    
    # Write a non-existent PID (daemon not actually running)
    pid_file.write_text("999999")
    status_file.write_text("{}")
    
    with patch('os.kill') as mock_kill:
        mock_kill.side_effect = ProcessLookupError()  # Process doesn't exist
        stop_daemon()
    
    # Files should be cleaned up
    assert not pid_file.exists()


def test_stop_daemon_with_valid_pid(tmp_path, monkeypatch):
    """Test stopping daemon with valid PID."""
    from mcpbundles.daemon import stop_daemon
    import signal
    
    test_dir = tmp_path / ".mcpbundles"
    test_dir.mkdir(parents=True)
    
    pid_file = test_dir / "tunnel.pid"
    status_file = test_dir / "status.json"
    
    monkeypatch.setattr("mcpbundles.daemon.PID_FILE", pid_file)
    monkeypatch.setattr("mcpbundles.daemon.STATUS_FILE", status_file)
    
    pid_file.write_text("12345")
    status_file.write_text("{}")
    
    kill_calls = []
    
    def mock_kill(pid, sig):
        kill_calls.append((pid, sig))
        if sig == 0 and len(kill_calls) < 3:
            # First few checks, process exists
            return
        # Eventually raise to simulate process died
        raise ProcessLookupError()
    
    with patch('os.kill', side_effect=mock_kill):
        with patch('time.sleep'):
            stop_daemon()
    
    # Should have called kill with SIGTERM
    assert any(sig == signal.SIGTERM for _, sig in kill_calls)
    assert not pid_file.exists()


def test_get_status_with_malformed_pid(tmp_path, monkeypatch):
    """Test status with malformed PID file."""
    test_dir = tmp_path / ".mcpbundles"
    test_dir.mkdir(parents=True)
    
    pid_file = test_dir / "tunnel.pid"
    monkeypatch.setattr("mcpbundles.daemon.PID_FILE", pid_file)
    
    # Write invalid PID
    pid_file.write_text("not_a_number")
    
    status = get_status()
    assert status["running"] is False
    # Invalid PID file should be cleaned up
    assert not pid_file.exists()


def test_get_status_no_status_file(tmp_path, monkeypatch):
    """Test status when PID exists but no status file."""
    test_dir = tmp_path / ".mcpbundles"
    test_dir.mkdir(parents=True)
    
    pid_file = test_dir / "tunnel.pid"
    status_file = test_dir / "status.json"
    monkeypatch.setattr("mcpbundles.daemon.PID_FILE", pid_file)
    monkeypatch.setattr("mcpbundles.daemon.STATUS_FILE", status_file)
    
    # Write current PID (so it's "running")
    current_pid = os.getpid()
    pid_file.write_text(str(current_pid))
    
    status = get_status()
    assert status["running"] is True
    assert status["pid"] == current_pid

