from qgis.core import (
    QgsCoordinateTransform,
    QgsFeature,
    QgsGeometry,
    QgsProject,
    QgsVectorLayer,
)

from .cadastre_editing import (
    copy_attributes,
    find_base_feature,
    find_containing_parcel,
    building_attrs_from_parcel,
    apply_attributes,
    enforce_no_overlap_same_layer,
    sync_buildings_parcno,
)


TARGET_EPSG = "EPSG:6316"


def _load_vector_layer(file_path):
    layer = QgsVectorLayer(file_path, "external_import", "ogr")
    if not layer.isValid():
        raise ValueError("Could not open external file as a vector layer.")
    return layer


def _ensure_polygon_layer(layer):
    if layer.geometryType() != 2:
        raise ValueError("External layer must be polygon.")
    return True


def _ensure_required_fields(layer, required_fields):
    names = layer.fields().names()
    missing = [f for f in required_fields if f not in names]
    if missing:
        raise ValueError("Missing required external fields: " + ", ".join(missing))


def _transform_geom_to_target(source_layer, geom, target_layer):
    if geom is None or geom.isEmpty():
        return None

    src_crs = source_layer.crs()
    dst_crs = target_layer.crs()
    if not src_crs.isValid() or not dst_crs.isValid():
        return geom

    if src_crs.authid() == dst_crs.authid():
        return QgsGeometry(geom)

    tr = QgsCoordinateTransform(src_crs, dst_crs, QgsProject.instance())
    g = QgsGeometry(geom)
    g.transform(tr)
    return g


def _apply_named_attrs_to_new_feature(feature, attrs):
    for f in feature.fields():
        name = f.name()
        if name.lower() in ("fid", "ogc_fid"):
            continue
        if name in attrs:
            try:
                feature.setAttribute(name, attrs[name])
            except Exception:
                pass


def import_parcels_from_file(file_path, parcels_layer, buildings_layer, update_area_for_all_features):
    """
    Import parcels from external file.
    Required field in external file: PARCNO
    """
    src = _load_vector_layer(file_path)
    _ensure_polygon_layer(src)
    _ensure_required_fields(src, ["PARCNO"])

    started_edit = False
    if not parcels_layer.isEditable():
        parcels_layer.startEditing()
        started_edit = True

    stats = {"imported": 0, "skipped": 0, "messages": []}

    for ext_feat in src.getFeatures():
        try:
            ext_geom = _transform_geom_to_target(src, ext_feat.geometry(), parcels_layer)
            if ext_geom is None or ext_geom.isEmpty():
                stats["skipped"] += 1
                stats["messages"].append(f"Skipped parcel external feature {ext_feat.id()}: empty geometry")
                continue

            ext_parcno = ext_feat["PARCNO"]
            if ext_parcno is None or str(ext_parcno).strip() == "":
                stats["skipped"] += 1
                stats["messages"].append(f"Skipped parcel external feature {ext_feat.id()}: empty PARCNO")
                continue

            base = find_base_feature(parcels_layer, ext_geom, exclude=None)
            attrs = copy_attributes(base)
            attrs["PARCNO"] = ext_parcno
            attrs["AREA"] = None

            nf = QgsFeature(parcels_layer.fields())
            nf.setGeometry(ext_geom)
            _apply_named_attrs_to_new_feature(nf, attrs)

            ok, added = parcels_layer.dataProvider().addFeatures([nf])
            if not ok or not added:
                stats["skipped"] += 1
                stats["messages"].append(f"Skipped parcel external feature {ext_feat.id()}: could not add feature")
                continue

            new_fid = added[0].id()
            enforce_no_overlap_same_layer(parcels_layer, new_fid)
            update_area_for_all_features(parcels_layer)
            sync_buildings_parcno(parcels_layer, buildings_layer)

            stats["imported"] += 1

        except Exception as e:
            stats["skipped"] += 1
            stats["messages"].append(f"Skipped parcel external feature {ext_feat.id()}: {e}")

    if started_edit:
        # mbetet në edit session; nuk bëjmë commit këtu
        pass

    return stats


def import_buildings_from_file(file_path, buildings_layer, parcels_layer, update_area_for_all_features):
    """
    Import buildings from external file.
    Required fields in external file: BROJ, DEL, BZG
    """
    src = _load_vector_layer(file_path)
    _ensure_polygon_layer(src)
    _ensure_required_fields(src, ["BROJ", "DEL", "BZG"])

    started_edit = False
    if not buildings_layer.isEditable():
        buildings_layer.startEditing()
        started_edit = True

    stats = {"imported": 0, "skipped": 0, "messages": []}

    for ext_feat in src.getFeatures():
        try:
            ext_geom = _transform_geom_to_target(src, ext_feat.geometry(), buildings_layer)
            if ext_geom is None or ext_geom.isEmpty():
                stats["skipped"] += 1
                stats["messages"].append(f"Skipped building external feature {ext_feat.id()}: empty geometry")
                continue

            broj = ext_feat["BROJ"]
            del_ = ext_feat["DEL"]
            bzg = ext_feat["BZG"]

            if any(v is None or str(v).strip() == "" for v in [broj, del_, bzg]):
                stats["skipped"] += 1
                stats["messages"].append(f"Skipped building external feature {ext_feat.id()}: BROJ/DEL/BZG missing")
                continue

            parcel = find_containing_parcel(parcels_layer, ext_geom)
            if parcel is None:
                stats["skipped"] += 1
                stats["messages"].append(
                    f"Skipped building external feature {ext_feat.id()}: object is not fully inside a parcel"
                )
                continue

            base_building = find_base_feature(buildings_layer, ext_geom, exclude=None)

            if base_building is not None:
                attrs = copy_attributes(base_building)
                attrs["KULT"] = base_building["KULT"] if "KULT" in base_building.fields().names() else None
            else:
                attrs = {}

            attrs.update(building_attrs_from_parcel(parcel))
            attrs["BROJ"] = broj
            attrs["DEL"] = del_
            attrs["BZG"] = bzg
            attrs["PARCNO"] = parcel["PARCNO"]
            attrs["AREA"] = None

            if base_building is None:
                attrs["KULT"] = None

            nf = QgsFeature(buildings_layer.fields())
            nf.setGeometry(ext_geom)
            _apply_named_attrs_to_new_feature(nf, attrs)

            ok, added = buildings_layer.dataProvider().addFeatures([nf])
            if not ok or not added:
                stats["skipped"] += 1
                stats["messages"].append(f"Skipped building external feature {ext_feat.id()}: could not add feature")
                continue

            new_fid = added[0].id()
            enforce_no_overlap_same_layer(buildings_layer, new_fid)
            update_area_for_all_features(buildings_layer)
            sync_buildings_parcno(parcels_layer, buildings_layer)

            stats["imported"] += 1

        except Exception as e:
            stats["skipped"] += 1
            stats["messages"].append(f"Skipped building external feature {ext_feat.id()}: {e}")

    if started_edit:
        pass

    return stats