# -*- coding: utf-8 -*-
"""
Canvas Tools for Advanced Map Downloader

This module provides map tools for canvas interaction:
- Point capture tool for Lat-Long with Pixels mode
- Rectangle drawing tool for extent-based modes
- Rubber band management for visualizing selected areas
"""

from qgis.PyQt.QtCore import Qt, pyqtSignal
from qgis.PyQt.QtGui import QColor
from qgis.gui import QgsMapTool, QgsMapToolEmitPoint, QgsRubberBand
from qgis.core import (
    QgsCoordinateReferenceSystem,
    QgsCoordinateTransform,
    QgsProject,
    QgsPointXY,
    QgsRectangle,
    QgsWkbTypes
)


class PointCaptureTool(QgsMapToolEmitPoint):
    """
    Map tool for capturing a single point (click on map).
    Used for Lat-Long with Pixels mode where user clicks center point.
    """

    point_captured = pyqtSignal(float, float)  # lat, lon in WGS84

    def __init__(self, canvas):
        super().__init__(canvas)
        self.canvas = canvas
        self.setCursor(Qt.CursorShape.CrossCursor)

    def canvasReleaseEvent(self, event):
        """Handle mouse click to capture point."""
        # Get click point in map coordinates
        point = self.toMapCoordinates(event.pos())

        # Get the canvas CRS
        map_crs = self.canvas.mapSettings().destinationCrs()
        wgs84_crs = QgsCoordinateReferenceSystem("EPSG:4326")

        # Transform to WGS84 if needed
        if map_crs != wgs84_crs:
            transformer = QgsCoordinateTransform(
                map_crs, wgs84_crs, QgsProject.instance())
            point_wgs84 = transformer.transform(point)
        else:
            point_wgs84 = point

        # Emit the point (lat, lon)
        self.point_captured.emit(point_wgs84.y(), point_wgs84.x())


class RectangleCaptureTool(QgsMapTool):
    """
    Map tool for drawing a rectangle on the canvas.
    Used for XYZ, Square Grid, WKT, WKB, GeoJSON modes.
    """

    # min_lat, min_lon, max_lat, max_lon in WGS84
    rectangle_captured = pyqtSignal(float, float, float, float)

    def __init__(self, canvas):
        super().__init__(canvas)
        self.canvas = canvas
        self.rubber_band = None
        self.start_point = None
        self.end_point = None
        self.is_drawing = False
        self.setCursor(Qt.CursorShape.CrossCursor)

    def canvasPressEvent(self, event):
        """Handle mouse press to start drawing."""
        self.start_point = self.toMapCoordinates(event.pos())
        self.end_point = self.start_point
        self.is_drawing = True

        # Create rubber band for preview
        if self.rubber_band is None:
            self.rubber_band = QgsRubberBand(
                self.canvas, QgsWkbTypes.GeometryType.PolygonGeometry)
            self.rubber_band.setColor(QColor(255, 0, 0, 100))
            self.rubber_band.setWidth(2)

        self.rubber_band.reset(QgsWkbTypes.GeometryType.PolygonGeometry)
        self._update_rubber_band()

    def canvasMoveEvent(self, event):
        """Handle mouse move to update rectangle preview."""
        if not self.is_drawing:
            return

        self.end_point = self.toMapCoordinates(event.pos())
        self._update_rubber_band()

    def canvasReleaseEvent(self, event):
        """Handle mouse release to finalize rectangle."""
        if not self.is_drawing:
            return

        self.end_point = self.toMapCoordinates(event.pos())
        self.is_drawing = False

        # Clear the rubber band
        if self.rubber_band:
            self.rubber_band.reset(QgsWkbTypes.GeometryType.PolygonGeometry)

        # Get the rectangle bounds
        if self.start_point and self.end_point:
            rect = QgsRectangle(self.start_point, self.end_point)

            # Transform to WGS84
            map_crs = self.canvas.mapSettings().destinationCrs()
            wgs84_crs = QgsCoordinateReferenceSystem("EPSG:4326")

            if map_crs != wgs84_crs:
                transformer = QgsCoordinateTransform(
                    map_crs, wgs84_crs, QgsProject.instance())
                rect_wgs84 = transformer.transformBoundingBox(rect)
            else:
                rect_wgs84 = rect

            # Emit rectangle as (min_lat, min_lon, max_lat, max_lon)
            self.rectangle_captured.emit(
                rect_wgs84.yMinimum(), rect_wgs84.xMinimum(),
                rect_wgs84.yMaximum(), rect_wgs84.xMaximum()
            )

    def _update_rubber_band(self):
        """Update the rubber band preview."""
        if not self.rubber_band or not self.start_point or not self.end_point:
            return

        self.rubber_band.reset(QgsWkbTypes.GeometryType.PolygonGeometry)

        # Create rectangle points
        rect = QgsRectangle(self.start_point, self.end_point)

        self.rubber_band.addPoint(
            QgsPointXY(
                rect.xMinimum(),
                rect.yMinimum()),
            False)
        self.rubber_band.addPoint(
            QgsPointXY(
                rect.xMinimum(),
                rect.yMaximum()),
            False)
        self.rubber_band.addPoint(
            QgsPointXY(
                rect.xMaximum(),
                rect.yMaximum()),
            False)
        self.rubber_band.addPoint(
            QgsPointXY(
                rect.xMaximum(),
                rect.yMinimum()),
            True)

    def deactivate(self):
        """Clean up when tool is deactivated."""
        if self.rubber_band:
            self.rubber_band.reset(QgsWkbTypes.GeometryType.PolygonGeometry)
        super().deactivate()


class ExtentRubberBand:
    """
    Utility class for managing a rubber band that shows the export extent.
    Creates and updates a persistent rubber band on the canvas.
    """

    def __init__(self, canvas):
        self.canvas = canvas
        self.rubber_band = None

    def create(self):
        """Create a new rubber band for extent visualization."""
        if self.rubber_band is None:
            self.rubber_band = QgsRubberBand(
                self.canvas, QgsWkbTypes.GeometryType.PolygonGeometry)
            # Blue with transparency
            self.rubber_band.setColor(QColor(0, 120, 255, 80))
            self.rubber_band.setStrokeColor(QColor(0, 80, 200))
            self.rubber_band.setWidth(2)

    def set_extent(self, extent, from_crs='EPSG:3857'):
        """
        Set the rubber band to show the given extent.

        :param extent: QgsRectangle
        :param from_crs: CRS of the extent
        """
        self.create()
        self.rubber_band.reset(QgsWkbTypes.GeometryType.PolygonGeometry)

        # Transform extent to map CRS
        map_crs = self.canvas.mapSettings().destinationCrs()
        source_crs = QgsCoordinateReferenceSystem(from_crs)

        if map_crs != source_crs:
            transformer = QgsCoordinateTransform(
                source_crs, map_crs, QgsProject.instance())
            extent = transformer.transformBoundingBox(extent)

        # Add rectangle points
        self.rubber_band.addPoint(
            QgsPointXY(
                extent.xMinimum(),
                extent.yMinimum()),
            False)
        self.rubber_band.addPoint(
            QgsPointXY(
                extent.xMinimum(),
                extent.yMaximum()),
            False)
        self.rubber_band.addPoint(
            QgsPointXY(
                extent.xMaximum(),
                extent.yMaximum()),
            False)
        self.rubber_band.addPoint(
            QgsPointXY(
                extent.xMaximum(),
                extent.yMinimum()),
            True)

    def set_geometry(self, geometry, from_crs='EPSG:4326'):
        """
        Set the rubber band to show the given geometry.

        :param geometry: QgsGeometry
        :param from_crs: CRS of the geometry
        """
        self.create()
        self.rubber_band.reset(QgsWkbTypes.GeometryType.PolygonGeometry)

        # Transform geometry to map CRS
        map_crs = self.canvas.mapSettings().destinationCrs()
        source_crs = QgsCoordinateReferenceSystem(from_crs)

        if map_crs != source_crs:
            transformer = QgsCoordinateTransform(
                source_crs, map_crs, QgsProject.instance())
            geometry.transform(transformer)

        self.rubber_band.setToGeometry(geometry, None)

    def clear(self):
        """Clear and hide the rubber band."""
        if self.rubber_band:
            self.rubber_band.reset(QgsWkbTypes.GeometryType.PolygonGeometry)

    def remove(self):
        """Completely remove the rubber band."""
        if self.rubber_band:
            self.canvas.scene().removeItem(self.rubber_band)
            self.rubber_band = None


def get_canvas_extent(iface):
    """
    Get the current canvas extent in WGS84.

    :param iface: QGIS interface
    :returns: tuple (min_lat, min_lon, max_lat, max_lon)
    """
    canvas = iface.mapCanvas()
    extent = canvas.extent()

    # Transform to WGS84
    map_crs = canvas.mapSettings().destinationCrs()
    wgs84_crs = QgsCoordinateReferenceSystem("EPSG:4326")

    if map_crs != wgs84_crs:
        transformer = QgsCoordinateTransform(
            map_crs, wgs84_crs, QgsProject.instance())
        extent = transformer.transformBoundingBox(extent)

    return (extent.yMinimum(), extent.xMinimum(),
            extent.yMaximum(), extent.xMaximum())


def get_canvas_extent_3857(iface):
    """
    Get the current canvas extent in EPSG:3857.

    :param iface: QGIS interface
    :returns: QgsRectangle in EPSG:3857
    """
    canvas = iface.mapCanvas()
    extent = canvas.extent()

    # Transform to EPSG:3857
    map_crs = canvas.mapSettings().destinationCrs()
    target_crs = QgsCoordinateReferenceSystem("EPSG:3857")

    if map_crs != target_crs:
        transformer = QgsCoordinateTransform(
            map_crs, target_crs, QgsProject.instance())
        extent = transformer.transformBoundingBox(extent)

    return extent
