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

import sqlalchemy
import sqlalchemy.orm
from init_helpers.dict_to_dataclass import get_dataclass_field_name_to_field, NoValue

from entity_read.entity import Entity
from entity_read.entity_field import get_sqlalchemy_metadata
from .utils import is_list_relation, has_default

logger = getLogger(__name__)


@dataclass(unsafe_hash=True, repr=False)
class _InsertViewMixin:
    @classmethod
    def _produce_field_name_to_field(cls, related_entity_type: type[Entity],
                                     relation_remote_column: sqlalchemy.Column = None) -> dict[str, Field | Callable]:
        # sorry for this: fast way to break circular dependency
        from .relation_insert import RelationInsertView
        field_name_to_field = copy.copy(get_dataclass_field_name_to_field(related_entity_type))
        result = {}
        for field_name, field in field_name_to_field.items():
            field_copy = copy.copy(field)
            sql_alchemy_property = get_sqlalchemy_metadata(field)

            if isinstance(sql_alchemy_property, sqlalchemy.orm.RelationshipProperty):
                relation: sqlalchemy.orm.RelationshipProperty = sql_alchemy_property
                field_copy.default = NoValue
                field_copy.default_factory = dataclasses.MISSING
                related_entity_view_type = RelationInsertView[relation]
                is_list = is_list_relation(relation)
                field_copy.type = list[related_entity_view_type] if is_list else related_entity_view_type
            elif isinstance(sql_alchemy_property, sqlalchemy.Column):
                column: sqlalchemy.Column = sql_alchemy_property
                if relation_remote_column is not None and column == relation_remote_column:
                    field_copy.init = False
                    field_copy.default = NoValue
                if field.default in (dataclasses.MISSING, NoValue) and has_default(column):
                    field_copy.default = NoValue
                    # used to consider null value as missing, if null is prohibited by structure; example: pk
                    if not column.nullable:
                        field_copy.type = Optional[field_copy.type]
            else:
                logger.warning(f"ignored attribute: %s, cos %s has unexpected type", field, sql_alchemy_property)

            result[field_name] = field_copy

        result['entity_type'] = related_entity_type
        return result
