# -*- coding: utf-8 -*-
import os
import traceback
import time
from qgis.PyQt import uic
from qgis.PyQt.QtWidgets import QDockWidget, QMessageBox
from qgis.PyQt.QtCore import Qt, QThread, pyqtSignal, QVariant
from qgis.core import (
    QgsProject, QgsSpatialIndex, QgsField, QgsFeature,
    QgsVectorLayer, QgsMessageLog, Qgis,
    QgsCoordinateTransform, QgsCoordinateReferenceSystem,
    QgsWkbTypes, QgsFields
)
from qgis.utils import iface

FORM_CLASS, _ = uic.loadUiType(os.path.join(
    os.path.dirname(__file__), 'cyanlove_calc_distince_les_xmeter_polygon_base.ui'))


class cyanlove_calc_distince_les_xmeter_polygon(QDockWidget, FORM_CLASS):
    closingPlugin = pyqtSignal()

    def __init__(self, parent=None):
        super(cyanlove_calc_distince_les_xmeter_polygon, self).__init__(parent)
        self.setupUi(self)
        self.iface = iface
        self.canvas = iface.mapCanvas()

        self.setFloating(True)
        self.setWindowFlags(Qt.Dialog)

        self.btn_merge_and_split.clicked.connect(self.run_clustering)

        self.progressBar.setRange(0, 100)
        self.progressBar.setValue(0)
        self.label_show.setText("就绪，请选择图层并设置参数。")

        self.cluster_thread = None

    def run_clustering(self):
        layer = self.mMapLayerComboBox.currentLayer()
        if not layer or not isinstance(layer, QgsVectorLayer):
            self.label_show.setText("❌ 请选择一个有效的矢量图层")
            return

        try:
            distance_text = self.lineEdit_distance.text().strip()
            if not distance_text:
                raise ValueError("距离阈值不能为空")
            distance = float(distance_text)
            if distance <= 0:
                raise ValueError("距离阈值必须大于0")
        except ValueError as e:
            self.label_show.setText(f"❌ 距离阈值无效: {e}")
            return

        prefix = self.lineEdit_prefix.text().strip()
        output_field = self.lineEdit_output_field.text().strip()
        if not output_field:
            output_field = "cluster_id"

        # 坐标系处理
        src_crs = layer.crs()
        transform = None
        if src_crs.isGeographic():
            reply = QMessageBox.information(
                self, "坐标系转换",
                "当前图层使用地理坐标系（度），将自动转换为 Web Mercator (EPSG:3857) 进行距离计算。\n"
                "计算结果将写入新图层，原始图层不会被修改。\n是否继续？",
                QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes
            )
            if reply == QMessageBox.No:
                return
            target_crs = QgsCoordinateReferenceSystem("EPSG:3857")
            transform = QgsCoordinateTransform(src_crs, target_crs, QgsProject.instance())
            self.label_show.setText("⏳ 将使用 EPSG:3857 投影计算距离")
        else:
            self.label_show.setText("⏳ 图层已是投影坐标系，直接计算")

        new_layer_name = f"{layer.name()}_clustered"

        # 若已有同名图层则移除
        existing = QgsProject.instance().mapLayersByName(new_layer_name)
        if existing:
            QgsProject.instance().removeMapLayer(existing[0].id())

        self.btn_merge_and_split.setEnabled(False)
        self.progressBar.setValue(0)

        self.cluster_thread = ClusteringThread(
            layer, distance, prefix, output_field,
            transform, new_layer_name
        )
        self.cluster_thread.progress.connect(self.update_progress)
        self.cluster_thread.updatelabel.connect(self.update_label)
        self.cluster_thread.finished.connect(self.on_thread_finished)
        self.cluster_thread.error_occurred.connect(self.on_thread_error)
        self.cluster_thread.clustering_done.connect(self.create_result_layer)
        self.cluster_thread.start()

    def update_progress(self, value):
        self.progressBar.setValue(value)

    def update_label(self, text):
        self.label_show.setText(text)

    def on_thread_finished(self):
        self.btn_merge_and_split.setEnabled(True)

    def on_thread_error(self, error_msg):
        self.btn_merge_and_split.setEnabled(True)
        self.label_show.setText("❌ 计算过程中出现错误，请查看消息日志。")
        QgsMessageLog.logMessage(error_msg, "距离聚类工具", level=Qgis.Critical)
        QMessageBox.critical(self, "错误", f"聚类失败，请查看日志。\n{error_msg}")

    def create_result_layer(self, feature_ids, labels, output_field, new_layer_name):
        layer = self.mMapLayerComboBox.currentLayer()
        if not layer:
            return

        try:
            fields = QgsFields()
            for field in layer.fields():
                fields.append(field)
            # 修改点：使用新式 QgsField 构造函数
            fields.append(QgsField(output_field, QVariant.String, "string", 50))

            wkb_type = layer.wkbType()
            crs = layer.crs()
            new_layer = QgsVectorLayer(f"{QgsWkbTypes.displayString(wkb_type)}?crs={crs.authid()}", new_layer_name, "memory")
            if not new_layer.isValid():
                raise Exception("无法创建内存图层")

            provider = new_layer.dataProvider()
            provider.addAttributes(fields)
            new_layer.updateFields()

            label_dict = {fid: label for fid, label in zip(feature_ids, labels)}

            new_layer.startEditing()
            for feat in layer.getFeatures():
                new_feat = QgsFeature(fields)
                new_feat.setGeometry(feat.geometry())
                attrs = feat.attributes()
                new_feat.setAttributes(attrs)
                label = label_dict.get(feat.id(), "")
                new_feat[output_field] = label
                provider.addFeature(new_feat)

            new_layer.commitChanges()
            QgsProject.instance().addMapLayer(new_layer)

            self.iface.mapCanvas().refresh()
            self.label_show.setText(
                f"✅ 成功创建图层 '{new_layer_name}'，共 {len(feature_ids)} 个要素，{len(set(labels))} 个簇"
            )
            QMessageBox.information(
                self, "完成",
                f"聚类完成！\n新图层 '{new_layer_name}' 已添加到地图。\n"
                f"共 {len(set(labels))} 个簇，簇编号字段为 '{output_field}'。"
            )

        except Exception as e:
            error_msg = f"创建新图层失败: {str(e)}\n{traceback.format_exc()}"
            QgsMessageLog.logMessage(error_msg, "距离聚类工具", level=Qgis.Critical)
            QMessageBox.critical(self, "错误", f"创建新图层失败: {str(e)}")

    def closeEvent(self, event):
        if self.cluster_thread and self.cluster_thread.isRunning():
            self.cluster_thread.terminate()
            self.cluster_thread.wait()
        event.accept()


class ClusteringThread(QThread):
    progress = pyqtSignal(int)
    updatelabel = pyqtSignal(str)
    error_occurred = pyqtSignal(str)
    clustering_done = pyqtSignal(list, list, str, str)

    def __init__(self, layer, distance, prefix, output_field, transform, new_layer_name):
        super().__init__()
        self.layer = layer
        self.distance = distance
        self.prefix = prefix
        self.output_field = output_field
        self.transform = transform
        self.new_layer_name = new_layer_name

    def _log_error(self, msg):
        full_msg = f"{msg}\n{traceback.format_exc()}"
        print(full_msg)
        self.error_occurred.emit(full_msg)

    def run(self):
        start_time = time.time()
        layer = self.layer
        distance = self.distance
        prefix = self.prefix
        output_field = self.output_field
        transform = self.transform

        try:
            self.updatelabel.emit("📦 正在读取要素...")
            features = []
            geoms_proj = []
            for feat in layer.getFeatures():
                geom = feat.geometry()
                if geom is None or geom.isEmpty():
                    continue
                if transform is not None:
                    try:
                        geom_proj = geom
                        geom_proj.transform(transform)
                        if geom_proj.isEmpty():
                            continue
                    except Exception:
                        continue
                else:
                    geom_proj = geom
                features.append(feat)
                geoms_proj.append(geom_proj)

            n = len(features)
            if n == 0:
                self.updatelabel.emit("❌ 没有有效要素（可能转换后几何为空）")
                self.progress.emit(100)
                return

            self.updatelabel.emit(f"📦 共加载 {n} 个有效要素")

            id_to_idx = {f.id(): idx for idx, f in enumerate(features)}
            parent = list(range(n))

            def find(x):
                while parent[x] != x:
                    parent[x] = parent[parent[x]]
                    x = parent[x]
                return x

            def union(x, y):
                rx, ry = find(x), find(y)
                if rx != ry:
                    parent[ry] = rx

            self.updatelabel.emit("🔍 构建空间索引...")
            spatial_index = QgsSpatialIndex()
            for i, geom in enumerate(geoms_proj):
                spatial_index.addFeature(features[i].id(), geom.boundingBox())

            self.updatelabel.emit("🔍 开始空间聚类...")
            pair_count = 0
            total = n
            step = max(1, total // 100)

            for i in range(n):
                geom_i = geoms_proj[i]
                if geom_i is None or geom_i.isEmpty():
                    continue

                search_geom = geom_i.buffer(distance, 5)
                if search_geom is None or search_geom.isEmpty():
                    continue

                candidate_ids = spatial_index.intersects(search_geom.boundingBox())

                for fid in candidate_ids:
                    if fid == features[i].id():
                        continue
                    j = id_to_idx.get(fid)
                    if j is None or j <= i:
                        continue

                    geom_j = geoms_proj[j]
                    if geom_j is None or geom_j.isEmpty():
                        continue

                    dist = geom_i.distance(geom_j)
                    if dist <= distance:
                        union(i, j)
                        pair_count += 1

                if (i + 1) % step == 0 or (i + 1) == total:
                    progress = int((i + 1) / total * 100)
                    self.progress.emit(progress)
                    self.updatelabel.emit(
                        f"⏳ 处理中: {i+1}/{total} 要素, 已合并 {pair_count} 对"
                    )

            self.updatelabel.emit(
                f"✅ 空间索引遍历完成，共合并 {pair_count} 对邻近要素"
            )

            cluster_map = {}
            cluster_id = 0
            for i in range(n):
                root = find(i)
                if root not in cluster_map:
                    cluster_map[root] = cluster_id
                    cluster_id += 1

            if prefix:
                cluster_labels = {root: f"{prefix}{cid + 1}" for root, cid in cluster_map.items()}
            else:
                cluster_labels = {root: str(cid + 1) for root, cid in cluster_map.items()}

            self.updatelabel.emit(f"📊 共生成 {cluster_id} 个簇")

            feature_ids = [f.id() for f in features]
            label_list = [cluster_labels[find(i)] for i in range(n)]

            self.clustering_done.emit(feature_ids, label_list, output_field, self.new_layer_name)

            elapsed = time.time() - start_time
            self.updatelabel.emit(
                f"⏱️ 聚类计算完成，耗时 {elapsed:.2f} 秒，正在创建新图层..."
            )
            self.progress.emit(100)

        except Exception as e:
            error_msg = f"聚类过程发生异常: {str(e)}\n{traceback.format_exc()}"
            self._log_error(error_msg)
            self.updatelabel.emit("❌ 计算失败，请查看日志")
            self.progress.emit(0)