from __future__ import annotations

import inspect
from typing import Annotated
from typing import Any
from typing import get_args
from typing import get_origin

from amsdal_models.classes.model import Model
from amsdal_utils.models.data_models.reference import Reference

from amsdal_ml.mcp_server.server_model_explorer.services.types import CrudOperation

CORE_MODEL_TYPES: set[str] = {
    'Option',
    'ClassObject',
    'ClassProperty',
    'Fixture',
    'StorageMetadata',
    'Validator',
}

_CRUD_TO_ACTION = {
    CrudOperation.READ: 'read',
    CrudOperation.CREATE: 'create',
    CrudOperation.UPDATE: 'update',
    CrudOperation.DELETE: 'delete',
}


def get_api_queryset(model_cls: type[Model]):
    """Use api_objects manager if available (row-level filtering), else default objects."""
    return getattr(model_cls, 'api_objects', model_cls.objects).all()


def get_action(operation: CrudOperation):
    """Lazily import Action and return the matching enum value."""
    from amsdal_server.apps.common.permissions.enums import Action

    return Action(_CRUD_TO_ACTION[operation])


def extract_referenced_model_name(annotation: Any) -> str | None:
    """
    Extract the target model name from a field annotation if it references another Model.

    Handles Reference[Model], Annotated[...], and Optional/Union types.
    Returns None if the annotation does not reference a user-defined Model.
    """
    origin = get_origin(annotation)

    if origin is Annotated:
        args = get_args(annotation)
        if args:
            return extract_referenced_model_name(args[0])

    if origin is Reference:
        args = get_args(annotation)
        if args:
            return extract_referenced_model_name(args[0])

    if origin is not None and 'Union' in str(origin):
        args = get_args(annotation)
        for arg in args:
            if arg is type(None):
                continue
            result = extract_referenced_model_name(arg)
            if result is not None:
                return result

    try:
        if isinstance(annotation, type) and issubclass(annotation, Model):
            name = annotation.__name__
            if name not in CORE_MODEL_TYPES:
                return name
    except TypeError:
        pass

    return None


def get_property_source(model_cls: type[Model], field_name: str) -> str | None:
    """
    Return the source code of a property getter from a model class.

    If the attribute is not a property or the source cannot be retrieved,
    returns None.
    """
    attr = getattr(model_cls, field_name, None)
    if not isinstance(attr, property):
        return None
    fget = attr.fget
    if fget is None:
        return None
    try:
        return inspect.getsource(fget)
    except OSError:
        return None
