# -*- coding: utf-8 -*-
"""QThread workers for TopoFix — check and fix in background.

Author: Loc Vu Trung tong hop vi dam me cung ban!
"""
import os
import shutil
import tempfile

from qgis.PyQt.QtCore import QThread, pyqtSignal


class CheckWorker(QThread):
    """Run topology checks in background thread."""
    progress = pyqtSignal(int, str)
    finished = pyqtSignal(dict)
    error = pyqtSignal(str)

    def __init__(self, layer, checkers, params):
        super().__init__()
        self.layer = layer
        self.checkers = checkers
        self.params = params
        self._cancelled = False

    def run(self):
        results = {}
        total = len(self.checkers)
        for i, checker in enumerate(self.checkers):
            if self._cancelled:
                return
            pct = int(((i + 0.5) / total) * 100)  # mid-step progress
            self.progress.emit(pct, checker.display_name_en)
            try:
                result = checker.check(self.layer, self.params)
                results[checker.name] = result
            except Exception as e:
                # Log error but continue to next checker
                results[checker.name] = {
                    "error_geometries": [],
                    "error_features": [],
                    "details": [f"Skipped: {e}"]
                }
        self.progress.emit(100, "Done")
        self.finished.emit(results)

    def cancel(self):
        self._cancelled = True


class FixWorker(QThread):
    """Run topology fixes sequentially in background thread.

    Pipeline: each fixer reads from input file, writes to output file.
    Output of step N becomes input of step N+1.
    Final result is emitted as file path.
    """
    progress = pyqtSignal(int, str)
    finished = pyqtSignal(str)
    error = pyqtSignal(str)

    def __init__(self, input_path, fixers, params):
        super().__init__()
        self.input_path = input_path
        self.fixers = fixers
        self.params = params
        self._cancelled = False
        self._tmp_dir = tempfile.mkdtemp(prefix="topofix_")

    def run(self):
        # Copy source to first input
        current = os.path.join(self._tmp_dir, "step_input.gpkg")
        try:
            from qgis.core import QgsVectorLayer, QgsVectorFileWriter
            src_layer = QgsVectorLayer(self.input_path, "src", "ogr")
            if not src_layer.isValid():
                self.error.emit(f"Cannot read: {self.input_path}")
                return
            opts = QgsVectorFileWriter.SaveVectorOptions()
            opts.driverName = "GPKG"
            err, msg = QgsVectorFileWriter.writeAsVectorFormatV3(
                src_layer, current, src_layer.transformContext(), opts)
            if err != QgsVectorFileWriter.WriterError.NoError:
                self.error.emit(f"Copy failed: {msg}")
                return
        except Exception as e:
            self.error.emit(f"Init: {e}")
            return

        total = len(self.fixers)
        for i, fixer in enumerate(self.fixers):
            if self._cancelled:
                self._cleanup()
                return
            pct = int((i / total) * 100)
            self.progress.emit(pct, f"Fixing: {fixer.display_name_en}")
            step_out = os.path.join(
                self._tmp_dir, f"step_{i}_out.gpkg")
            try:
                fixer.fix(current, step_out, self.params)
                if os.path.exists(step_out):
                    current = step_out
                # else: fixer didn't produce output, keep current
            except Exception as e:
                self.error.emit(
                    f"{fixer.name}: {e}")
                # Continue with current file

        self.progress.emit(100, "Done")
        self.finished.emit(current)

    def cleanup(self):
        """Remove temp dir after layer is loaded."""
        # Don't cleanup immediately — QGIS needs the file
        pass  # nosec B110

    def cancel(self):
        self._cancelled = True

    def _cleanup(self):
        try:
            shutil.rmtree(self._tmp_dir, ignore_errors=True)
        except Exception:  # nosec B110
            pass  # nosec B110
