#  Copyright (C) 2023
#  ABM, Moscow
#
#  UNPUBLISHED PROPRIETARY MATERIAL.
#  ALL RIGHTS RESERVED.
#
#  Authors: Mike Orlov <m.orlov@abm-jsc.ru>
import dataclasses
from typing import Any, Callable, Awaitable

from no_value import NoValue
from sqlalchemy import ForeignKey, Column
from sqlalchemy.orm import relationship
from sqlalchemy.sql.type_api import TypeEngine

from .utils.defs import SA_DATACLASS_METADATA_KEY
from .utils.no_default import NoDefault


def entity_field(
        type_: type[TypeEngine] | TypeEngine | ForeignKey,
        foreign_key: ForeignKey = None,
        *,
        primary_key: bool = False, nullable: bool = True, unique: bool = False, index: bool = False,
        default: Any = None, server_default: Any = None, default_factory: Callable = None,
        insert_default: Callable[[...], Awaitable[Any]] = None, autoincrement: bool = None,
        identifier: bool = False
) -> dataclasses.field:
    if primary_key:
        nullable = True

    column_args = [type_, foreign_key]
    column_kwargs = {"primary_key": primary_key} if primary_key else {"nullable": nullable, "unique": unique}

    if default is not None:
        column_kwargs['default'] = default
    if server_default is not None:
        column_kwargs['server_default'] = server_default
    if index:
        column_kwargs['index'] = index
    if autoincrement is not None:
        column_kwargs['autoincrement'] = autoincrement

    metadata = {SA_DATACLASS_METADATA_KEY: Column(*column_args, **column_kwargs)}

    # extension attributes
    if insert_default:
        # TODO: check no "default" passed, think about using insert_default with None
        metadata['insert_default'] = insert_default
    metadata['identifier'] = identifier
    # metadata['descend'] = None

    kwargs: dict[str, Any] = {"metadata": metadata}
    if default_factory:
        kwargs['default_factory'] = default_factory
    else:
        # noinspection PyTypedDict
        kwargs['default'] = NoDefault
        if default:
            kwargs['default'] = default
        elif nullable:
            kwargs['default'] = None
        elif insert_default is not None or server_default is not None:
            kwargs['default'] = NoValue

    field = dataclasses.field(**kwargs)
    return field


def entity_relation_field(remote_class, *, init: bool = True, use_list: bool = True,
                          order_by: str | bool | list = False, lazy: str = "noload",
                          **relationship_kwargs) -> dataclasses.field:
    metadata = {
        SA_DATACLASS_METADATA_KEY: relationship(
            remote_class, uselist=use_list, order_by=order_by, lazy=lazy, **relationship_kwargs
        )}
    return dataclasses.field(metadata=metadata, init=init, default_factory=list)


def get_sqlalchemy_metadata(field: dataclasses.field) -> Any:
    return field.metadata[SA_DATACLASS_METADATA_KEY]
