"""Qt widget for intercepting drag events and initiating native drag operations."""

import logging
from pathlib import Path
from typing import List, Optional, Union

from PySide6.QtCore import QMimeData, Qt, QUrl, Signal
from PySide6.QtGui import QDrag
from PySide6.QtWidgets import QWidget

from webdrop_bridge.config import Config
from webdrop_bridge.core.url_converter import URLConverter
from webdrop_bridge.core.validator import PathValidator, ValidationError

logger = logging.getLogger(__name__)


class DragInterceptor(QWidget):
    """Widget that handles drag initiation for file paths or Azure URLs.

    Intercepts drag events from web content, converts Azure Blob Storage URLs
    to local paths, validates them, and initiates native Qt drag operations.

    Supports both single and multiple file drag operations.

    Signals:
        drag_started: Emitted when a drag operation begins successfully
                     (source_urls_or_paths: str, local_paths: str - comma-separated for multiple)
        drag_failed: Emitted when drag initiation fails
                    (source_urls_or_paths: str, error_message: str)
    """

    # Signals with string parameters
    drag_started = Signal(str, str)  # (source_urls_or_paths, local_paths)
    drag_failed = Signal(str, str)  # (source_urls_or_paths, error_message)

    def __init__(self, config: Config, parent: Optional[QWidget] = None):
        """Initialize the drag interceptor.

        Args:
            config: Application configuration
            parent: Parent widget
        """
        super().__init__(parent)
        self.config = config
        self._validator = PathValidator(
            config.allowed_roots, check_file_exists=config.check_file_exists
        )
        self._url_converter = URLConverter(config)

    def handle_drag(self, text_or_list: Union[str, List[str]]) -> bool:
        """Handle drag event from web view (single or multiple files).

        Determines if the text/list contains Azure URLs or file paths, converts if needed,
        validates, and initiates native drag operation.

        Supports:
        - Single string (backward compatible)
        - List of strings (multiple drag support)

        Args:
            text_or_list: Azure URL/file path (str) or list of URLs/paths (List[str])

        Returns:
            True if native drag was initiated, False otherwise
        """
        # Normalize input to list
        if isinstance(text_or_list, str):
            text_list = [text_or_list]
        elif isinstance(text_or_list, (list, tuple)):
            text_list = list(text_or_list)
        else:
            error_msg = f"Unexpected drag data type: {type(text_or_list)}"
            logger.error(error_msg)
            self.drag_failed.emit("", error_msg)
            return False

        # Validate that we have content
        if not text_list or all(not t or not str(t).strip() for t in text_list):
            error_msg = "Empty drag text"
            logger.warning(error_msg)
            self.drag_failed.emit("", error_msg)
            return False

        # Clean up text items
        text_list = [str(t).strip() for t in text_list if str(t).strip()]
        logger.debug(f"Handling drag for {len(text_list)} item(s)")

        # Convert each text to local path
        local_paths = []
        source_texts = []

        for text in text_list:
            # Check if it's an Azure URL and convert to local path
            if self._url_converter.is_azure_url(text):
                local_path = self._url_converter.convert_url_to_path(text)
                if local_path is None:
                    error_msg = f"No mapping found for URL: {text}"
                    logger.warning(error_msg)
                    self.drag_failed.emit(text, error_msg)
                    return False
                source_texts.append(text)
            else:
                # Treat as direct file path
                local_path = Path(text)
                source_texts.append(text)

            # Validate the path
            try:
                self._validator.validate(local_path)
            except ValidationError as e:
                error_msg = str(e)
                logger.warning(f"Validation failed for {local_path}: {error_msg}")
                self.drag_failed.emit(text, error_msg)
                return False

            local_paths.append(local_path)

        logger.info(
            f"Initiating drag for {len(local_paths)} file(s): {[str(p) for p in local_paths]}"
        )

        # Create native file drag with all paths
        success = self._create_native_drag(local_paths)

        if success:
            source_str = " | ".join(source_texts) if len(source_texts) > 1 else source_texts[0]
            paths_str = (
                " | ".join(str(p) for p in local_paths)
                if len(local_paths) > 1
                else str(local_paths[0])
            )
            self.drag_started.emit(source_str, paths_str)
        else:
            error_msg = "Failed to create native drag operation"
            logger.error(error_msg)
            source_str = " | ".join(source_texts) if len(source_texts) > 1 else source_texts[0]
            self.drag_failed.emit(source_str, error_msg)

        return success

    def _create_native_drag(self, file_paths: Union[Path, List[Path]]) -> bool:
        """Create a native file system drag operation.

        Args:
            file_paths: Single local file path or list of local file paths

        Returns:
            True if drag was initiated successfully
        """
        try:
            # Normalize to list
            if isinstance(file_paths, Path):
                paths_list = [file_paths]
            else:
                paths_list = list(file_paths)

            # Create MIME data with file URLs
            mime_data = QMimeData()
            file_urls = [QUrl.fromLocalFile(str(p)) for p in paths_list]
            mime_data.setUrls(file_urls)

            logger.debug(f"Creating drag with {len(file_urls)} file(s)")

            # Create and execute drag
            drag = QDrag(self)
            drag.setMimeData(mime_data)

            # Optional: Set a drag icon/pixmap if available
            # drag.setPixmap(...)

            # Start drag operation (blocks until drop or cancel)
            # In RDP/remote environments, drag.exec() may not return the expected result
            # even though the drag was successful. We accept the drag as successful if
            # it completes without exception.
            result = drag.exec(Qt.DropAction.CopyAction)

            # Accept any drop action result (including NoDropAction) as successful
            # in remote environments like RDP where OS feedback is unreliable.
            logger.debug(f"Drag operation completed with result: {result}")
            return True

        except Exception as e:
            logger.exception(f"Error creating native drag: {e}")
            return False
