from __future__ import annotations

import os
import sys
import subprocess
import time
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest

# Add src to sys.path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))

from config import Config
from command_runner import (
    update_command_state,
    load_command_states,
    recover_interrupted_commands,
)
from actions.power import handle_server_reboot


def _fresh_config(tmp: str) -> Config:
    os.environ["WOLFPANEL_DEV_HOME"] = tmp
    os.environ["WOLFPANEL_API_MOCK"] = "1"
    import importlib
    import config as config_module
    importlib.reload(config_module)
    return config_module.load_config()


def _registered_config(tmp: str) -> Config:
    cfg = _fresh_config(tmp)
    from identity import ensure_dirs, store_credentials
    ensure_dirs(cfg)
    store_credentials(cfg, "server_123", "wp_agent_dev_token")
    return _fresh_config(tmp)


@patch("subprocess.Popen")
@patch("time.sleep")
@patch("os.sync", create=True)
def test_reboot_action_systemctl_success(mock_sync, mock_sleep, mock_popen):
    mock_popen.return_value = MagicMock()
    
    res = handle_server_reboot({})
    assert res["success"] is True
    assert "Reboot command sent" in res["message"]
    
    mock_sync.assert_called_once()
    mock_sleep.assert_any_call(2)
    mock_sleep.assert_any_call(30)
    mock_popen.assert_called_once_with(
        ["systemctl", "reboot"],
        shell=False,
        env={"LC_ALL": "C"}
    )


@patch("subprocess.Popen")
@patch("time.sleep")
@patch("os.sync", create=True)
def test_reboot_action_fallback_success(mock_sync, mock_sleep, mock_popen):
    # First call raises FileNotFoundError, second call succeeds
    mock_popen.side_effect = [FileNotFoundError("systemctl not found"), MagicMock()]
    
    res = handle_server_reboot({})
    assert res["success"] is True
    
    mock_sync.assert_called_once()
    mock_sleep.assert_any_call(2)
    mock_sleep.assert_any_call(30)
    
    # Assert popen was called twice: first systemctl reboot, then reboot
    assert mock_popen.call_count == 2
    mock_popen.assert_any_call(["systemctl", "reboot"], shell=False, env={"LC_ALL": "C"})
    mock_popen.assert_any_call(["reboot"], shell=False, env={"LC_ALL": "C"})


@patch("subprocess.Popen")
@patch("time.sleep")
@patch("os.sync", create=True)
def test_reboot_action_failure(mock_sync, mock_sleep, mock_popen):
    # Both systemctl and reboot fail
    mock_popen.side_effect = [FileNotFoundError("systemctl not found"), OSError("reboot failed")]
    
    res = handle_server_reboot({})
    assert res["success"] is False
    assert "Failed to execute reboot command" in res["error"]


def test_reboot_recovery_real_reboot_succeeded(tmp_path):
    cfg = _registered_config(str(tmp_path))
    
    # 401: pre_reboot_boot_time is 500
    update_command_state(
        cfg.var_dir, 
        "401", 
        status="running", 
        command_type="server.reboot", 
        pre_reboot_boot_time=500.0, 
        updated_at="old_time"
    )
    
    # 402: Normal command
    update_command_state(
        cfg.var_dir, 
        "402", 
        status="running", 
        command_type="some_action", 
        updated_at="old_time"
    )

    api_client = MagicMock()
    
    # Patch get_system_boot_time to return 600 (> 500)
    with patch("command_runner.load_agent_token", return_value="wp_agent_dev_token"), \
         patch("command_runner.get_system_boot_time", return_value=600.0):
         
        recover_interrupted_commands(cfg, api_client)
        
        states = load_command_states(cfg.var_dir)
        
        # Verify 401 succeeded and 402 failed
        assert states["401"]["status"] == "success"
        assert states["402"]["status"] == "failed"
        
        api_client.post_command_result.assert_any_call(
            401,
            {
                "status": "succeeded",
                "result": {"success": True, "message": "Server rebooted successfully"}
            },
            "wp_agent_dev_token"
        )


def test_reboot_recovery_agent_restart_failed(tmp_path):
    cfg = _registered_config(str(tmp_path))
    
    # 403: pre_reboot_boot_time is 500
    update_command_state(
        cfg.var_dir, 
        "403", 
        status="running", 
        command_type="server.reboot", 
        pre_reboot_boot_time=500.0, 
        updated_at="old_time"
    )

    api_client = MagicMock()
    
    # Patch get_system_boot_time to return 500 (<= 500, did not reboot)
    with patch("command_runner.load_agent_token", return_value="wp_agent_dev_token"), \
         patch("command_runner.get_system_boot_time", return_value=500.0):
         
        recover_interrupted_commands(cfg, api_client)
        
        states = load_command_states(cfg.var_dir)
        
        # Verify 403 failed
        assert states["403"]["status"] == "failed"
        
        api_client.post_command_result.assert_any_call(
            403,
            {
                "status": "failed",
                "result": {"error": "Server did not reboot or boot time could not be verified"}
            },
            "wp_agent_dev_token"
        )


def test_reboot_recovery_no_boot_time_failed(tmp_path):
    cfg = _registered_config(str(tmp_path))
    
    # 404: pre_reboot_boot_time is 500
    update_command_state(
        cfg.var_dir, 
        "404", 
        status="running", 
        command_type="server.reboot", 
        pre_reboot_boot_time=500.0, 
        updated_at="old_time"
    )

    api_client = MagicMock()
    
    # Patch get_system_boot_time to return None
    with patch("command_runner.load_agent_token", return_value="wp_agent_dev_token"), \
         patch("command_runner.get_system_boot_time", return_value=None):
         
        recover_interrupted_commands(cfg, api_client)
        
        states = load_command_states(cfg.var_dir)
        
        # Verify 404 failed
        assert states["404"]["status"] == "failed"
        
        api_client.post_command_result.assert_any_call(
            404,
            {
                "status": "failed",
                "result": {"error": "Server did not reboot or boot time could not be verified"}
            },
            "wp_agent_dev_token"
        )

