"""Resource manager for Resource Hacker.

This module provides functionality to manage PE file resources including
extraction, replacement, and export/import operations.
"""

from __future__ import annotations

import logging
from pathlib import Path

from .pe_parser import PEParser, ResourceEntry

logger = logging.getLogger(__name__)


class ResourceManager:
    """Manager for PE file resources."""

    def __init__(self):
        self.parser = PEParser()
        self.current_file: Path | None = None

    def load_pe_file(self, file_path: Path) -> bool:
        """Load a PE file for resource management.

        Args:
            file_path: Path to the PE file

        Returns
        -------
            bool: True if file loaded successfully, False otherwise
        """
        if self.parser.load_file(file_path):
            self.current_file = file_path
            logger.info(f"Loaded PE file for resource management: {file_path}")
            return True
        return False

    def extract_resource(self, type_id: int, name_id: int, language_id: int = 0) -> bytes | None:
        """Extract raw resource data.

        Args:
            type_id: Resource type identifier
            name_id: Resource name identifier
            language_id: Language identifier (default: 0)

        Returns
        -------
            Resource data as bytes, or None if not found
        """
        resource = self.parser.get_resource_by_id(type_id, name_id, language_id)
        if resource:
            logger.info(f"Extracted resource: Type={type_id}, Name={name_id}")
            return resource.data
        return None

    def export_resource(self, type_id: int, name_id: int, output_path: Path, language_id: int = 0) -> bool:
        """Export a resource to a file.

        Args:
            type_id: Resource type identifier
            name_id: Resource name identifier
            output_path: Output file path
            language_id: Language identifier (default: 0)

        Returns
        -------
            bool: True if export successful, False otherwise
        """
        try:
            data = self.extract_resource(type_id, name_id, language_id)
            if data is None:
                logger.error(f"Resource not found: Type={type_id}, Name={name_id}")
                return False

            output_path.parent.mkdir(parents=True, exist_ok=True)
            output_path.write_bytes(data)
            logger.info(f"Exported resource to: {output_path}")
            return True
        except Exception as e:
            logger.error(f"Failed to export resource: {e}")
            return False

    def get_all_resources(self) -> list[ResourceEntry]:
        """Get all resources from the loaded PE file.

        Returns
        -------
            List of all ResourceEntry objects
        """
        if not self.parser.file_info:
            return []
        return self.parser.file_info.resources

    def get_resources_by_type(self, type_id: int) -> list[ResourceEntry]:
        """Get all resources of a specific type.

        Args:
            type_id: Resource type identifier

        Returns
        -------
            List of ResourceEntry objects of the specified type
        """
        return self.parser.get_resources_by_type(type_id)

    def get_resource_types(self) -> dict:
        """Get all resource types present in the file.

        Returns
        -------
            Dictionary mapping type IDs to type names
        """
        return self.parser.get_resource_types()

    def get_resource_info(self, type_id: int, name_id: int, language_id: int = 0) -> dict | None:
        """Get detailed information about a specific resource.

        Args:
            type_id: Resource type identifier
            name_id: Resource name identifier
            language_id: Language identifier (default: 0)

        Returns
        -------
            Dictionary with resource information, or None if not found
        """
        resource = self.parser.get_resource_by_id(type_id, name_id, language_id)
        if not resource:
            return None

        return {
            "type_id": resource.type_id,
            "type_name": resource.type_name,
            "name_id": resource.name_id,
            "name": resource.name,
            "language_id": resource.language_id,
            "offset": resource.offset,
            "size": resource.size,
            "data_size": len(resource.data),
        }

    def replace_resource(self, type_id: int, name_id: int, new_data: bytes, language_id: int = 0) -> bool:
        """Replace a resource with new data.

        Args:
            type_id: Resource type identifier
            name_id: Resource name identifier
            new_data: New resource data
            language_id: Language identifier (default: 0)

        Returns
        -------
            bool: True if replacement successful, False otherwise
        """
        try:
            if not self.current_file or not self.parser.pe:
                logger.error("No PE file loaded for modification")
                return False

            # Find the resource to replace
            resource = self.parser.get_resource_by_id(type_id, name_id, language_id)
            if not resource:
                logger.error(f"Resource not found: Type={type_id}, Name={name_id}")
                return False

            # Update the resource data in the PE structure
            if not self._update_resource_data(resource, new_data):
                return False

            # Save the modified PE file
            backup_path = self.current_file.with_suffix(self.current_file.suffix + ".backup")
            try:
                # Create backup
                import shutil

                shutil.copy2(self.current_file, backup_path)
                logger.info(f"Backup created: {backup_path}")

                # Write modified PE file
                self.parser.pe.write(str(self.current_file))
                logger.info(f"Successfully updated resource in: {self.current_file}")
                return True

            except Exception as e:
                logger.error(f"Failed to save modified PE file: {e}")
                # Restore backup if save failed
                if backup_path.exists():
                    shutil.copy2(backup_path, self.current_file)
                    backup_path.unlink()
                return False

        except Exception as e:
            logger.error(f"Failed to replace resource: {e}")
            return False

    def _update_resource_data(self, resource: ResourceEntry, new_data: bytes) -> bool:
        """Update resource data in the PE structure.

        Args:
            resource: Resource entry to update
            new_data: New data to write

        Returns
        -------
            bool: True if update successful, False otherwise
        """
        try:
            if not self.parser.pe:
                return False

            # Find the resource entry in PE structure
            for resource_type in self.parser.pe.DIRECTORY_ENTRY_RESOURCE.entries:
                if resource_type.id == resource.type_id and hasattr(resource_type, "directory"):
                    for resource_id in resource_type.directory.entries:
                        if resource_id.id == resource.name_id and hasattr(resource_id, "directory"):
                            for resource_lang in resource_id.directory.entries:
                                if resource_lang.data.lang == resource.language_id:
                                    # Update the resource data
                                    old_size = resource_lang.data.struct.Size
                                    new_size = len(new_data)

                                    # Update the data in PE structure
                                    self.parser.pe.set_bytes_at_offset(resource_lang.data.struct.OffsetToData, new_data)

                                    # Update size in structure
                                    resource_lang.data.struct.Size = new_size

                                    # Update resource entry size
                                    resource.size = new_size
                                    resource.data = new_data

                                    logger.info(f"Updated resource data: {old_size} -> {new_size} bytes")
                                    return True

            logger.error("Could not locate resource in PE structure")
            return False

        except Exception as e:
            logger.error(f"Failed to update resource data: {e}")
            return False

    def import_resource(self, type_id: int, name_id: int, input_path: Path, language_id: int = 0) -> bool:
        """Import a resource from a file.

        Args:
            type_id: Resource type identifier
            name_id: Resource name identifier
            input_path: Input file path
            language_id: Language identifier (default: 0)

        Returns
        -------
            bool: True if import successful, False otherwise
        """
        try:
            if not input_path.exists():
                logger.error(f"Input file not found: {input_path}")
                return False

            data = input_path.read_bytes()
            return self.replace_resource(type_id, name_id, data, language_id)
        except Exception as e:
            logger.error(f"Failed to import resource: {e}")
            return False

    def export_all_resources(self, output_dir: Path) -> tuple[int, int]:
        """Export all resources to a directory structure.

        Args:
            output_dir: Output directory path

        Returns
        -------
            Tuple of (successful_exports, total_resources)
        """
        if not self.parser.file_info:
            return 0, 0

        successful = 0
        total = len(self.parser.file_info.resources)

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

        for resource in self.parser.file_info.resources:
            # Create type directory
            type_dir = output_dir / resource.type_name
            type_dir.mkdir(exist_ok=True)

            # Get appropriate file extension
            extension = self._get_resource_extension(resource.type_id)

            # Create filename with proper extension
            filename = f"{resource.name_id}_{resource.language_id}{extension}"
            output_path = type_dir / filename

            if self.export_resource(resource.type_id, resource.name_id, output_path, resource.language_id):
                successful += 1

        logger.info(f"Exported {successful}/{total} resources to {output_dir}")
        return successful, total

    def _get_resource_extension(self, type_id: int) -> str:
        """Get appropriate file extension for resource type."""
        extension_map = {
            1: ".cur",  # RT_CURSOR
            2: ".bmp",  # RT_BITMAP
            3: ".ico",  # RT_ICON
            4: ".rc",  # RT_MENU
            5: ".dlg",  # RT_DIALOG
            6: ".txt",  # RT_STRING
            7: ".fnt",  # RT_FONTDIR
            8: ".fnt",  # RT_FONT
            9: ".rc",  # RT_ACCELERATOR
            10: ".bin",  # RT_RCDATA
            11: ".msg",  # RT_MESSAGETABLE
            12: ".cur",  # RT_GROUP_CURSOR
            14: ".ico",  # RT_GROUP_ICON
            16: ".rc",  # RT_VERSION
            17: ".rc",  # RT_DLGINCLUDE
            19: ".bin",  # RT_PLUGPLAY
            20: ".vxd",  # RT_VXD
            21: ".ani",  # RT_ANICURSOR
            22: ".ani",  # RT_ANIICON
            23: ".html",  # RT_HTML
            24: ".xml",  # RT_MANIFEST
        }
        return extension_map.get(type_id, ".bin")

    def get_file_info(self) -> dict | None:
        """Get information about the loaded PE file.

        Returns
        -------
            Dictionary with file information, or None if no file loaded
        """
        if not self.parser.file_info:
            return None

        info = self.parser.file_info
        return {
            "file_path": str(info.file_path),
            "file_name": info.file_path.name,
            "machine": info.machine,
            "subsystem": info.subsystem,
            "is_dll": info.is_dll,
            "is_64bit": info.is_64bit,
            "image_base": hex(info.image_base),
            "entry_point": hex(info.entry_point),
            "resource_count": len(info.resources),
        }

    def close(self):
        """Close the current PE file."""
        self.parser.close()
        self.current_file = None
        logger.info("Resource manager closed")

    def __enter__(self):
        """Enter the resource manager context."""
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """Close the resource manager on exit."""
        self.close()
