#!/usr/bin/env python3
"""
Test utilities for deb-bootc-image-builder.

This module provides common utilities for testing, including:
- Test data generation
- Mock objects
- Helper functions
"""

import os
import tempfile
import shutil
import json
import yaml
from typing import Dict, List, Any, Optional
import logging

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class TestDataGenerator:
    """Generate test data for deb-bootc-image-builder tests."""

    @staticmethod
    def create_debian_package_list() -> List[str]:
        """Create a list of Debian packages for testing."""
        return [
            "linux-image-amd64",
            "linux-headers-amd64",
            "systemd",
            "systemd-sysv",
            "dbus",
            "ostree",
            "grub-efi-amd64",
            "initramfs-tools",
            "util-linux",
            "parted",
            "e2fsprogs",
            "dosfstools",
            "efibootmgr",
            "sudo",
            "network-manager",
            "curl",
            "wget",
            "nano",
            "vim-tiny"
        ]

    @staticmethod
    def create_debian_repository_config() -> Dict[str, Any]:
        """Create Debian repository configuration for testing."""
        return {
            "release": "trixie",
            "arch": "amd64",
            "repos": [
                {
                    "name": "debian",
                    "baseurls": ["http://deb.debian.org/debian"],
                    "enabled": True
                },
                {
                    "name": "debian-security",
                    "baseurls": ["http://deb.debian.org/debian-security"],
                    "enabled": True
                }
            ]
        }

    @staticmethod
    def create_ostree_config() -> Dict[str, Any]:
        """Create OSTree configuration for testing."""
        return {
            "mode": "bare-user-only",
            "repo": "/var/lib/ostree/repo",
            "bootable": True,
            "deployment": {
                "osname": "debian",
                "ref": "debian/trixie/amd64"
            }
        }

    @staticmethod
    def create_grub_config() -> Dict[str, Any]:
        """Create GRUB configuration for testing."""
        return {
            "uefi": True,
            "secure_boot": False,
            "timeout": 5,
            "default_entry": 0,
            "kernel_path": "/boot/vmlinuz",
            "initramfs_path": "/boot/initrd.img"
        }

    @staticmethod
    def create_filesystem_config() -> Dict[str, Any]:
        """Create filesystem configuration for testing."""
        return {
            "rootfs_type": "ext4",
            "ostree_integration": True,
            "home_symlink": True,
            "users": [
                {
                    "name": "debian-user",
                    "password": "debian",
                    "groups": ["sudo", "users"]
                }
            ],
            "permissions": {
                "/etc/ostree": "755",
                "/var/lib/ostree": "755"
            }
        }


class MockContainerImage:
    """Mock container image for testing."""

    def __init__(self, labels: Optional[Dict[str, str]] = None):
        """Initialize mock container image."""
        self.labels = labels or {
            "com.debian.bootc": "true",
            "ostree.bootable": "true",
            "org.debian.version": "13",
            "version": "1.0"
        }
        self.ref = "debian:trixie"
        self.arch = "amd64"
        self.os = "linux"

    def get_labels(self) -> Dict[str, str]:
        """Get image labels."""
        return self.labels

    def get_ref(self) -> str:
        """Get image reference."""
        return self.ref

    def get_arch(self) -> str:
        """Get image architecture."""
        return self.arch

    def get_os(self) -> str:
        """Get image operating system."""
        return self.os


class MockOSTreeRepo:
    """Mock OSTree repository for testing."""

    def __init__(self, path: str):
        """Initialize mock OSTree repository."""
        self.path = path
        self.refs = ["debian/trixie/amd64"]
        self.deployments = []

    def list_refs(self) -> List[str]:
        """List repository references."""
        return self.refs

    def list_deployments(self) -> List[Dict[str, Any]]:
        """List repository deployments."""
        return self.deployments

    def get_deployment_info(self, ref: str) -> Optional[Dict[str, Any]]:
        """Get deployment information."""
        if ref in self.refs:
            return {
                "ref": ref,
                "osname": "debian",
                "bootable": True,
                "version": "13"
            }
        return None


class TestEnvironment:
    """Test environment setup and teardown."""

    def __init__(self, work_dir: str):
        """Initialize test environment."""
        self.work_dir = work_dir
        self.original_cwd = os.getcwd()

    def setup(self):
        """Set up test environment."""
        os.chdir(self.work_dir)
        
        # Create basic directory structure
        dirs = [
            "etc", "var", "home", "boot", "usr",
            "usr/bin", "usr/lib", "usr/sbin",
            "var/lib", "var/lib/ostree", "var/home"
        ]
        
        for dir_path in dirs:
            full_path = os.path.join(self.work_dir, dir_path)
            os.makedirs(full_path, exist_ok=True)
        
        # Create /home -> /var/home symlink
        var_home = os.path.join(self.work_dir, "var", "home")
        home_link = os.path.join(self.work_dir, "home")
        if os.path.exists(home_link):
            os.remove(home_link)
        os.symlink(var_home, home_link)
        
        logger.info(f"Test environment set up in {self.work_dir}")

    def teardown(self):
        """Tear down test environment."""
        os.chdir(self.original_cwd)
        logger.info("Test environment torn down")

    def create_test_file(self, path: str, content: str = ""):
        """Create a test file with specified content."""
        full_path = os.path.join(self.work_dir, path.lstrip("/"))
        os.makedirs(os.path.dirname(full_path), exist_ok=True)
        
        with open(full_path, 'w') as f:
            f.write(content)
        
        return full_path

    def create_test_directory(self, path: str):
        """Create a test directory."""
        full_path = os.path.join(self.work_dir, path.lstrip("/"))
        os.makedirs(full_path, exist_ok=True)
        return full_path


def create_temp_manifest(manifest_data: Dict[str, Any]) -> str:
    """Create a temporary manifest file for testing."""
    temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False)
    
    try:
        json.dump(manifest_data, temp_file, indent=2)
        temp_file.close()
        return temp_file.name
    except Exception as e:
        temp_file.close()
        os.unlink(temp_file.name)
        raise e


def cleanup_temp_files(*file_paths: str):
    """Clean up temporary files."""
    for file_path in file_paths:
        try:
            if os.path.exists(file_path):
                os.unlink(file_path)
        except Exception as e:
            logger.warning(f"Failed to clean up {file_path}: {e}")


def assert_filesystem_structure(work_dir: str, expected_dirs: List[str]):
    """Assert that expected filesystem structure exists."""
    for expected_dir in expected_dirs:
        full_path = os.path.join(work_dir, expected_dir.lstrip("/"))
        assert os.path.exists(full_path), f"Directory {expected_dir} not found"
        assert os.path.isdir(full_path), f"{expected_dir} is not a directory"


def assert_file_contents(file_path: str, expected_content: str):
    """Assert that file contains expected content."""
    assert os.path.exists(file_path), f"File {file_path} not found"
    
    with open(file_path, 'r') as f:
        actual_content = f.read()
    
    assert actual_content == expected_content, \
        f"File content mismatch in {file_path}"


def create_mock_context():
    """Create a mock osbuild context for testing."""
    context = Mock()
    context.root = "/tmp/mock-root"
    
    def mock_run(cmd):
        mock_result = Mock()
        mock_result.returncode = 0
        mock_result.stdout = b"mock output"
        mock_result.stderr = b""
        return mock_result
    
    context.run = mock_run
    return context
