#  Copyright (C) 2025
#  ABM, Moscow
#
#  UNPUBLISHED PROPRIETARY MATERIAL.
#  ALL RIGHTS RESERVED.
#
#  Authors: Mike Orlov <m.orlov@abm-jsc.ru>
import functools
from dataclasses import dataclass, field
from typing import Mapping, ClassVar

from frozendict import frozendict
from init_helpers import Jsonable

from openapi_tools.spec.spec_resource import SpecResource, SpecRef
from .base_schema import BaseSchema
from .type_schema import TypeSchema


@dataclass(frozen=True, slots=True)
class ObjectSchema(TypeSchema):
    type_: ClassVar[str] = "object"
    item_key_to_schema: Mapping[str, BaseSchema | SpecRef] = field(default_factory=frozendict)
    additional_items_schema: BaseSchema | SpecRef | None = None

    def __post_init__(self):
        object.__setattr__(self, 'item_key_to_schema', frozendict(self.item_key_to_schema))

    def get_spec_dependencies(self) -> frozenset['SpecResource']:
        result = set()
        if self.item_key_to_schema:
            result = result.union({v for v in self.item_key_to_schema.values() if isinstance(v, SpecResource)})
        if isinstance(self.additional_items_schema, SpecResource):
            result.add(self.additional_items_schema)
        return frozenset(BaseSchema.get_spec_dependencies(self) | result)

    def get_spec_dict(self, dependency_to_ref: Mapping['SpecResource', SpecRef | dict]) -> frozendict[str, Jsonable]:
        result = {
            'additionalProperties':
                dependency_to_ref[additional_items_schema]
                if isinstance((additional_items_schema := self.additional_items_schema), SpecResource)
                else False
        }
        if self.item_key_to_schema is not None:
            if required := [key for key, value in self.item_key_to_schema.items() if not value.has_default]:
                result['required'] = required
            result['properties'] = {
                key: dependency_to_ref[value] if isinstance(value, SpecResource) else value
                for key, value in self.item_key_to_schema.items()
            }
        return frozendict(TypeSchema.get_spec_dict(self, dependency_to_ref) | result)

    @functools.cache
    def _get_repr_parts(self) -> tuple[str, ...]:
        parts = list(TypeSchema._get_repr_parts(self))
        if self.item_key_to_schema:
            parts.append(f'item_key_to_schema={dict(self.item_key_to_schema)}')
        parts += [f'additional_items_schema={self.additional_items_schema!r}'] if self.additional_items_schema else []
        return tuple(parts)

    def __repr__(self):
        return f'{self.__class__.__name__}({", ".join(self._get_repr_parts())})'

    __str__ = __repr__
