"""
Scale Polygon Action for Right-click Utilities and Shortcuts Hub

Scales the selected polygon feature by a specified scale factor.
User inputs the scale factor, and the polygon is scaled around its centroid.
"""

from .base_action import BaseAction
from qgis.core import QgsGeometry, QgsPointXY, QgsWkbTypes
from qgis.PyQt.QtWidgets import (
    QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, 
    QFormLayout, QDoubleSpinBox
)
from ..i18n import tr


class ScalePolygonDialog(QDialog):
    """Dialog for user input of scale factor."""
    
    def __init__(self, parent=None, default_scale=1.0, polygon_area=None):
        super().__init__(parent)
        self.setWindowTitle(tr("Scale Polygon"))
        self.setModal(True)
        self.resize(350, 150)
        
        layout = QVBoxLayout()
        form_layout = QFormLayout()
        
        # Polygon area info
        if polygon_area is not None:
            area_label = QLabel(tr("Polygon area: {area:.2f} square map units").format(area=polygon_area))
            area_label.setStyleSheet("color: gray; font-size: 10px;")
            form_layout.addRow("", area_label)
        
        # Scale factor input
        self.scale_spinbox = QDoubleSpinBox()
        self.scale_spinbox.setRange(0.01, 100.0)
        self.scale_spinbox.setValue(default_scale)
        self.scale_spinbox.setSuffix("x")
        self.scale_spinbox.setDecimals(2)
        form_layout.addRow(tr("Scale Factor:"), self.scale_spinbox)
        
        # Scale help
        scale_help = QLabel(tr("1.0 = original size, >1.0 = larger, <1.0 = smaller"))
        scale_help.setStyleSheet("color: gray; font-size: 10px;")
        form_layout.addRow("", scale_help)
        
        layout.addLayout(form_layout)
        
        # Buttons
        button_layout = QHBoxLayout()
        self.ok_button = QPushButton(tr("Scale"))
        self.cancel_button = QPushButton(tr("Cancel"))
        
        self.ok_button.clicked.connect(self.accept)
        self.cancel_button.clicked.connect(self.reject)
        
        button_layout.addWidget(self.ok_button)
        button_layout.addWidget(self.cancel_button)
        layout.addLayout(button_layout)
        
        self.setLayout(layout)
        
        # Set focus to scale input
        self.scale_spinbox.setFocus()
        self.scale_spinbox.selectAll()
    
    def get_scale(self):
        """Get the input scale factor."""
        return self.scale_spinbox.value()


class ScalePolygonAction(BaseAction):
    """
    Action to scale a polygon feature by a specified scale factor.
    
    This action scales the selected polygon feature around its centroid by the scale
    factor specified by the user. Scale factor > 1.0 makes the polygon larger,
    scale factor < 1.0 makes it smaller.
    """
    
    def __init__(self):
        """Initialize the action with metadata and configuration."""
        super().__init__()
        
        # Required properties
        self.action_id = "scale_polygon"
        self.name = "Scale Polygon"
        self.category = "Editing"
        self.description = "Scale the selected polygon feature by a specified scale factor. User inputs scale factor (1.0 = original size, >1.0 = larger, <1.0 = smaller). Polygon is scaled around its centroid."
        self.enabled = True
        
        # Action scoping - this works on individual features
        self.set_action_scope('feature')
        self.set_supported_scopes(['feature'])
        
        # Feature type support - only works with polygon features
        self.set_supported_click_types(['polygon', 'multipolygon'])
        self.set_supported_geometry_types(['polygon', 'multipolygon'])
    
    def get_settings_schema(self):
        """
        Define the settings schema for this action.
        
        Returns:
            dict: Settings schema with setting definitions
        """
        return {
            # SCALING SETTINGS
            'default_scale_factor': {
                'type': 'float',
                'default': 1.0,
                'label': 'Default Scale Factor',
                'description': 'Default scale factor value shown in the input dialog (1.0 = original size)',
                'min': 0.01,
                'max': 100.0,
                'step': 0.1,
            },
            
            # BEHAVIOR SETTINGS
            'confirm_before_scale': {
                'type': 'bool',
                'default': False,
                'label': 'Confirm Before Scaling',
                'description': 'Show confirmation dialog before scaling the polygon',
            },
            'show_success_message': {
                'type': 'bool',
                'default': True,
                'label': 'Show Success Message',
                'description': 'Display a message when polygon is scaled successfully',
            },
            'show_polygon_area_info': {
                'type': 'bool',
                'default': True,
                'label': 'Show Polygon Area Info',
                'description': 'Display polygon area information in the input dialog and success messages',
            },
            'auto_commit_changes': {
                'type': 'bool',
                'default': True,
                'label': 'Auto-commit Changes',
                'description': 'Automatically commit changes after scaling (recommended)',
            },
            'handle_edit_mode_automatically': {
                'type': 'bool',
                'default': True,
                'label': 'Handle Edit Mode Automatically',
                'description': 'Automatically enter/exit edit mode as needed',
            },
            'rollback_on_error': {
                'type': 'bool',
                'default': True,
                'label': 'Rollback on Error',
                'description': 'Rollback changes if scaling operation fails',
            },
        }
    
    def get_setting(self, setting_name, default_value=None):
        """
        Get a setting value for this action.
        
        Args:
            setting_name (str): Name of the setting to retrieve
            default_value: Default value if setting not found
            
        Returns:
            Setting value or default_value
        """
        from qgis.PyQt.QtCore import QSettings
        settings = QSettings()
        key = f"RightClickUtilities/{self.action_id}/{setting_name}"
        return settings.value(key, default_value)
    
    def execute(self, context):
        """
        Execute the scale polygon action.
        
        Args:
            context (dict): Context dictionary with click information
        """
        # Get settings with proper type conversion
        try:
            default_scale = float(self.get_setting('default_scale_factor', 1.0))
            confirm_before_scale = bool(self.get_setting('confirm_before_scale', False))
            show_success = bool(self.get_setting('show_success_message', True))
            show_polygon_area = bool(self.get_setting('show_polygon_area_info', True))
            auto_commit = bool(self.get_setting('auto_commit_changes', True))
            handle_edit_mode = bool(self.get_setting('handle_edit_mode_automatically', True))
            rollback_on_error = bool(self.get_setting('rollback_on_error', True))
        except (ValueError, TypeError) as e:
            self.show_error(tr("Error"), tr("Invalid setting values: {error}").format(error=str(e)))
            return
        
        # Extract context elements
        detected_features = context.get('detected_features', [])
        
        if not detected_features:
            self.show_error(tr("Error"), tr("No polygon features found at this location"))
            return
        
        # Get the first (closest) detected feature
        detected_feature = detected_features[0]
        feature = detected_feature.feature
        layer = detected_feature.layer
        
        # Get feature geometry
        geometry = feature.geometry()
        if not geometry or geometry.isEmpty():
            self.show_error(tr("Error"), tr("Feature has no valid geometry"))
            return
        
        # Validate that this is a polygon feature
        if geometry.type() != QgsWkbTypes.PolygonGeometry:
            self.show_error(tr("Error"), tr("This action only works with polygon features"))
            return
        
        # Calculate polygon area if requested
        polygon_area = None
        if show_polygon_area:
            try:
                polygon_area = geometry.area()
            except Exception:
                pass
        
        # Show input dialog
        dialog = ScalePolygonDialog(
            None,
            default_scale=default_scale,
            polygon_area=polygon_area
        )
        
        if dialog.exec_() != QDialog.Accepted:
            return  # User cancelled
        
        # Get the user input scale factor
        scale_factor = dialog.get_scale()
        
        # Confirm scaling if enabled
        if confirm_before_scale:
            confirmation_message = tr("Scale polygon feature ID {fid} from layer '{layer}' by {scale:.2f}x?\n\n").format(fid=feature.id(), layer=layer.name(), scale=scale_factor)
            if show_polygon_area and polygon_area is not None:
                new_area = polygon_area * (scale_factor * scale_factor)
                confirmation_message += tr("Current area: {area:.2f} square map units\n").format(area=polygon_area)
                confirmation_message += tr("New area: {area:.2f} square map units").format(area=new_area)

            if not self.confirm_action(tr("Scale Polygon"), confirmation_message):
                return
        
        # Handle edit mode
        edit_result = None
        was_in_edit_mode = False
        edit_mode_entered = False
        
        if handle_edit_mode:
            edit_result = self.handle_edit_mode(layer, "polygon scaling")
            if edit_result[0] is None:  # Error occurred
                return
            was_in_edit_mode, edit_mode_entered = edit_result
        
        try:
            # Prepare history payload collection for modified geometries
            features_payload = []

            # Get centroid as scaling center point
            centroid = geometry.centroid().asPoint()
            centroid_x = centroid.x()
            centroid_y = centroid.y()
            
            # Create a copy of the geometry for scaling
            scaled_geometry = QgsGeometry(geometry)
            
            # Preserve original geometry for undo backup
            original_geom = None
            try:
                original_geom = QgsGeometry(geometry)
            except Exception:
                original_geom = None

            # Scale the geometry around its centroid by transforming each vertex
            # Try single polygon first, then multipolygon
            try:
                # Try as single polygon first
                polygon = geometry.asPolygon()
                scaled_polygon = []
                
                for ring in polygon:
                    scaled_ring = []
                    for vertex in ring:
                        # Calculate offset from centroid
                        offset_x = vertex.x() - centroid_x
                        offset_y = vertex.y() - centroid_y
                        
                        # Scale the offset
                        scaled_offset_x = offset_x * scale_factor
                        scaled_offset_y = offset_y * scale_factor
                        
                        # Calculate new vertex position
                        new_x = centroid_x + scaled_offset_x
                        new_y = centroid_y + scaled_offset_y
                        scaled_ring.append(QgsPointXY(new_x, new_y))
                    
                    scaled_polygon.append(scaled_ring)
                
                # Create new geometry from scaled vertices
                scaled_geometry = QgsGeometry.fromPolygonXY(scaled_polygon)
            except (TypeError, AttributeError):
                # Handle multipolygon geometry
                try:
                    multi_polygon = geometry.asMultiPolygon()
                    scaled_multi_polygon = []
                    
                    for polygon_part in multi_polygon:
                        scaled_polygon_part = []
                        for ring in polygon_part:
                            scaled_ring = []
                            for vertex in ring:
                                # Calculate offset from centroid
                                offset_x = vertex.x() - centroid_x
                                offset_y = vertex.y() - centroid_y
                                
                                # Scale the offset
                                scaled_offset_x = offset_x * scale_factor
                                scaled_offset_y = offset_y * scale_factor
                                
                                # Calculate new vertex position
                                new_x = centroid_x + scaled_offset_x
                                new_y = centroid_y + scaled_offset_y
                                scaled_ring.append(QgsPointXY(new_x, new_y))
                            
                            scaled_polygon_part.append(scaled_ring)
                        
                        scaled_multi_polygon.append(scaled_polygon_part)
                    
                    # Create new geometry from scaled vertices
                    scaled_geometry = QgsGeometry.fromMultiPolygonXY(scaled_multi_polygon)
                except (TypeError, AttributeError):
                    self.show_error(tr("Error"), tr("Unsupported polygon geometry type"))
                    return
            
            # Update feature geometry
            feature.setGeometry(scaled_geometry)
            if not layer.updateFeature(feature):
                self.show_error(tr("Error"), tr("Failed to update polygon geometry"))
                return

            # Record old/new geometry for history
            try:
                import base64

                old_geom = None
                try:
                    if original_geom is not None:
                        old_wkb = original_geom.asWkb()
                        old_geom = {'wkb_base64': base64.b64encode(old_wkb).decode('utf-8')}
                except Exception:
                    old_geom = None

                new_geom = None
                try:
                    new_wkb = scaled_geometry.asWkb()
                    new_geom = {'wkb_base64': base64.b64encode(new_wkb).decode('utf-8')}
                except Exception:
                    new_geom = None

                features_payload.append({
                    'fid': int(feature.id()),
                    'old_geometry': old_geom,
                    'new_geometry': new_geom
                })
            except Exception:
                pass
            
            # Commit changes if enabled
            if auto_commit and handle_edit_mode:
                if not self.commit_changes(layer, "polygon scaling"):
                    return

            # After commit â€” record history entry for geometry updates if we modified features
            try:
                if features_payload:
                    from ..history_manager import HistoryManager

                    history_layers = [self.create_layer_descriptor(layer)]

                    self.record_to_history(
                        description=f"Scaled feature {feature.id()} in layer {layer.name()} by {scale_factor:.2f}x",
                        undo_type=HistoryManager.UNDO_TYPE_UPDATE_GEOMETRY,
                        can_undo=True,
                        undo_payload=None,
                        layers=history_layers,
                        features=features_payload,
                        meta={
                            'from_crs': layer.crs().authid() if layer.crs().isValid() else '',
                            'to_crs': layer.crs().authid() if layer.crs().isValid() else ''
                        }
                    )
            except Exception:
                pass
            
            # Show success message if enabled
            if show_success:
                success_message = tr("Polygon feature ID {fid} scaled successfully by {scale:.2f}x").format(fid=feature.id(), scale=scale_factor)

                if show_polygon_area and polygon_area is not None:
                    new_area = polygon_area * (scale_factor * scale_factor)
                    success_message += "\n\n" + tr("Original area: {area:.2f} square map units").format(area=polygon_area)
                    success_message += "\n" + tr("New area: {area:.2f} square map units").format(area=new_area)

                self.show_info(tr("Success"), success_message)
            
        except Exception as e:
            self.show_error(tr("Error"), tr("Failed to scale polygon: {error}").format(error=str(e)))
            if rollback_on_error and handle_edit_mode:
                self.rollback_changes(layer)
        
        finally:
            # Exit edit mode if we entered it
            if handle_edit_mode:
                self.exit_edit_mode(layer, edit_mode_entered)


# REQUIRED: Create global instance for automatic discovery
scale_polygon = ScalePolygonAction()


