# -*- coding: utf-8 -*-
"""Must-Not-Overlap — smart cut keeping larger polygon.

Uses intersects() + area threshold to detect ALL overlap types.
Fix: cuts overlap area from the SMALLER polygon to preserve data.

Author: Loc Vu Trung tong hop vi dam me cung ban!
"""
from .base import BaseChecker


class MustNotOverlapChecker(BaseChecker):
    name = "must_not_overlap"
    display_name_en = "Must Not Overlap"
    display_name_vi = "Cat thong minh"

    def check(self, layer, params):
        from qgis.core import QgsSpatialIndex
        mno_min = params.get("mno_min_area", 0.001)  # m²
        index = QgsSpatialIndex(layer.getFeatures())
        features = {f.id(): f for f in layer.getFeatures()}
        errors, checked = [], set()

        for fid, feat in features.items():
            g1 = feat.geometry()
            if not g1 or g1.isNull():
                continue
            for cid in index.intersects(g1.boundingBox()):
                if cid <= fid:
                    continue
                if (fid, cid) in checked:
                    continue
                checked.add((fid, cid))
                g2 = features[cid].geometry()
                if not g2 or g2.isNull():
                    continue
                # Catch ALL overlap types
                if not g1.intersects(g2) or g1.touches(g2):
                    continue
                inter = g1.intersection(g2)
                if inter and not inter.isNull() and inter.area() > mno_min:
                    errors.append(inter)
        return {"error_geometries": errors, "error_features": [],
                "details": [f"Area: {g.area():.4f} m2" for g in errors]}

    def fix(self, input_path, output_path, params):
        from qgis.core import (
            QgsVectorLayer, QgsSpatialIndex,
            QgsVectorFileWriter, QgsCoordinateTransformContext)
        mno_min = params.get("mno_min_area", 0.001)
        layer = QgsVectorLayer(input_path, "in", "ogr")
        features = {f.id(): f for f in layer.getFeatures()}
        index = QgsSpatialIndex(layer.getFeatures())
        changes = {}

        checked = set()
        for fid, feat in features.items():
            g1 = changes.get(fid, feat.geometry())
            if not g1 or g1.isNull():
                continue
            for cid in index.intersects(g1.boundingBox()):
                if cid <= fid or (fid, cid) in checked:
                    continue
                checked.add((fid, cid))
                g2 = changes.get(cid, features[cid].geometry())
                if not g2 or g2.isNull():
                    continue
                if not g1.intersects(g2) or g1.touches(g2):
                    continue
                inter = g1.intersection(g2)
                if not inter or inter.isNull() or inter.area() <= mno_min:
                    continue
                # Cut from SMALLER polygon
                if g1.area() < g2.area():
                    orig_area = g1.area()
                    result = g1.difference(inter)
                    # Safety guards
                    if not result or result.isNull() or result.isEmpty():
                        continue
                    if result.area() < orig_area * 0.5:
                        continue
                    if not result.isGeosValid():
                        result = result.makeValid()
                        if not result or result.isNull():
                            continue
                    g1 = result
                    changes[fid] = g1
                else:
                    orig_area = g2.area()
                    result = g2.difference(inter)
                    # Safety guards
                    if not result or result.isNull() or result.isEmpty():
                        continue
                    if result.area() < orig_area * 0.5:
                        continue
                    if not result.isGeosValid():
                        result = result.makeValid()
                        if not result or result.isNull():
                            continue
                    g2 = result
                    changes[cid] = g2

        if changes:
            layer.dataProvider().changeGeometryValues(changes)

        opts = QgsVectorFileWriter.SaveVectorOptions()
        opts.driverName = "GPKG"
        opts.forceMulti = True
        opts.includeZ = True
        QgsVectorFileWriter.writeAsVectorFormatV3(
            layer, output_path, QgsCoordinateTransformContext(), opts)
        return output_path
