"""File utility functions for Resource Hacker.

This module provides various file handling utilities including
file validation, path operations, and backup functionality.
"""

from __future__ import annotations

import logging
import shutil
from pathlib import Path

try:
    from ..ui.config import app_config
except ValueError:
    # Fallback for CLI-only usage
    app_config = None

logger = logging.getLogger(__name__)

# Common file extensions
PE_EXTENSIONS = {".exe", ".dll", ".sys", ".ocx", ".scr", ".cpl", ".ax", ".acm", ".drv"}
IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".bmp", ".gif", ".tiff", ".ico"}
RESOURCE_EXTENSIONS = {".res", ".rc", ".xml", ".manifest"}


class FileUtils:
    """Utility class for file operations."""

    @staticmethod
    def is_pe_file(file_path: Path) -> bool:
        """Check if a file is a valid PE file.

        Args:
            file_path: Path to the file to check

        Returns
        -------
            bool: True if file is a valid PE file, False otherwise
        """
        if not file_path.exists() or not file_path.is_file():
            return False

        # Check file extension first
        if file_path.suffix.lower() not in PE_EXTENSIONS:
            return False

        # Check file header for PE signature
        try:
            with open(file_path, "rb") as f:
                # Read enough bytes to check PE signature
                header = f.read(64)
                if len(header) < 64:
                    return False

                # Check for MZ signature (first 2 bytes)
                if header[:2] != b"MZ":
                    return False

                # Find PE signature offset (at 0x3C)
                pe_offset = int.from_bytes(header[0x3C:0x40], "little")
                if pe_offset + 4 > len(header):
                    # Need to read more data
                    f.seek(pe_offset)
                    pe_signature = f.read(4)
                else:
                    pe_signature = header[pe_offset : pe_offset + 4]

                # Check for PE signature
                return pe_signature == b"PE\x00\x00"

        except Exception as e:
            logger.debug(f"Error checking PE file {file_path}: {e}")
            return False

    @staticmethod
    def is_image_file(file_path: Path) -> bool:
        """Check if a file is a valid image file.

        Args:
            file_path: Path to the file to check

        Returns
        -------
            bool: True if file is a valid image file, False otherwise
        """
        if not file_path.exists() or not file_path.is_file():
            return False

        if file_path.suffix.lower() not in IMAGE_EXTENSIONS:
            return False

        # Try to open with PIL to validate image format
        try:
            from PIL import Image

            with Image.open(file_path) as img:
                img.verify()
            return True
        except Exception:
            return False

    @staticmethod
    def get_file_info(file_path: Path) -> dict | None:
        """Get detailed information about a file.

        Args:
            file_path: Path to the file

        Returns
        -------
            Dictionary with file information, or None if file doesn't exist
        """
        if not file_path.exists():
            return None

        try:
            stat = file_path.stat()
            return {
                "name": file_path.name,
                "path": str(file_path),
                "size": stat.st_size,
                "size_formatted": FileUtils.format_file_size(stat.st_size),
                "modified": stat.st_mtime,
                "is_file": file_path.is_file(),
                "is_dir": file_path.is_dir(),
                "extension": file_path.suffix.lower(),
                "is_pe": FileUtils.is_pe_file(file_path) if file_path.is_file() else False,
                "is_image": FileUtils.is_image_file(file_path) if file_path.is_file() else False,
            }
        except Exception as e:
            logger.error(f"Error getting file info for {file_path}: {e}")
            return None

    @staticmethod
    def format_file_size(size_bytes: int) -> str:
        """Format file size in human-readable format.

        Args:
            size_bytes: Size in bytes

        Returns
        -------
            Formatted size string
        """
        if size_bytes == 0:
            return "0 B"

        size_names = ["B", "KB", "MB", "GB", "TB"]
        i = 0
        size = float(size_bytes)

        while size >= 1024.0 and i < len(size_names) - 1:
            size /= 1024.0
            i += 1

        return f"{size:.1f} {size_names[i]}"

    @staticmethod
    def create_backup(file_path: Path, backup_dir: Path | None = None) -> Path | None:
        """Create a backup of a file.

        Args:
            file_path: Path to the file to backup
            backup_dir: Directory for backup files (default: same directory as original)

        Returns
        -------
            Path to backup file, or None if backup failed
        """
        if not file_path.exists():
            logger.error(f"Cannot backup non-existent file: {file_path}")
            return None

        try:
            if backup_dir is None:
                backup_dir = file_path.parent

            backup_dir.mkdir(parents=True, exist_ok=True)

            # Create backup filename
            backup_name = f"{file_path.stem}_backup_{file_path.suffix}"
            backup_path = backup_dir / backup_name

            # Handle existing backup files
            counter = 1
            while backup_path.exists():
                backup_name = f"{file_path.stem}_backup_{counter}{file_path.suffix}"
                backup_path = backup_dir / backup_name
                counter += 1

            # Copy file
            shutil.copy2(file_path, backup_path)
            logger.info(f"Created backup: {backup_path}")
            return backup_path

        except Exception as e:
            logger.error(f"Failed to create backup of {file_path}: {e}")
            return None

    @staticmethod
    def get_recent_files(max_files: int = 10) -> list[Path]:
        """Get list of recently accessed PE files using global configuration.

        Args:
            max_files: Maximum number of recent files to return

        Returns
        -------
            List of recent file paths
        """
        try:
            recent_files = []
            for file_str in app_config.get_recent_files()[:max_files]:
                file_path = Path(file_str)
                if file_path.exists() and FileUtils.is_pe_file(file_path):
                    recent_files.append(file_path)
            return recent_files

        except Exception as e:
            logger.error(f"Error reading recent files: {e}")
            return []

    @staticmethod
    def add_recent_file(file_path: Path) -> None:
        """Add a file to the recent files list using global configuration.

        Args:
            file_path: Path to add to recent files
        """
        try:
            if not FileUtils.is_pe_file(file_path):
                return

            app_config.add_recent_file(str(file_path))
            logger.debug(f"Added to recent files: {file_path}")

        except Exception as e:
            logger.error(f"Error updating recent files: {e}")

    @staticmethod
    def validate_output_path(output_path: Path) -> bool:
        """Validate that an output path can be written to.

        Args:
            output_path: Path to validate

        Returns
        -------
            bool: True if path is valid for writing, False otherwise
        """
        try:
            # Check if parent directory exists and is writable
            parent_dir = output_path.parent
            if not parent_dir.exists():
                parent_dir.mkdir(parents=True, exist_ok=True)

            # Test write access
            test_file = parent_dir / ".write_test"
            test_file.write_text("test")
            test_file.unlink()

            return True

        except Exception as e:
            logger.error(f"Output path validation failed for {output_path}: {e}")
            return False

    @staticmethod
    def get_unique_filename(base_path: Path) -> Path:
        """Get a unique filename by adding numbers if file exists.

        Args:
            base_path: Base path to make unique

        Returns
        -------
            Unique path
        """
        if not base_path.exists():
            return base_path

        counter = 1
        while True:
            new_path = base_path.parent / f"{base_path.stem}_{counter}{base_path.suffix}"
            if not new_path.exists():
                return new_path
            counter += 1
