"""Resource tree view component for Resource Hacker.

This module provides the tree view widget for displaying PE file resources
in a hierarchical structure.
"""

from __future__ import annotations

import logging
from pathlib import Path

from PySide2.QtCore import QItemSelectionModel, Qt, Signal
from PySide2.QtWidgets import QAction, QFileDialog, QMenu, QMessageBox, QTreeWidget, QTreeWidgetItem

from ..models.resource_manager import ResourceManager

logger = logging.getLogger(__name__)


class ResourceTreeWidgetItem(QTreeWidgetItem):
    """Custom tree widget item for resource entries."""

    def __init__(self, resource_info: dict):
        super().__init__()
        self.resource_info = resource_info
        self._setup_item()

    def _setup_item(self):
        """Set up the tree item with resource information."""
        # Set item text (columns: Name, Type, Size, Language)
        self.setText(0, str(self.resource_info.get("name", "Unknown")))
        self.setText(1, self.resource_info.get("type_name", "Unknown"))
        self.setText(2, self.resource_info.get("size_formatted", "0 B"))
        self.setText(3, str(self.resource_info.get("language_id", 0)))

        # Store resource identifiers in user data
        self.setData(
            0,
            Qt.UserRole,
            {
                "type_id": self.resource_info.get("type_id"),
                "name_id": self.resource_info.get("name_id"),
                "language_id": self.resource_info.get("language_id"),
            },
        )


class ResourceTreeView(QTreeWidget):
    """Tree view for displaying PE file resources."""

    # Signals
    resource_selected = Signal(dict)  # Emits resource info when selected
    file_loaded = Signal(str)  # Emits file path when loaded

    def __init__(self):
        super().__init__()
        self.resource_manager: ResourceManager | None = None
        self.current_file_path: str | None = None
        self._setup_tree()
        self._setup_context_menu()

    def _setup_tree(self):
        """Set up the tree widget."""
        # Set headers
        self.setHeaderLabels(["Name", "Type", "Size", "Language"])
        self.setAlternatingRowColors(True)
        self.setSortingEnabled(True)
        self.setContextMenuPolicy(Qt.CustomContextMenu)

        # Set column widths
        self.setColumnWidth(0, 200)  # Name
        self.setColumnWidth(1, 150)  # Type
        self.setColumnWidth(2, 80)  # Size
        self.setColumnWidth(3, 80)  # Language

        # Enable selection
        self.setSelectionMode(QTreeWidget.SingleSelection)
        self.setSelectionBehavior(QTreeWidget.SelectRows)

        # Connect signals
        self.itemSelectionChanged.connect(self._on_selection_changed)
        self.customContextMenuRequested.connect(self._show_context_menu)

        logger.info("Resource tree view initialized")

    def _setup_context_menu(self):
        """Set up context menu for tree items."""
        self.context_menu = QMenu(self)

        # Export action
        self.export_action = QAction("Export Resource...", self)
        self.export_action.triggered.connect(self._export_resource)
        self.context_menu.addAction(self.export_action)

        # Replace action
        self.replace_action = QAction("Replace Resource...", self)
        self.replace_action.triggered.connect(self._replace_resource)
        self.context_menu.addAction(self.replace_action)

        self.context_menu.addSeparator()

        # Properties action
        self.properties_action = QAction("Properties", self)
        self.properties_action.triggered.connect(self._show_properties)
        self.context_menu.addAction(self.properties_action)

    def load_pe_file(self, file_path: str, resource_manager: ResourceManager):
        """Load a PE file and populate the tree."""
        try:
            self.clear()
            self.resource_manager = resource_manager
            self.current_file_path = str(file_path)

            # Get all resources
            resources = resource_manager.get_all_resources()
            if not resources:
                self._add_empty_item("No resources found")
                self.file_loaded.emit(str(file_path))
                return

            # Group resources by type
            resources_by_type = {}
            for resource in resources:
                type_id = resource.type_id
                if type_id not in resources_by_type:
                    resources_by_type[type_id] = []
                resources_by_type[type_id].append(resource)

            # Create type group items
            for type_id, type_resources in resources_by_type.items():
                if type_resources:
                    type_name = type_resources[0].type_name
                    type_item = self._create_type_item(type_id, type_name, type_resources)
                    self.addTopLevelItem(type_item)

                    # Add resource items under type
                    for resource in type_resources:
                        resource_item = self._create_resource_item(resource)
                        type_item.addChild(resource_item)

            # Expand first level items
            for i in range(self.topLevelItemCount()):
                item = self.topLevelItem(i)
                item.setExpanded(True)

            self.file_loaded.emit(str(file_path))
            logger.info(f"Loaded {len(resources)} resources from {file_path}")

        except Exception as e:
            logger.error(f"Error loading PE file {file_path}: {e}")
            self._add_error_item(f"Error loading file: {e!s}")

    def _create_type_item(self, type_id: int, type_name: str, resources: list) -> QTreeWidgetItem:
        """Create a tree item for a resource type group."""
        item = QTreeWidgetItem()
        item.setText(0, f"{type_name} ({len(resources)})")
        item.setText(1, f"Type {type_id}")
        item.setFirstColumnSpanned(True)
        item.setFlags(item.flags() & ~Qt.ItemIsSelectable)  # Make type items non-selectable
        return item

    def _create_resource_item(self, resource) -> ResourceTreeWidgetItem:
        """Create a tree item for a resource entry."""
        # Create resource info dictionary
        resource_info = {
            "type_id": resource.type_id,
            "type_name": resource.type_name,
            "name_id": resource.name_id,
            "name": str(resource.name_id),
            "language_id": resource.language_id,
            "offset": resource.offset,
            "size": resource.size,
            "size_formatted": self._format_size(resource.size),
            "data": resource.data,
        }

        return ResourceTreeWidgetItem(resource_info)

    def _add_empty_item(self, message: str):
        """Add an empty state item."""
        item = QTreeWidgetItem()
        item.setText(0, message)
        item.setFirstColumnSpanned(True)
        item.setFlags(Qt.NoItemFlags)
        self.addTopLevelItem(item)

    def _add_error_item(self, message: str):
        """Add an error state item."""
        item = QTreeWidgetItem()
        item.setText(0, f"Error: {message}")
        item.setFirstColumnSpanned(True)
        item.setFlags(Qt.NoItemFlags)
        self.addTopLevelItem(item)

    def _format_size(self, size: int) -> str:
        """Format size in human-readable format."""
        if size == 0:
            return "0 B"

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

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

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

    def _on_selection_changed(self):
        """Handle selection change."""
        selected_items = self.selectedItems()
        if not selected_items:
            return

        item = selected_items[0]

        # Only process ResourceTreeWidgetItem instances (leaf nodes)
        if isinstance(item, ResourceTreeWidgetItem):
            self.resource_selected.emit(item.resource_info)

    def _show_context_menu(self, position):
        """Show context menu for selected item."""
        item = self.itemAt(position)
        if not item or not isinstance(item, ResourceTreeWidgetItem):
            return

        # Update action states
        self.export_action.setEnabled(True)
        self.replace_action.setEnabled(True)
        self.properties_action.setEnabled(True)

        # Show context menu
        self.context_menu.exec_(self.viewport().mapToGlobal(position))

    def _replace_resource(self):
        """Replace the selected resource with new data."""
        selected_items = self.selectedItems()
        if not selected_items:
            return

        item = selected_items[0]
        if not isinstance(item, ResourceTreeWidgetItem):
            return

        resource_info = item.resource_info
        type_id = resource_info.get("type_id")
        name_id = resource_info.get("name_id")
        language_id = resource_info.get("language_id", 0)
        type_name = resource_info.get("type_name", "Unknown")
        current_size = resource_info.get("size", 0)

        # Generate appropriate file filter based on resource type
        extension = self._get_resource_extension(type_id)
        filters = f"Resource Files (*{extension});;All Files (*.*)"

        # For icons, also allow common image formats
        if type_id in [3, 14]:  # RT_ICON or RT_GROUP_ICON
            filters = "Icon Files (*.ico *.png *.bmp *.jpg *.jpeg);;All Files (*.*)"

        # Show open file dialog
        file_path, _ = QFileDialog.getOpenFileName(
            self,
            "Select New Resource File",
            "",
            filters,
        )

        if file_path and self.resource_manager:
            try:
                input_path = Path(file_path)
                if not input_path.exists():
                    QMessageBox.critical(
                        self,
                        "File Not Found",
                        f"The selected file does not exist:\n{input_path}",
                    )
                    return

                # Read new resource data
                new_data = input_path.read_bytes()
                new_size = len(new_data)

                # Confirm replacement
                reply = QMessageBox.question(
                    self,
                    "Confirm Replacement",
                    f"Replace {type_name} resource {name_id} (Language: {language_id})?\n\n"
                    f"Current size: {self._format_size(current_size)} ({current_size} bytes)\n"
                    f"New size: {self._format_size(new_size)} ({new_size} bytes)\n\n"
                    f"This operation cannot be undone. Continue?",
                    QMessageBox.Yes | QMessageBox.No,
                    QMessageBox.No,
                )

                if reply == QMessageBox.No:
                    return

                # Perform the replacement
                if self.resource_manager.replace_resource(type_id, name_id, new_data, language_id):
                    QMessageBox.information(
                        self,
                        "Replacement Successful",
                        f"Resource replaced successfully!\n\n"
                        f"Type: {type_name}\n"
                        f"ID: {name_id}\n"
                        f"Language: {language_id}\n"
                        f"Original size: {self._format_size(current_size)}\n"
                        f"New size: {self._format_size(new_size)}",
                    )
                    # Refresh the view to show updated data
                    self.refresh_view()
                else:
                    QMessageBox.critical(
                        self,
                        "Replacement Failed",
                        "Failed to replace resource. The operation may have failed or been cancelled.",
                    )
            except Exception as e:
                QMessageBox.critical(
                    self,
                    "Replacement Error",
                    f"An error occurred while replacing the resource:\n\n{e!s}",
                )

    def _export_resource(self):
        """Export the selected resource."""
        selected_items = self.selectedItems()
        if not selected_items:
            return

        item = selected_items[0]
        if not isinstance(item, ResourceTreeWidgetItem):
            return

        resource_info = item.resource_info
        type_id = resource_info.get("type_id")
        name_id = resource_info.get("name_id")
        language_id = resource_info.get("language_id", 0)
        type_name = resource_info.get("type_name", "Unknown")

        # Generate appropriate file extension based on resource type
        extension = self._get_resource_extension(type_id)
        default_filename = f"{type_name}_{name_id}_{language_id}{extension}"

        # Show save file dialog
        file_path, _ = QFileDialog.getSaveFileName(
            self,
            "Export Resource",
            default_filename,
            f"Resource Files (*{extension});;All Files (*.*)",
        )

        if file_path and self.resource_manager:
            try:
                output_path = Path(file_path)
                if self.resource_manager.export_resource(type_id, name_id, output_path, language_id):
                    from PySide2.QtWidgets import QMessageBox

                    QMessageBox.information(
                        self,
                        "Export Successful",
                        f"Resource exported successfully to:\n{output_path}",
                    )
                else:
                    from PySide2.QtWidgets import QMessageBox

                    QMessageBox.critical(
                        self,
                        "Export Failed",
                        "Failed to export resource. The resource may not exist or is corrupted.",
                    )
            except Exception as e:
                from PySide2.QtWidgets import QMessageBox

                QMessageBox.critical(
                    self,
                    "Export Error",
                    f"An error occurred while exporting the resource:\n\n{e!s}",
                )

    def _show_properties(self):
        """Show properties of selected resource."""
        selected_items = self.selectedItems()
        if not selected_items:
            return

        item = selected_items[0]
        if not isinstance(item, ResourceTreeWidgetItem):
            return

        resource_info = item.resource_info

        # Create properties message
        properties = [
            f"Name: {resource_info.get('name', 'Unknown')}",
            f"Type: {resource_info.get('type_name', 'Unknown')} (ID: {resource_info.get('type_id', 'Unknown')})",
            f"Size: {resource_info.get('size_formatted', '0 B')} ({resource_info.get('size', 0)} bytes)",
            f"Language: {resource_info.get('language_id', 0)}",
            f"Offset: 0x{resource_info.get('offset', 0):x}",
            f"Data Hash: {self._get_data_hash(resource_info.get('data', b''))}",
        ]

        from PySide2.QtWidgets import QMessageBox

        QMessageBox.information(self, "Resource Properties", "\n".join(properties))

    def _get_data_hash(self, data: bytes) -> str:
        """Get hex representation of first few bytes of data."""
        if not data:
            return "Empty"

        # Show first 16 bytes as hex
        hex_data = data[:16].hex()
        if len(data) > 16:
            hex_data += "..."
        return hex_data

    def get_selected_resource(self) -> dict | None:
        """Get information about the currently selected resource."""
        selected_items = self.selectedItems()
        if not selected_items:
            return None

        item = selected_items[0]
        if isinstance(item, ResourceTreeWidgetItem):
            return item.resource_info

        return None

    def select_resource(self, type_id: int, name_id: int, language_id: int = 0):
        """Select a specific resource in the tree."""
        for i in range(self.topLevelItemCount()):
            type_item = self.topLevelItem(i)
            for j in range(type_item.childCount()):
                resource_item = type_item.child(j)
                if isinstance(resource_item, ResourceTreeWidgetItem):
                    resource_info = resource_item.resource_info
                    if (
                        resource_info.get("type_id") == type_id
                        and resource_info.get("name_id") == name_id
                        and resource_info.get("language_id") == language_id
                    ):
                        self.setCurrentItem(resource_item, 0, QItemSelectionModel.ClearAndSelect)
                        return True
        return False

    def refresh_view(self):
        """Refresh the tree view with current data."""
        if self.current_file_path and self.resource_manager:
            self.load_pe_file(self.current_file_path, self.resource_manager)

    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")
