from qgis.core import (
    QgsCoordinateReferenceSystem,
    QgsCoordinateTransform,
    QgsFeature,
    QgsField,
    QgsFields,
    QgsGeometry,
    QgsProject,
    QgsVectorLayer,
    QgsWkbTypes,
)
from qgis.PyQt.QtCore import QVariant


TARGET_EPSG = "EPSG:6316"
TARGET_CRS = QgsCoordinateReferenceSystem(TARGET_EPSG)


PARCEL_FIELDS = [
    "ID",
    "ID_PROC",
    "ID_PROC_EXPORT",
    "ID_PARC",
    "ID_PP",
    "MAIN_PARCEL",
    "OPS_KOPS",
    "PLIST",
    "BROJ",
    "DEL",
    "BZG",
    "KLASA",
    "SKICA",
    "SOPS",
    "AREA",
    "PARCNO",
    "CADMUN",
    "GIS_ELAB",
    "GIS_WORK",
    "KULT",
    "MESTO",
    "LOCK_ID_PROC",
]

BUILDING_FIELDS = [
    "ID",
    "ID_PROC",
    "ID_PROC_EXPORT",
    "ID_PARC",
    "ID_PP",
    "MAIN_PARCEL",
    "GIS_WORK",
    "CADMUN",
    "BUILDNO",
    "PARCNO",
    "OPS",
    "PLIST",
    "BROJ",
    "DEL",
    "BZG",
    "KLASA",
    "SKICA",
    "SOPS",
    "AREA",
    "GIS_ELAB",
    "KULT",
    "MESTO",
    "TYPE",
    "ADDRESS",
    "OWNER",
    "LOCK_ID_PROC",
]


def _create_fields(field_names):
    fields = QgsFields()
    for name in field_names:
        fields.append(QgsField(name, QVariant.String))
    return fields


def _collect_source_field_names(layer):
    return [f.name() for f in layer.fields()]


def _feature_to_attrs(src_feature, target_field_names, source_field_names):
    attrs = []
    for name in target_field_names:
        value = src_feature[name] if name in source_field_names else None
        attrs.append("" if value is None else str(value))
    return attrs


def _create_memory_layer(layer_name, field_names):
    layer = QgsVectorLayer(f"Polygon?crs={TARGET_EPSG}", layer_name, "memory")
    if not layer.isValid():
        raise Exception(f"Could not create memory layer: {layer_name}")

    pr = layer.dataProvider()
    ok = pr.addAttributes(_create_fields(field_names))
    if not ok:
        raise Exception(f"Could not add fields to memory layer: {layer_name}")

    layer.updateFields()
    return layer


def _transform_geometry_to_target_crs(geom, source_crs):
    if geom is None or geom.isEmpty():
        return None

    g = QgsGeometry(geom)

    if source_crs.isValid() and source_crs != TARGET_CRS:
        tr = QgsCoordinateTransform(source_crs, TARGET_CRS, QgsProject.instance())
        result = g.transform(tr)
        if result != 0:
            return None

    return g


def _normalize_to_single_2d_polygon(geom):
    if geom is None or geom.isEmpty():
        return None

    g = QgsGeometry(geom)

    if QgsWkbTypes.geometryType(g.wkbType()) != QgsWkbTypes.PolygonGeometry:
        return None

    if QgsWkbTypes.isMultiType(g.wkbType()):
        multi = g.asMultiPolygon()
        if not multi or len(multi) != 1:
            return None
        g = QgsGeometry.fromPolygonXY(multi[0])
    else:
        poly = g.asPolygon()
        if not poly:
            return None
        g = QgsGeometry.fromPolygonXY(poly)

    if g.isEmpty():
        return None

    if QgsWkbTypes.geometryType(g.wkbType()) != QgsWkbTypes.PolygonGeometry:
        return None

    if QgsWkbTypes.isMultiType(g.wkbType()):
        return None

    return g


def _copy_features(source_layer, target_layer, target_field_names):
    source_field_names = _collect_source_field_names(source_layer)
    source_crs = source_layer.crs()

    pr = target_layer.dataProvider()

    added = 0
    skipped = 0

    new_features = []

    for src_feat in source_layer.getFeatures():
        geom = src_feat.geometry()
        geom = _transform_geometry_to_target_crs(geom, source_crs)
        geom = _normalize_to_single_2d_polygon(geom)

        if geom is None:
            skipped += 1
            continue

        feat = QgsFeature(target_layer.fields())
        feat.setGeometry(geom)
        feat.setAttributes(
            _feature_to_attrs(src_feat, target_field_names, source_field_names)
        )
        new_features.append(feat)

    ok, out_feats = pr.addFeatures(new_features)
    if not ok:
        raise Exception(f"Could not add features to memory layer: {target_layer.name()}")

    target_layer.updateExtents()
    added = len(out_feats)

    if added == 0:
        raise Exception(f"No valid features were copied into layer: {target_layer.name()}")

    return {"added": added, "skipped": skipped}


def build_session_layers(parcels_source_path, buildings_source_path):
    parcels_src = QgsVectorLayer(parcels_source_path, "parcels_o_src", "ogr")
    buildings_src = QgsVectorLayer(buildings_source_path, "buildings_o_src", "ogr")

    if not parcels_src.isValid():
        raise Exception("Could not open source parcels_o.gml")

    if not buildings_src.isValid():
        raise Exception("Could not open source buildings_o.gml")

    parcels_layer = _create_memory_layer("PARC_TRANS", PARCEL_FIELDS)
    buildings_layer = _create_memory_layer("BUILDINGS_TRANS", BUILDING_FIELDS)

    parcels_stats = _copy_features(parcels_src, parcels_layer, PARCEL_FIELDS)
    buildings_stats = _copy_features(buildings_src, buildings_layer, BUILDING_FIELDS)

    return {
        "session_dir": None,
        "gpkg_path": None,
        "parcels_layer": parcels_layer,
        "buildings_layer": buildings_layer,
        "parcels_stats": parcels_stats,
        "buildings_stats": buildings_stats,
    }