"""
MapHub Transfer Module - Cross-instance transfer functionality.

This module provides functionality to transfer folders, maps, and QGIS projects
between different MapHub instances.
"""

import tempfile
import uuid
from dataclasses import dataclass, field
from pathlib import Path
from typing import Dict, Any, List, Optional

from .client import MapHubClient


@dataclass
class TransferStats:
    """Statistics for a transfer operation."""
    maps_transferred: int = 0
    maps_failed: int = 0
    folders_transferred: int = 0
    folders_failed: int = 0
    qgis_projects_transferred: int = 0
    qgis_projects_failed: int = 0
    errors: List[str] = field(default_factory=list)


class MapHubTransfer:
    """
    Transfer folders and maps between MapHub instances.

    This class provides functionality to copy entire folder structures,
    including maps with their metadata and QGIS project files, from one
    MapHub instance to another.
    """

    def __init__(
        self,
        source_client: MapHubClient,
        dest_client: MapHubClient,
        temp_dir: Optional[str] = None
    ):
        """
        Initialize the transfer handler.

        Args:
            source_client: MapHubClient connected to the source instance
            dest_client: MapHubClient connected to the destination instance
            temp_dir: Optional temporary directory for file transfers.
                     If not provided, a system temp directory will be used.
        """
        self.source_client = source_client
        self.dest_client = dest_client
        self._temp_dir = temp_dir
        self._managed_temp_dir = None
        self.stats = TransferStats()

    @property
    def temp_dir(self) -> Path:
        """Get the temporary directory, creating it if necessary."""
        if self._temp_dir:
            path = Path(self._temp_dir)
            path.mkdir(parents=True, exist_ok=True)
            return path

        if self._managed_temp_dir is None:
            self._managed_temp_dir = tempfile.mkdtemp(prefix="maphub_transfer_")
        return Path(self._managed_temp_dir)

    def transfer_folder(
        self,
        source_folder_id: uuid.UUID,
        dest_parent_folder_id: uuid.UUID,
        recursive: bool = True,
        transfer_qgis_project: bool = True,
        preserve_public_status: bool = False
    ) -> TransferStats:
        """
        Transfer a folder and its contents from source to destination instance.

        Args:
            source_folder_id: UUID of the folder to transfer from source
            dest_parent_folder_id: UUID of the parent folder in destination
            recursive: Whether to transfer subfolders recursively
            transfer_qgis_project: Whether to transfer QGIS project files
            preserve_public_status: Whether to preserve the public/private status of maps

        Returns:
            TransferStats with transfer results
        """
        # Reset stats for this transfer
        self.stats = TransferStats()

        try:
            self._transfer_folder_recursive(
                source_folder_id,
                dest_parent_folder_id,
                recursive,
                transfer_qgis_project,
                preserve_public_status
            )
        except Exception as e:
            self.stats.errors.append(f"Fatal error during transfer: {e}")

        return self.stats

    def _transfer_folder_recursive(
        self,
        source_folder_id: uuid.UUID,
        dest_parent_folder_id: uuid.UUID,
        recursive: bool,
        transfer_qgis_project: bool,
        preserve_public_status: bool
    ) -> Optional[uuid.UUID]:
        """
        Internal recursive folder transfer implementation.

        Returns:
            UUID of the created folder in destination, or None on failure
        """
        try:
            # Get source folder info
            source_folder_info = self.source_client.folder.get_folder(source_folder_id)
            folder_data = source_folder_info.get("folder", {})
            folder_name = folder_data.get("name", "Transferred Folder")

            print(f"Transferring folder: {folder_name}")

            # Create folder in destination
            dest_folder = self.dest_client.folder.create_folder(
                folder_name,
                dest_parent_folder_id
            )
            dest_folder_id = uuid.UUID(dest_folder["id"])

            self.stats.folders_transferred += 1

            # Transfer QGIS project if requested and present
            if transfer_qgis_project:
                self._transfer_qgis_project(source_folder_id, dest_folder_id)

            # Transfer maps in this folder
            maps = source_folder_info.get("map_infos", [])
            for map_info in maps:
                source_map_id = uuid.UUID(map_info["id"])
                self._transfer_map(source_map_id, dest_folder_id, preserve_public_status)

            # Recursively transfer subfolders if requested
            if recursive:
                subfolders = source_folder_info.get("child_folders", [])
                for subfolder in subfolders:
                    subfolder_id = uuid.UUID(subfolder["id"])
                    self._transfer_folder_recursive(
                        subfolder_id,
                        dest_folder_id,
                        recursive,
                        transfer_qgis_project,
                        preserve_public_status
                    )

            return dest_folder_id

        except Exception as e:
            error_msg = f"Error transferring folder {source_folder_id}: {e}"
            print(error_msg)
            self.stats.errors.append(error_msg)
            self.stats.folders_failed += 1
            return None

    def _transfer_qgis_project(
        self,
        source_folder_id: uuid.UUID,
        dest_folder_id: uuid.UUID
    ) -> bool:
        """
        Transfer a QGIS project file from source to destination.

        Args:
            source_folder_id: UUID of the source folder
            dest_folder_id: UUID of the destination folder

        Returns:
            True if transfer was successful, False otherwise
        """
        try:
            # Check if source folder has a QGIS project
            if not self.source_client.folder.is_project(source_folder_id):
                return False

            print(f"Transferring QGIS project for folder {source_folder_id}")

            # Download to temp file
            temp_path = self.temp_dir / f"project_{source_folder_id}.qgz"
            self.source_client.folder.download_qgis_project(source_folder_id, str(temp_path))

            # Upload to destination
            self.dest_client.folder.upload_qgis_project(dest_folder_id, str(temp_path))

            # Clean up temp file
            temp_path.unlink(missing_ok=True)

            self.stats.qgis_projects_transferred += 1
            print(f"Successfully transferred QGIS project")
            return True

        except Exception as e:
            error_msg = f"Error transferring QGIS project for folder {source_folder_id}: {e}"
            print(error_msg)
            self.stats.errors.append(error_msg)
            self.stats.qgis_projects_failed += 1
            return False

    def _transfer_map(
        self,
        source_map_id: uuid.UUID,
        dest_folder_id: uuid.UUID,
        preserve_public_status: bool
    ) -> bool:
        """
        Transfer a map from source to destination.

        Args:
            source_map_id: UUID of the source map
            dest_folder_id: UUID of the destination folder
            preserve_public_status: Whether to preserve the public/private status

        Returns:
            True if transfer was successful, False otherwise
        """
        try:
            # Get source map info
            source_map_data = self.source_client.maps.get_map(source_map_id)["map"]
            map_name = source_map_data.get("name", "Transferred Map")
            map_type = source_map_data.get("type", "unknown")

            print(f"Transferring map: {map_name}")

            # Determine file extension based on map type
            if map_type == "raster":
                extension = ".tif"
                file_format = "tif"
            else:
                extension = ".fgb"
                file_format = "fgb"

            # Download map to temp file
            temp_path = self.temp_dir / f"map_{source_map_id}{extension}"
            self.source_client.maps.download_map(source_map_id, str(temp_path), file_format)

            # Upload to destination
            is_public = source_map_data.get("public", False) if preserve_public_status else False
            response = self.dest_client.maps.upload_map(
                map_name=map_name,
                folder_id=dest_folder_id,
                public=is_public,
                path=str(temp_path)
            )

            dest_map_id = response.get("map_id")
            if dest_map_id:
                # Update metadata on destination
                self._update_map_metadata(uuid.UUID(dest_map_id), source_map_data, preserve_public_status)

            # Clean up temp file
            temp_path.unlink(missing_ok=True)

            self.stats.maps_transferred += 1
            print(f"Successfully transferred map: {map_name}")
            return True

        except Exception as e:
            error_msg = f"Error transferring map {source_map_id}: {e}"
            print(error_msg)
            self.stats.errors.append(error_msg)
            self.stats.maps_failed += 1
            return False

    def _update_map_metadata(
        self,
        dest_map_id: uuid.UUID,
        source_map_data: Dict[str, Any],
        preserve_public_status: bool
    ) -> None:
        """
        Update map metadata on the destination to match the source.

        Args:
            dest_map_id: UUID of the destination map
            source_map_data: Source map metadata
            preserve_public_status: Whether to preserve the public/private status
        """
        try:
            update_kwargs = {}

            # Copy metadata fields
            if source_map_data.get("description"):
                update_kwargs["description"] = source_map_data["description"]
            if source_map_data.get("tags"):
                update_kwargs["tags"] = source_map_data["tags"]
            if source_map_data.get("license"):
                update_kwargs["license"] = source_map_data["license"]
            if source_map_data.get("source"):
                update_kwargs["source"] = source_map_data["source"]
            if source_map_data.get("readme"):
                update_kwargs["readme"] = source_map_data["readme"]
            if source_map_data.get("visuals"):
                update_kwargs["visuals"] = source_map_data["visuals"]
            if preserve_public_status and source_map_data.get("public") is not None:
                update_kwargs["public"] = source_map_data["public"]

            if update_kwargs:
                self.dest_client.maps.update_map(dest_map_id, **update_kwargs)

        except Exception as e:
            # Don't fail the whole transfer for metadata update failures
            error_msg = f"Warning: Could not update metadata for map {dest_map_id}: {e}"
            print(error_msg)
            self.stats.errors.append(error_msg)

    def get_transfer_report(self) -> str:
        """
        Generate a human-readable report of the transfer results.

        Returns:
            Formatted string with transfer statistics
        """
        lines = [
            "=" * 50,
            "MapHub Transfer Report",
            "=" * 50,
            "",
            "Folders:",
            f"  Transferred: {self.stats.folders_transferred}",
            f"  Failed: {self.stats.folders_failed}",
            "",
            "Maps:",
            f"  Transferred: {self.stats.maps_transferred}",
            f"  Failed: {self.stats.maps_failed}",
            "",
            "QGIS Projects:",
            f"  Transferred: {self.stats.qgis_projects_transferred}",
            f"  Failed: {self.stats.qgis_projects_failed}",
        ]

        if self.stats.errors:
            lines.extend([
                "",
                "Errors:",
            ])
            for error in self.stats.errors:
                lines.append(f"  - {error}")

        lines.extend([
            "",
            "=" * 50,
        ])

        return "\n".join(lines)

    def cleanup(self) -> None:
        """Clean up temporary files and directories."""
        if self._managed_temp_dir:
            import shutil
            try:
                shutil.rmtree(self._managed_temp_dir)
            except Exception as e:
                print(f"Warning: Could not clean up temp directory: {e}")
            self._managed_temp_dir = None
