import csv
import os
import re
import time
import traceback
from datetime import datetime

from qgis import processing
from qgis.PyQt.QtCore import QVariant, QThread
from qgis.PyQt.QtWidgets import QFileDialog
from qgis._core import QgsMessageLog, Qgis
from qgis.core import QgsFeatureRequest, QgsMapLayer, QgsWkbTypes,  \
     QgsDistanceArea, QgsProcessingContext, QgsSpatialIndex
from qgis.utils import iface
from shapely.wkb import loads as wkb_loads
from qgis.PyQt import QtWidgets, uic, QtCore
from qgis.PyQt.QtCore import pyqtSignal

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


class cyanlove_calc_intersects_geometry(QtWidgets.QDockWidget, FORM_CLASS):
    closingPlugin = pyqtSignal()

    def __init__(self, parent=None):
        super(cyanlove_calc_intersects_geometry, self).__init__(parent)

        self.export_thread = None
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.setupUi(self)
        self.setFloating(True)
        self.property_dict = None  # 存储搜索出来的名称，图元中心经纬度
        self.is_running = False  # 线程状态标志
        self.setWindowFlags(QtCore.Qt.Dialog)  # 使对话框始终在前
        # 设置为模态窗口
        # self.setWindowModality(QtCore.Qt.ApplicationModal)  # 或者使用 Qt.WindowModal
        self.btn_calc_intersects_geometry.clicked.connect(self.calc_intersects_geometry)

    def calc_intersects_geometry(self):
        options = QFileDialog.Options()  # 创建文件对话框选项
        options |= QFileDialog.ReadOnly  # 可以设置一些选项，例如只读
        csv_file_path, _ = QFileDialog.getSaveFileName(self,
                                                       "选择文件",
                                                       "",
                                                       "CSV文件 (*.csv);;所有文件 (*)",  # 可以添加其他过滤器
                                                       options=options)  # 传递 options 参数
        # 检查用户是否选择了文件
        if csv_file_path:
            current_layer1 = self.mMapLayerComboBox.currentLayer()
            current_layer2 = self.mMapLayerComboBox_2.currentLayer()
            geom_type1 = current_layer1.wkbType()
            geom_type2 = current_layer2.wkbType()
            if geom_type1 in [QgsWkbTypes.Point, QgsWkbTypes.MultiPoint,
                              QgsWkbTypes.Polygon, QgsWkbTypes.MultiPolygon,
                              QgsWkbTypes.LineString, QgsWkbTypes.MultiLineString] and \
               geom_type2 in [QgsWkbTypes.Point, QgsWkbTypes.MultiPoint,
                              QgsWkbTypes.Polygon, QgsWkbTypes.MultiPolygon,
                              QgsWkbTypes.LineString, QgsWkbTypes.MultiLineString]:
                # 确保使用现有的进度条
                self.progressBar.setRange(0, 100)  # 设置进度条范围
                self.progressBar.setValue(0)  # 初始化进度条值
                self.progressBar.show()  # 显示进度条 (如果之前是隐藏状态)
                # 启动导出线程
                self.export_thread = ExportThread(current_layer1, current_layer2, csv_file_path)
                self.export_thread.progress.connect(self.update_progress)  # 连接信号到槽
                self.export_thread.updatelabel.connect(self.update_label)
                self.export_thread.finished.connect(self.export_finished)  # 导出完成后连接到槽
                self.export_thread.error_occurred.connect(self.on_error)  # 新增
                self.export_thread.start()  # 启动线程

    def update_label(self, value):
        self.label_show.setText(value)  # 更新值

    def update_progress(self, value):
        self.progressBar.setValue(value)  # 更新进度条的值

    def export_finished(self):
        self.progressBar.setValue(100)

    def on_error(self, error_msg):
        self.label_show.setText("计算过程中出现错误，请查看消息日志。")
        QgsMessageLog.logMessage(error_msg, "插件调试", level=Qgis.Critical)
        print(error_msg)

class ExportThread(QThread):
    progress = pyqtSignal(int)
    updatelabel = pyqtSignal(str)
    error_occurred = pyqtSignal(str)   # 新增错误信号

    def __init__(self, selected_layer1, selected_layer2, csv_file_path):
        super().__init__()
        self.layer1 = selected_layer1   # 第一个图层（通常是多边形）
        self.layer2 = selected_layer2   # 第二个图层（通常是点）
        self.csv_file_path = csv_file_path

    def _log_error(self, msg):
        """记录错误到日志和控制台，并发射错误信号"""
        full_msg = f"{msg}\n{traceback.format_exc()}"
        print(full_msg)
        self.error_occurred.emit(full_msg)

    def _qgsgeom_to_shapely(self, qgs_geom):
        """将 QgsGeometry 转换为 Shapely 几何，失败时返回 None"""
        try:
            if qgs_geom is None or qgs_geom.isEmpty():
                return None
            wkb = qgs_geom.asWkb()
            if wkb is None or len(wkb) == 0:
                return None
            wkb_bytes = bytes(wkb)
            if not wkb_bytes:
                return None
            return wkb_loads(wkb_bytes)
        except Exception as e:
            self._log_error(f"转换 QgsGeometry 到 Shapely 失败: {e}")
            return None

    def run(self):
        should_stop = False
        try:
            start_time = time.time()
            self.updatelabel.emit("正在构建第一个图层的空间索引...")
            # 为第一个图层建立空间索引（用于快速候选）
            spatial_index = QgsSpatialIndex(self.layer1.getFeatures())

            field_names1 = [f.name() for f in self.layer1.fields()]
            field_names2 = [f.name() for f in self.layer2.fields()]
            header = field_names1 + field_names2

            total_features2 = self.layer2.featureCount()
            if total_features2 == 0:
                self.updatelabel.emit("第二个图层没有要素，无需计算。")
                self.progress.emit(100)
                return

            # 预提取第一个图层的所有要素（几何 + 属性 + ID）
            layer1_features = []
            for feat in self.layer1.getFeatures():
                geom = feat.geometry()
                if geom and not geom.isEmpty():
                    shapely_geom = self._qgsgeom_to_shapely(geom)
                    if shapely_geom is None:
                        self._log_error(f"第一个图层要素 ID {feat.id()} 转换失败，中止计算。")
                        should_stop = True
                        break
                else:
                    shapely_geom = None
                attrs = {f: str(feat[f]) for f in field_names1}
                layer1_features.append((shapely_geom, attrs, feat.id()))

            if should_stop:
                self.updatelabel.emit("预提取第一个图层要素时出错，已中止。")
                return

            self.updatelabel.emit(f"开始处理 {total_features2} 个要素（第二个图层）...")

            with open(self.csv_file_path, mode='w', newline='', encoding='gbk') as csv_file:
                writer = csv.writer(csv_file)
                writer.writerow(header)

                last_progress = -1
                # ----- 外层遍历第二个图层 -----
                for idx, feature2 in enumerate(self.layer2.getFeatures()):
                    if should_stop:
                        break

                    try:
                        geom2 = feature2.geometry()
                        if geom2 is None or geom2.isEmpty():
                            # 无效几何，跳过（不输出）
                            progress = int((idx + 1) / total_features2 * 100)
                            if progress != last_progress:
                                self.progress.emit(progress)
                                last_progress = progress
                            continue

                        # 将第二个图层的几何转为 Shapely
                        shapely_geom2 = self._qgsgeom_to_shapely(geom2)
                        if shapely_geom2 is None:
                            self._log_error(f"第二个图层要素 ID {feature2.id()} 转换失败，中止计算。")
                            should_stop = True
                            break

                        # 使用空间索引获取可能相交的第一个图层要素ID
                        candidate_ids = spatial_index.intersects(geom2.boundingBox())
                        found = False

                        # 遍历候选要素
                        for shapely_geom1, attrs1, fid1 in layer1_features:
                            if fid1 not in candidate_ids or shapely_geom1 is None:
                                continue
                            try:
                                if shapely_geom1.intersects(shapely_geom2):
                                    # 找到一个相交组合，写入一行
                                    row1 = [attrs1[f] for f in field_names1]
                                    row2 = [str(feature2[f]) for f in field_names2]
                                    writer.writerow(row1 + row2)
                                    found = True
                            except Exception as e:
                                self._log_error(f"相交判断出错 (第二个图层要素 {feature2.id()}, 第一个图层要素 {fid1}): {e}")
                                should_stop = True
                                break

                        if should_stop:
                            break

                        # 更新进度（基于已处理第二个图层要素数）
                        progress = int((idx + 1) / total_features2 * 100)
                        if progress != last_progress:
                            self.progress.emit(progress)
                            last_progress = progress

                    except Exception as e:
                        self._log_error(f"处理第二个图层要素 ID {feature2.id()} 时发生严重错误: {e}")
                        should_stop = True
                        break

            if should_stop:
                self.updatelabel.emit("计算因错误而中止，请查看消息日志。")
                self.error_occurred.emit("计算过程中发生错误，已终止。")
            else:
                end_time = time.time()
                execution_time = end_time - start_time
                self.updatelabel.emit(f"计算完成！耗时 {execution_time:.2f} 秒，共处理 {total_features2} 个要素。")
                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.error_occurred.emit(error_msg)


