
from typing import List, Union, Tuple

import numbers

from qgis.core import QgsVectorLayer, QgsMessageLog
from qgis.core import Qgis

from geogst.core.utils.inspections.errors import *
from geogst.core.utils.inspections.functions import *  # caller_name, used when reporting errors


def field_values(
    layer,
    curr_field_ndx: numbers.Integral
):

    values = []

    if layer.selectedFeatureCount() > 0:
        features = layer.selectedFeatures()
    else:
        features = layer.getFeatures()

    for feature in features:
        values.append(feature.attributes()[curr_field_ndx])

    return values


def extract_vector_attributes(
    layer,
    field_list
):
    """
    Probably to deprecate in favour of 'extract_layer_attributes'
    """

    # Verify layer is valid
    if not isinstance(layer, QgsVectorLayer):
        QgsMessageLog.logMessage(
            f"Invalid layer type: expected QgsVectorLayer, got {type(layer)}",
            "qgSurf",
            Qgis.MessageLevel.Warning
        )
        return []

    if not layer.isValid():
        QgsMessageLog.logMessage(
            f"Layer '{layer.name()}' is not valid",
            "qgSurf",
            Qgis.MessageLevel.Warning
        )
        return []

    # Verify provider is valid
    provider = layer.dataProvider()
    if provider is None:
        QgsMessageLog.logMessage(
            f"Layer '{layer.name()}' has no valid data provider",
            "qgSurf",
            Qgis.MessageLevel.Warning
        )
        return []

    # Get field indices, filtering out -1 (field not found)
    field_indices = []
    for field_name in field_list:
        idx = provider.fieldNameIndex(field_name)
        if idx >= 0:
            field_indices.append(idx)
        else:
            QgsMessageLog.logMessage(
                f"Field '{field_name}' not found in layer '{layer.name()}'",
                "qgSurf",
                Qgis.MessageLevel.Warning
            )

    if layer.selectedFeatureCount() > 0:
        features = layer.selectedFeatures()
    else:
        features = layer.getFeatures()

    # retrieve (selected) attributes features
    data_list = []
    for feature in features:
        # Use direct attribute access with index
        data_list.append([feature.attribute(field_ndx) for field_ndx in field_indices])

    return data_list


def extract_layer_attributes(
        layer: QgsVectorLayer,
        fields: List[str]
) -> List:
    """
    Get attributes from layer based on provided field names list.

    :param layer: the source layer for attribute extraction.
    :param fields: a list of field names for attribute extraction.
    :return: list of table values.
    """

    # Verify layer is valid
    if not isinstance(layer, QgsVectorLayer):
        QgsMessageLog.logMessage(
            f"Invalid layer type: expected QgsVectorLayer, got {type(layer)}",
            "qgSurf",
            Qgis.MessageLevel.Warning
        )
        return []

    if not layer.isValid():
        QgsMessageLog.logMessage(
            f"Layer '{layer.name()}' is not valid",
            "qgSurf",
            Qgis.MessageLevel.Warning
        )
        return []

    # Verify provider is valid
    provider = layer.dataProvider()
    if provider is None:
        QgsMessageLog.logMessage(
            f"Layer '{layer.name()}' has no valid data provider",
            "qgSurf",
            Qgis.MessageLevel.Warning
        )
        return []

    # Get field indices, filtering out -1 (field not found)
    field_indices = []
    for field_name in fields:
        idx = provider.fieldNameIndex(field_name)
        if idx >= 0:
            field_indices.append(idx)
        else:
            QgsMessageLog.logMessage(
                f"Field '{field_name}' not found in layer '{layer.name()}'",
                "qgSurf",
                Qgis.MessageLevel.Warning
            )

    if layer.selectedFeatureCount() > 0:
        features = layer.selectedFeatures()
    else:
        features = layer.getFeatures()

    # retrieve selected features with relevant attributes

    rec_list = []

    for feature in features:

        # creates feature attribute list
        # Use direct attribute access with index instead of attrs[field_ndx].name()
        feat_list = []
        for field_ndx in field_indices:
            feat_list.append(feature.attribute(field_ndx))

        # add to result list

        rec_list.append(feat_list)

    return rec_list


def extract_point_layer_geometries_and_attributes(
        layer: QgsVectorLayer,
        fields: List[str]
) -> Tuple[Union[None, List], Error]:
    """
    Get attributes from point layer based on provided field names list.

    :param layer: the source layer for attribute extraction.
    :param fields: a list of field names for attribute extraction.
    :return: list of table values.
    """

    try:

        # Verify layer is valid
        if not isinstance(layer, QgsVectorLayer):
            return None, Error(
                True,
                caller_name(),
                Exception(f"Invalid layer type: expected QgsVectorLayer, got {type(layer)}"),
                traceback.format_exc()
            )

        if not layer.isValid():
            return None, Error(
                True,
                caller_name(),
                Exception(f"Layer '{layer.name()}' is not valid"),
                traceback.format_exc()
            )

        # Verify provider is valid
        provider = layer.dataProvider()
        if provider is None:
            return None, Error(
                True,
                caller_name(),
                Exception(f"Layer '{layer.name()}' has no valid data provider"),
                traceback.format_exc()
            )

        # Get field indices, filtering out -1 (field not found)
        field_indices = []
        for field_name in fields:
            idx = provider.fieldNameIndex(field_name)
            if idx >= 0:
                field_indices.append(idx)
            else:
                QgsMessageLog.logMessage(
                    f"Field '{field_name}' not found in layer '{layer.name()}'",
                    "qgSurf",
                    Qgis.MessageLevel.Warning
                )

        if layer.selectedFeatureCount() > 0:
            points = layer.selectedFeatures()
        else:
            points = layer.getFeatures()

        # retrieve selected features with relevant attributes

        rec_list = []

        for point in points:

            geom = point.geometry()

            # converting a null geometry to a point raises, and that would
            # discard the records of the whole layer

            if geom is None or geom.isNull() or geom.isEmpty():

                QgsMessageLog.logMessage(
                    f"Feature {point.id()} has null or empty geometry, skipping",
                    "qgSurf",
                    Qgis.MessageLevel.Warning
                )
                continue

            # the first vertex, rather than asPoint(), so that multipoint
            # features are read instead of raising

            qgs_point_xy = geom.vertexAt(0)

            # creates feature attribute list
            # Use direct attribute access with index instead of attrs[field_ndx].name()
            feat_list = [qgs_point_xy.x(), qgs_point_xy.y()]
            for field_ndx in field_indices:
                feat_list.append(point.attribute(field_ndx))

            # add to result list

            rec_list.append(feat_list)

        return rec_list, Error()

    except Exception as e:

        return None, Error(
            True,
            caller_name(),
            e,
            traceback.format_exc()
        )