import re
from qgis.PyQt.QtCore import QVariant
from qgis.core import (
    QgsProcessing,
    QgsProcessingAlgorithm,
    QgsProcessingParameterVectorLayer,
    QgsProcessingParameterField,
    QgsProcessingParameterFeatureSink,
    QgsProcessingParameterNumber,
    QgsFeature,
    QgsField,
    QgsFields,
    QgsProcessingException,
    QgsFeatureSink,
    QgsProcessingFeatureSourceDefinition
)
import processing

class ShorelineDataPrepFinal(QgsProcessingAlgorithm):
    BEACH_BOUNDARY = 'BEACH_BOUNDARY'
    BASELINE = 'BASELINE'
    SHORELINES = 'SHORELINES'
    DATE_FIELD = 'DATE_FIELD'
    SEGMENT_LENGTH = 'SEGMENT_LENGTH'
    OUTPUT_BASELINE = 'OUTPUT_BASELINE'
    OUTPUT_SHORELINES = 'OUTPUT_SHORELINES'

    def name(self): return 'shoreline_data_prep_fixed'
    def displayName(self): return '0. Preparation: Clip & Split Baseline'
    def group(self): return 'Shoreline Analysis'
    def groupId(self): return 'shoreline'
    def createInstance(self): return ShorelineDataPrepFinal()

    def shortHelpString(self):
        return (
            "<h3>Step 1 — Clip &amp; split baseline</h3>"
            "<p>Select one feature in the beach boundary layer, then run. Clips the "
            "baseline and shorelines to that polygon, splits the baseline into fixed-"
            "length segments and numbers them in a new <code>longi</code> field "
            "(1…N along the line), and adds a normalized <code>date_clean</code> "
            "field (YYYYMMDD) to the shorelines.</p>"
        )

    def initAlgorithm(self, config=None):
        self.addParameter(QgsProcessingParameterVectorLayer(
            self.BEACH_BOUNDARY, 'Beach Boundary Layer (SELECT a feature first!)',
            [QgsProcessing.TypeVectorPolygon]))

        self.addParameter(QgsProcessingParameterVectorLayer(
            self.BASELINE, 'Baseline Layer',
            [QgsProcessing.TypeVectorLine]))

        self.addParameter(QgsProcessingParameterVectorLayer(
            self.SHORELINES, 'Shorelines Layer',
            [QgsProcessing.TypeVectorLine]))

        self.addParameter(QgsProcessingParameterField(
            self.DATE_FIELD, 'Date Field (in Shorelines)',
            'any', self.SHORELINES))

        self.addParameter(QgsProcessingParameterNumber(
            self.SEGMENT_LENGTH, 'Baseline Segment Length (meters)',
            QgsProcessingParameterNumber.Double, 50.0, False, 1.0))

        self.addParameter(QgsProcessingParameterFeatureSink(
            self.OUTPUT_BASELINE, 'Prepared Baseline (Split & Numbered)'))

        self.addParameter(QgsProcessingParameterFeatureSink(
            self.OUTPUT_SHORELINES, 'Prepared Shorelines (Clean Date)'))

    def processAlgorithm(self, parameters, context, feedback):
        boundary = self.parameterAsVectorLayer(parameters, self.BEACH_BOUNDARY, context)
        baseline = self.parameterAsVectorLayer(parameters, self.BASELINE, context)
        shorelines = self.parameterAsVectorLayer(parameters, self.SHORELINES, context)
        date_f_name = self.parameterAsString(parameters, self.DATE_FIELD, context)
        segment_length = self.parameterAsDouble(parameters, self.SEGMENT_LENGTH, context)

        if not boundary or not baseline or not shorelines:
            raise QgsProcessingException("Missing input layers!")

        # ОРИГІНАЛЬНИЙ МЕТОД ОБРІЗКИ: використовуємо тільки ВИДІЛЕНИЙ полігон
        boundary_source = QgsProcessingFeatureSourceDefinition(boundary.id(), True)

        # --------------------------------------------------
        # 1. ОБРОБКА БАЗОВОЇ ЛІНІЇ
        # --------------------------------------------------
        feedback.pushInfo("Clipping Baseline by original method...")
        bl_clipped = processing.run("native:clip", {
            'INPUT': baseline,
            'OVERLAY': boundary_source,
            'OUTPUT': 'memory:'
        }, context=context, feedback=feedback)['OUTPUT']

        feedback.pushInfo(f"Splitting Baseline into {segment_length}m segments...")
        bl_split = processing.run("native:splitlinesbylength", {
            'INPUT': bl_clipped,
            'LENGTH': segment_length,
            'OUTPUT': 'memory:'
        }, context=context, feedback=feedback)['OUTPUT']

        # Створення атрибуту longi та нумерація
        bl_fields = QgsFields()
        bl_fields.append(QgsField('longi', QVariant.Int))

        (bl_sink, bl_id) = self.parameterAsSink(parameters, self.OUTPUT_BASELINE, context,
                                               bl_fields, bl_split.wkbType(), bl_split.sourceCrs())

        segment_id = 1
        for f in bl_split.getFeatures():
            new_f = QgsFeature(bl_fields)
            new_f.setGeometry(f.geometry())
            new_f['longi'] = segment_id
            bl_sink.addFeature(new_f, QgsFeatureSink.FastInsert)
            segment_id += 1

        # --------------------------------------------------
        # 2. ОБРОБКА БЕРЕГОВИХ ЛІНІЙ
        # --------------------------------------------------
        feedback.pushInfo("Clipping Shorelines by original method...")
        sh_clipped = processing.run("native:clip", {
            'INPUT': shorelines,
            'OVERLAY': boundary_source,
            'OUTPUT': 'memory:'
        }, context=context, feedback=feedback)['OUTPUT']

        feedback.pushInfo("Cleaning Shorelines dates...")
        new_fields = sh_clipped.fields()
        if 'date_clean' not in new_fields.names():
            new_fields.append(QgsField('date_clean', QVariant.String, len=12))

        (sh_sink, sh_id) = self.parameterAsSink(parameters, self.OUTPUT_SHORELINES, context,
                                               new_fields, sh_clipped.wkbType(), sh_clipped.sourceCrs())

        for f in sh_clipped.getFeatures():
            new_f = QgsFeature(new_fields)
            new_f.setGeometry(f.geometry())

            for attr_name in f.fields().names():
                new_f[attr_name] = f[attr_name]

            raw_val = str(f[date_f_name]) if date_f_name in f.fields().names() else ""
            new_f['date_clean'] = self.clean_date(raw_val)

            sh_sink.addFeature(new_f, QgsFeatureSink.FastInsert)

        return {self.OUTPUT_BASELINE: bl_id, self.OUTPUT_SHORELINES: sh_id}

    def clean_date(self, text):
        if not text:
            return "19840101"

        # Формат 8 цифр підряд (напр. 20210604)
        match = re.search(r'(\d{8})', text)
        if match:
            return match.group(1)

        # Формат з роздільниками (напр. 2021-06-04 або 2021.06.04)
        match2 = re.search(r'(\d{4})[-/.](\d{2})[-/.](\d{2})', text)
        if match2:
            return f"{match2.group(1)}{match2.group(2)}{match2.group(3)}"

        return "19840101"
