"""
Create Buffer Around Point Layer

Creates buffers around all point features in the clicked layer and writes them to a new polygon layer.
"""

from .base_action import BaseAction
from qgis.core import (
    QgsVectorLayer, QgsProject, QgsFeature, QgsGeometry, QgsField,
    QgsCoordinateTransform, QgsCoordinateReferenceSystem, QgsWkbTypes
)
from qgis.PyQt.QtCore import QVariant
from qgis.PyQt.QtWidgets import QInputDialog
from ..i18n import tr


class CreateBufferAroundPointLayerAction(BaseAction):
    def __init__(self):
        super().__init__()
        self.action_id = "create_buffer_around_point_layer"
        self.name = "Create Buffer Around Point Layer"
        self.category = "Geometry"
        self.description = "Create buffers around all points in the clicked point layer."
        self.enabled = True

        self.set_action_scope('layer')
        self.set_supported_scopes(['layer'])

        self.set_supported_click_types(['point', 'multipoint'])
        self.set_supported_geometry_types(['point', 'multipoint'])

    def get_settings_schema(self):
        return {
            'buffer_distance': {'type': 'float', 'default': 100.0},
            'buffer_segments': {'type': 'int', 'default': 8},
            'layer_storage_type': {'type': 'choice', 'default': 'temporary', 'options': ['temporary', 'permanent']},
            'output_layer_name': {'type': 'str', 'default': 'Point Buffers'},
            'add_to_project': {'type': 'bool', 'default': True},
            'zoom_to_result': {'type': 'bool', 'default': True}
        }

    def supports_undo(self) -> bool:
        return True

    def get_undo_category(self) -> str:
        return 'payload'

    def execute(self, context):
        try:
            default_buffer = float(self.get_setting('buffer_distance', 100.0))
            segments = int(self.get_setting('buffer_segments', 8))
            out_name = str(self.get_setting('output_layer_name', 'Point Buffers'))
            add_to_project = bool(self.get_setting('add_to_project', True))
            zoom_to_result = bool(self.get_setting('zoom_to_result', True))
        except (ValueError, TypeError) as e:
            self.show_error(tr("Error"), tr("Invalid setting values: {error}").format(error=str(e)))
            return

        layer = context.get('layer')
        if not isinstance(layer, QgsVectorLayer):
            self.show_error(tr("Error"), tr("Target layer is not a vector layer"))
            return

        # Ask user for buffer distance each time
        buffer_distance, ok = QInputDialog.getDouble(
            None,
            tr("Buffer Distance"),
            tr("Enter buffer distance for layer '{layer_name}' (map units):").format(layer_name=layer.name()),
            default_buffer,
            0.0,
            1e12,
            3
        )
        if not ok:
            return

        # Collect features
        features = list(layer.getFeatures())
        if not features:
            self.show_warning(tr("No features"), tr("The selected layer contains no features"))
            return

        # Calculation CRS if needed
        layer_crs = layer.crs()
        calc_crs = QgsCoordinateReferenceSystem('EPSG:3857') if layer_crs.isGeographic() else None

        # Create output layer
        crs_auth = layer.crs().authid() if layer.crs().isValid() else ''
        uri = f"Polygon?crs={crs_auth}" if crs_auth else "Polygon"
        out_layer = QgsVectorLayer(uri, out_name, "memory")
        if not out_layer.isValid():
            self.show_error(tr("Error"), tr("Failed to create output layer"))
            return

        out_layer.dataProvider().addAttributes([QgsField('source_fid', QVariant.Int)])
        out_layer.updateFields()

        out_feats = []
        for f in features:
            geom = QgsGeometry(f.geometry())
            if calc_crs:
                try:
                    transform_to_calc = QgsCoordinateTransform(layer_crs, calc_crs, QgsProject.instance())
                    geom.transform(transform_to_calc)
                except Exception:
                    pass

            buf = geom.buffer(buffer_distance, segments)
            if buf is None or buf.isEmpty():
                continue

            if calc_crs:
                try:
                    rev = QgsCoordinateTransform(calc_crs, layer_crs, QgsProject.instance())
                    buf.transform(rev)
                except Exception:
                    pass

            newf = QgsFeature(out_layer.fields())
            newf.setGeometry(buf)
            newf.setAttribute('source_fid', int(f.id()))
            out_feats.append(newf)

        if not out_feats:
            self.show_error(tr("Error"), tr("No buffers created"))
            return

        ok, added = out_layer.dataProvider().addFeatures(out_feats)
        if not ok:
            self.show_error(tr("Error"), tr("Failed to add buffered features to output layer"))
            return

        out_layer.updateExtents()
        if add_to_project:
            QgsProject.instance().addMapLayer(out_layer)

        if zoom_to_result:
            try:
                canvas = context.get('canvas')
                if canvas:
                    canvas.setExtent(out_layer.extent())
                    canvas.refresh()
            except Exception:
                pass

        # Record history with full layer definition for redo
        created = [self.create_feature_backup(f, out_layer) for f in out_layer.getFeatures()]

        fields = []
        try:
            for field in out_layer.fields():
                fields.append({'name': field.name(), 'qmeta_type': field.type(), 'length': field.length(), 'precision': field.precision()})
        except Exception:
            pass

        layer_def = {
            'layer_name': out_layer.name(),
            'crs': out_layer.crs().authid() if out_layer.crs().isValid() else '',
            'geometry_type': QgsWkbTypes.displayString(out_layer.wkbType()),
            'fields': fields,
            'features': created
        }

        self.record_to_history(
            description=tr("Created {count} buffers for layer {layer_name}").format(count=len(created), layer_name=layer.name()),
            undo_type='create_layer',
            can_undo=True,
            undo_payload={'layer_definitions': [layer_def]},
            layers=[self.create_layer_descriptor(out_layer)],
            features=created
        )


# global instance
create_buffer_around_point_layer = CreateBufferAroundPointLayerAction()
