from __future__ import annotations

import os
from unittest.mock import patch, MagicMock
import pytest

import actions.backup
from actions.backup import (
    handle_backup_create,
    handle_backup_delete,
    handle_backup_list,
    handle_backup_restore,
)


@pytest.fixture(autouse=True)
def setup_temp_backup_dir(tmp_path):
    """Override BACKUP_BASE_DIR with a temporary folder for isolated tests."""
    original_base = actions.backup.BACKUP_BASE_DIR
    actions.backup.BACKUP_BASE_DIR = str(tmp_path)
    
    # Set DEV_HOME and WOLFPANEL_DEV_HOME
    original_dev_home = os.environ.get("WOLFPANEL_DEV_HOME")
    os.environ["WOLFPANEL_DEV_HOME"] = str(tmp_path)
    original_backup_dev_home = actions.backup.DEV_HOME
    actions.backup.DEV_HOME = str(tmp_path)
    
    yield tmp_path
    
    actions.backup.BACKUP_BASE_DIR = original_base
    actions.backup.DEV_HOME = original_backup_dev_home
    if original_dev_home:
        os.environ["WOLFPANEL_DEV_HOME"] = original_dev_home
    else:
        os.environ.pop("WOLFPANEL_DEV_HOME", None)


def test_backup_create_invalid_domain(tmp_path):
    docroot = os.path.join(str(tmp_path), "var", "www", "test")
    res = handle_backup_create({"domain": "invalid/domain", "docroot": docroot})
    assert res["success"] is False
    assert "Invalid domain name format" in res["error"]


@patch("subprocess.run")
def test_backup_create_success(mock_run, tmp_path):
    domain = "example.com"
    docroot = os.path.join(str(tmp_path), "var", "www", "docroot")
    os.makedirs(docroot, exist_ok=True)
    
    # Mocking os.path.getsize to return fake size
    with patch("os.path.getsize", return_value=12345):
        res = handle_backup_create({
            "domain": domain,
            "docroot": docroot,
            "include_files": True,
            "include_db": False
        })
        assert res["success"] is True
        assert res["size_bytes"] == 12345
        assert res["filename"].startswith(f"{domain}_")
        assert res["filename"].endswith(".tar.gz")
        assert mock_run.call_count == 2 # 1 for docroot compression, 1 for final zip


def test_backup_delete_path_traversal(tmp_path):
    outside_file = os.path.join(str(tmp_path), "..", "outside.tar.gz")
    res = handle_backup_delete({"backup_path": outside_file})
    assert res["success"] is False
    assert "outside of backup base directory" in res["error"]


def test_backup_delete_success(tmp_path):
    domain_dir = os.path.join(str(tmp_path), "example.com")
    os.makedirs(domain_dir, exist_ok=True)
    backup_file = os.path.join(domain_dir, "example.com_20260101_120000.tar.gz")
    with open(backup_file, "w") as f:
        f.write("mock backup content")
        
    assert os.path.exists(backup_file)
    res = handle_backup_delete({"backup_path": backup_file})
    assert res["success"] is True
    assert res["deleted"] is True
    assert not os.path.exists(backup_file)


def test_backup_list(tmp_path):
    domain = "example.com"
    domain_dir = os.path.join(str(tmp_path), domain)
    os.makedirs(domain_dir, exist_ok=True)
    
    backup_file = os.path.join(domain_dir, "example.com_20260101_120000.tar.gz")
    with open(backup_file, "w") as f:
        f.write("mock backup content")

    res = handle_backup_list({"domain": domain})
    assert res["success"] is True
    assert len(res["backups"]) == 1
    assert res["backups"][0]["filename"] == "example.com_20260101_120000.tar.gz"
    assert res["backups"][0]["path"] == backup_file


@patch("subprocess.run")
def test_backup_restore_success(mock_run, tmp_path):
    domain = "example.com"
    domain_dir = os.path.join(str(tmp_path), domain)
    os.makedirs(domain_dir, exist_ok=True)
    backup_file = os.path.join(domain_dir, "example.com_20260101_120000.tar.gz")
    with open(backup_file, "w") as f:
        f.write("mock backup content")

    docroot = os.path.join(str(tmp_path), "var", "www", "docroot")
    os.makedirs(docroot, exist_ok=True)

    # Patch os.path.exists inside temp restore directory
    with patch("os.path.exists", return_value=True):
        res = handle_backup_restore({
            "backup_path": backup_file,
            "docroot": docroot,
            "restore_files": True,
            "restore_db": False
        })
        assert res["success"] is True
        assert res["restored"] is True
        assert mock_run.call_count >= 2 # 1 extract main backup, 1 extract files.tar.gz
