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

Scales all polygon features in the selected polygon layer by a specified scale factor.
Each polygon is scaled around its own centroid.
"""

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


class ScalePolygonLayerDialog(QDialog):
    """Dialog for user input of scale factor and scaling mode."""
    
    def __init__(self, parent=None, default_scale=1.0, feature_count=None, default_mode='individual', default_scale_objects=True):
        super().__init__(parent)
        self.setWindowTitle(tr("Scale Polygon Layer"))
        self.setModal(True)
        self.resize(400, 250)
        
        layout = QVBoxLayout()
        form_layout = QFormLayout()
        
        # Feature count info
        if feature_count is not None:
            count_label = QLabel(tr("Features in layer: {count}").format(count=feature_count))
            count_label.setStyleSheet("color: gray; font-size: 10px;")
            form_layout.addRow("", count_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)
        
        # Scaling mode selection
        mode_group = QGroupBox(tr("Scaling Mode"))
        mode_layout = QVBoxLayout()
        
        self.individual_radio = QRadioButton(tr("Scale individual objects"))
        self.individual_radio.setToolTip(tr("Each polygon scales around its own centroid"))
        self.layer_radio = QRadioButton(tr("Scale entire layer"))
        self.layer_radio.setToolTip(tr("Scale the layer as a whole around layer centroid (distances between objects are scaled)"))
        
        if default_mode == 'individual':
            self.individual_radio.setChecked(True)
        else:
            self.layer_radio.setChecked(True)
        
        mode_layout.addWidget(self.individual_radio)
        mode_layout.addWidget(self.layer_radio)
        
        # Scale object sizes option (only for layer scaling)
        self.scale_objects_checkbox = QCheckBox(tr("Also scale object sizes"))
        self.scale_objects_checkbox.setToolTip(tr("When scaling the layer, also scale each polygon's geometry around its own centroid"))
        self.scale_objects_checkbox.setChecked(default_scale_objects)
        self.scale_objects_checkbox.setEnabled(False)  # Disabled by default, enabled when layer mode is selected
        
        # Connect radio buttons to enable/disable scale objects option
        def update_scale_objects_enabled():
            self.scale_objects_checkbox.setEnabled(self.layer_radio.isChecked())
        
        self.individual_radio.toggled.connect(lambda checked: update_scale_objects_enabled())
        self.layer_radio.toggled.connect(lambda checked: update_scale_objects_enabled())
        
        mode_layout.addWidget(self.scale_objects_checkbox)
        mode_group.setLayout(mode_layout)
        layout.addWidget(mode_group)
        
        # 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_values(self):
        """Get the input values."""
        return {
            'scale_factor': self.scale_spinbox.value(),
            'mode': 'individual' if self.individual_radio.isChecked() else 'layer',
            'scale_objects': self.scale_objects_checkbox.isChecked() if self.layer_radio.isChecked() else False
        }


class ScalePolygonLayerAction(BaseAction):
    """
    Action to scale all polygon features in a polygon layer by a specified scale factor.
    
    This action scales all polygon features in the selected layer around their own
    centroid by the scale factor specified by the user. Scale factor > 1.0 makes
    polygons larger, scale factor < 1.0 makes them smaller.
    """
    
    def __init__(self):
        """Initialize the action with metadata and configuration."""
        super().__init__()
        
        # Required properties
        self.action_id = "scale_polygon_layer"
        self.name = "Scale Polygon Layer"
        self.category = "Editing"
        self.description = "Scale all polygon features in the selected polygon layer by a specified scale factor. Each polygon is scaled around its own centroid. User inputs scale factor (1.0 = original size, >1.0 = larger, <1.0 = smaller)."
        self.enabled = True
        
        # Action scoping - this works on entire layers
        self.set_action_scope('layer')
        self.set_supported_scopes(['layer'])
        
        # Feature type support - only works with polygon layers
        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,
            },
            'default_scaling_mode': {
                'type': 'choice',
                'default': 'individual',
                'label': 'Default Scaling Mode',
                'description': 'Default scaling mode: "individual" scales each object around its own centroid, "layer" scales the entire layer around layer centroid',
                'options': ['individual', 'layer'],
            },
            'default_scale_objects': {
                'type': 'bool',
                'default': True,
                'label': 'Default: Scale Object Sizes',
                'description': 'When scaling the entire layer, also scale each object\'s geometry by default',
            },
            
            # BEHAVIOR SETTINGS
            'confirm_before_scale': {
                'type': 'bool',
                'default': True,
                'label': 'Confirm Before Scaling',
                'description': 'Show confirmation dialog before scaling all polygons in the layer',
            },
            'show_success_message': {
                'type': 'bool',
                'default': True,
                'label': 'Show Success Message',
                'description': 'Display a message when layer is scaled successfully',
            },
            'show_feature_count': {
                'type': 'bool',
                'default': True,
                'label': 'Show Feature Count',
                'description': 'Display feature count information in dialogs and 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',
            },
            'skip_invalid_geometries': {
                'type': 'bool',
                'default': True,
                'label': 'Skip Invalid Geometries',
                'description': 'Skip features with invalid or empty geometries',
            },
        }
    
    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 _scale_polygon_geometry(self, geometry, scale_factor, center_point=None):
        """
        Scale a polygon geometry around a center point.
        
        Args:
            geometry (QgsGeometry): Polygon geometry to scale
            scale_factor (float): Scale factor to apply
            center_point (QgsPointXY, optional): Center point for scaling. If None, uses geometry's centroid.
            
        Returns:
            QgsGeometry: Scaled geometry, or None if scaling failed
        """
        try:
            # Get center point for scaling
            if center_point is None:
                center_point = geometry.centroid().asPoint()
            
            centroid_x = center_point.x()
            centroid_y = center_point.y()
            
            # Try single polygon first
            try:
                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
                return 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
                    return QgsGeometry.fromMultiPolygonXY(scaled_multi_polygon)
                except (TypeError, AttributeError):
                    return None
        except Exception:
            return None
    
    def execute(self, context):
        """
        Execute the scale polygon layer 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))
            default_mode = str(self.get_setting('default_scaling_mode', 'individual'))
            default_scale_objects = bool(self.get_setting('default_scale_objects', True))
            confirm_before_scale = bool(self.get_setting('confirm_before_scale', True))
            show_success = bool(self.get_setting('show_success_message', True))
            show_feature_count = bool(self.get_setting('show_feature_count', 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))
            skip_invalid = bool(self.get_setting('skip_invalid_geometries', True))
        except (ValueError, TypeError) as e:
            self.show_error("Error", f"Invalid setting values: {str(e)}")
            return
        
        # Extract context elements
        detected_features = context.get('detected_features', [])
        
        if not detected_features:
            self.show_error("Error", "No polygon features found at this location")
            return
        
        # Get the layer from the first detected feature
        detected_feature = detected_features[0]
        layer = detected_feature.layer
        
        # Get feature count
        feature_count = layer.featureCount()
        
        if feature_count == 0:
            self.show_error("Error", "Layer contains no features")
            return
        
        # Show input dialog
        dialog = ScalePolygonLayerDialog(
            None,
            default_scale=default_scale,
            feature_count=feature_count if show_feature_count else None,
            default_mode=default_mode,
            default_scale_objects=default_scale_objects
        )
        
        if dialog.exec_() != QDialog.Accepted:
            return  # User cancelled
        
        # Get the user input values
        values = dialog.get_values()
        scale_factor = values['scale_factor']
        scaling_mode = values['mode']
        scale_objects = values['scale_objects']
        
        # Calculate layer centroid if scaling entire layer
        layer_center = None
        if scaling_mode == 'layer':
            try:
                layer_extent = layer.extent()
                if not layer_extent.isEmpty():
                    layer_center = layer_extent.center()
                else:
                    # Fallback: calculate from features
                    features = list(layer.getFeatures())
                    if features:
                        total_x = 0.0
                        total_y = 0.0
                        count = 0
                        for feature in features:
                            geometry = feature.geometry()
                            if geometry and not geometry.isEmpty():
                                centroid = geometry.centroid().asPoint()
                                total_x += centroid.x()
                                total_y += centroid.y()
                                count += 1
                        if count > 0:
                            layer_center = QgsPointXY(total_x / count, total_y / count)
            except Exception:
                pass
            
            if layer_center is None:
                self.show_error(tr("Error"), tr("Could not calculate layer center point"))
                return
        
        # Confirm scaling if enabled
        if confirm_before_scale:
            if scaling_mode == 'individual':
                confirmation_message = f"Scale all {feature_count} polygon features in layer '{layer.name()}' by {scale_factor:.2f}x?\n\n"
                confirmation_message += "Each polygon will be scaled around its own centroid."
            else:
                confirmation_message = f"Scale entire layer '{layer.name()}' ({feature_count} features) by {scale_factor:.2f}x?\n\n"
                confirmation_message += "Distances between objects will be scaled around the layer centroid."
                if scale_objects:
                    confirmation_message += "\nObject sizes will also be scaled."
                else:
                    confirmation_message += "\nObject sizes will remain unchanged."
            
            if not self.confirm_action("Scale Polygon Layer", 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 layer scaling")
            if edit_result[0] is None:  # Error occurred
                return
            was_in_edit_mode, edit_mode_entered = edit_result
        
        try:
            # Process all features in the layer
            success_count = 0
            error_count = 0
            # Prepare history payload collection for modified geometries
            features_payload = []
            
            for feature in layer.getFeatures():
                geometry = feature.geometry()
                
                if not geometry or geometry.isEmpty():
                    if skip_invalid:
                        error_count += 1
                        continue
                    else:
                        self.show_error("Error", f"Feature {feature.id()} has invalid geometry")
                        if rollback_on_error and handle_edit_mode:
                            self.rollback_changes(layer)
                        return
                
                # Preserve original geometry for undo backup
                original_geom = None
                try:
                    original_geom = QgsGeometry(geometry)
                except Exception:
                    original_geom = None

                # Scale the geometry based on mode
                if scaling_mode == 'individual':
                    # Scale around feature's own centroid
                    scaled_geometry = self._scale_polygon_geometry(geometry, scale_factor)
                else:
                    # Scale around layer center
                    scaled_geometry = self._scale_polygon_geometry(geometry, scale_factor, layer_center)
                    
                    # If also scaling object sizes, scale again around feature's own centroid
                    if scale_objects:
                        scaled_geometry = self._scale_polygon_geometry(scaled_geometry, scale_factor)
                
                if not scaled_geometry:
                    if skip_invalid:
                        error_count += 1
                        continue
                    else:
                        self.show_error("Error", f"Failed to scale feature {feature.id()}")
                        if rollback_on_error and handle_edit_mode:
                            self.rollback_changes(layer)
                        return
                
                # Update feature geometry
                feature.setGeometry(scaled_geometry)
                if not layer.updateFeature(feature):
                    if skip_invalid:
                        error_count += 1
                        continue
                    else:
                        self.show_error("Error", f"Failed to update feature {feature.id()}")
                        if rollback_on_error and handle_edit_mode:
                            self.rollback_changes(layer)
                        return

                # Record successful modification for history (store old + new geometries)
                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

                success_count += 1
            
            # Commit changes if enabled
            if auto_commit and handle_edit_mode:
                if not self.commit_changes(layer, "polygon layer 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 {len(features_payload)} feature(s) 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 = f"Polygon layer scaled successfully by {scale_factor:.2f}x"
                
                if show_feature_count:
                    success_message += f"\n\nFeatures processed: {success_count}"
                    if error_count > 0:
                        success_message += f"\nFeatures skipped: {error_count}"
                
                self.show_info("Success", success_message)
            
        except Exception as e:
            self.show_error("Error", f"Failed to scale polygon layer: {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_layer = ScalePolygonLayerAction()


