from __future__ import annotations

from datetime import date, datetime
from enum import Enum
from typing import Any, Dict, List, Literal, Optional, Union

from linkml_runtime.linkml_model import Decimal
from pydantic import BaseModel as BaseModel
from pydantic import Field

metamodel_version = "None"
version = "None"


class WeakRefShimBaseModel(BaseModel):
    __slots__ = "__weakref__"


class ConfiguredBaseModel(
    WeakRefShimBaseModel,
    validate_assignment=True,
    validate_all=True,
    underscore_attrs_are_private=True,
    extra="forbid",
    arbitrary_types_allowed=True,
):
    pass


class CollectionType(str, Enum):
    SingleValued = "SingleValued"
    MultiValued = "MultiValued"
    MultiValuedList = "MultiValuedList"
    MultiValuedDict = "MultiValuedDict"


class TransformationSpecification(ConfiguredBaseModel):
    """
    A collection of mappings between source and target classes
    """

    id: Optional[str] = Field(
        None, description="""Unique identifier for this transformation specification"""
    )
    title: Optional[str] = Field(
        None,
        description="""human readable title for this transformation specification""",
    )
    prefixes: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict, description="""maps prefixes to URL expansions"""
    )
    source_schema: Optional[str] = Field(
        None,
        description="""name of the schema that describes the source (input) objects""",
    )
    target_schema: Optional[str] = Field(
        None,
        description="""name of the schema that describes the target (output) objects""",
    )
    class_derivations: Optional[Dict[str, ClassDerivation]] = Field(
        default_factory=dict,
        description="""Instructions on how to derive a set of classes in the target schema from classes in the source schema.""",
    )
    enum_derivations: Optional[Dict[str, EnumDerivation]] = Field(
        default_factory=dict,
        description="""Instructions on how to derive a set of enums in the target schema""",
    )
    slot_derivations: Optional[Dict[str, SlotDerivation]] = Field(
        default_factory=dict,
        description="""Instructions on how to derive a set of top level slots in the target schema""",
    )


class ElementDerivation(ConfiguredBaseModel):
    """
    An abstract grouping for classes that provide a specification of how to  derive a target element from a source element.
    """

    name: str = Field(None, description="""Name of the element in the target schema""")
    copy_directives: Optional[Dict[str, CopyDirective]] = Field(default_factory=dict)
    overrides: Optional[Any] = Field(None, description="""overrides source schema slots""")
    is_a: Optional[str] = Field(None)
    mixins: Optional[Dict[str, ElementDerivation]] = Field(default_factory=dict)
    value_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table that is applied directly to mappings, in order of precedence""",
    )
    expression_to_value_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table in which the keys are expressions""",
    )
    expression_to_expression_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table in which the keys and values are expressions""",
    )


class ClassDerivation(ElementDerivation):
    """
    A specification of how to derive a target class from a source class.
    """

    populated_from: Optional[str] = Field(
        None, description="""Name of the class in the source schema"""
    )
    joins: Optional[Dict[str, AliasedClass]] = Field(
        default_factory=dict,
        description="""Additional classes to be joined to derive instances of the target class""",
    )
    slot_derivations: Optional[Dict[str, SlotDerivation]] = Field(default_factory=dict)
    name: str = Field(None, description="""Name of the element in the target schema""")
    copy_directives: Optional[Dict[str, CopyDirective]] = Field(default_factory=dict)
    overrides: Optional[Any] = Field(None, description="""overrides source schema slots""")
    is_a: Optional[str] = Field(None)
    mixins: Optional[Dict[str, ElementDerivation]] = Field(default_factory=dict)
    value_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table that is applied directly to mappings, in order of precedence""",
    )
    expression_to_value_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table in which the keys are expressions""",
    )
    expression_to_expression_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table in which the keys and values are expressions""",
    )


class AliasedClass(ConfiguredBaseModel):
    """
    alias-class key value pairs for classes
    """

    alias: str = Field(None, description="""name of the class to be aliased""")
    class_named: Optional[str] = Field(None, description="""local alias for the class""")


class SlotDerivation(ElementDerivation):
    """
    A specification of how to derive the value of a target slot from a source slot
    """

    name: str = Field(None, description="""Target slot name""")
    populated_from: Optional[str] = Field(None, description="""Source slot name""")
    expr: Optional[str] = Field(
        None,
        description="""An expression to be evaluated on the source object to derive the target slot. Should be specified using the LinkML expression language.""",
    )
    range: Optional[str] = Field(None)
    inverse_of: Optional[Inverse] = Field(
        None,
        description="""Used to specify a class-slot tuple that is the inverse of the derived/target slot. This is used primarily for mapping to relational databases or formalisms that do not allow multiple values. The class representing the repeated element has a foreign key slot inserted in that 'back references' the original multivalued slot.""",
    )
    hide: Optional[bool] = Field(None, description="""True if this is suppressed""")
    type_designator: Optional[bool] = Field(None)
    cast_collection_as: Optional[CollectionType] = Field(None)
    copy_directives: Optional[Dict[str, CopyDirective]] = Field(default_factory=dict)
    overrides: Optional[Any] = Field(None, description="""overrides source schema slots""")
    is_a: Optional[str] = Field(None)
    mixins: Optional[Dict[str, ElementDerivation]] = Field(default_factory=dict)
    value_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table that is applied directly to mappings, in order of precedence""",
    )
    expression_to_value_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table in which the keys are expressions""",
    )
    expression_to_expression_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table in which the keys and values are expressions""",
    )


class EnumDerivation(ElementDerivation):
    """
    A specification of how to derive the value of a target enum from a source enum
    """

    name: str = Field(None, description="""Target enum name""")
    populated_from: Optional[str] = Field(None, description="""Source enum name""")
    expr: Optional[str] = Field(
        None,
        description="""An expression to be evaluated on the source object to derive the target slot. Should be specified using the LinkML expression language.""",
    )
    hide: Optional[bool] = Field(None, description="""True if this is suppressed""")
    permissible_value_derivations: Optional[Dict[str, PermissibleValueDerivation]] = Field(
        default_factory=dict,
        description="""Instructions on how to derive a set of PVs in the target schema""",
    )
    copy_directives: Optional[Dict[str, CopyDirective]] = Field(default_factory=dict)
    overrides: Optional[Any] = Field(None, description="""overrides source schema slots""")
    is_a: Optional[str] = Field(None)
    mixins: Optional[Dict[str, ElementDerivation]] = Field(default_factory=dict)
    value_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table that is applied directly to mappings, in order of precedence""",
    )
    expression_to_value_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table in which the keys are expressions""",
    )
    expression_to_expression_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table in which the keys and values are expressions""",
    )


class PermissibleValueDerivation(ElementDerivation):
    """
    A specification of how to derive the value of a PV from a source enum
    """

    name: str = Field(None, description="""Target permissible value text""")
    expr: Optional[str] = Field(None)
    populated_from: Optional[str] = Field(None, description="""Source permissible value""")
    hide: Optional[bool] = Field(None)
    copy_directives: Optional[Dict[str, CopyDirective]] = Field(default_factory=dict)
    overrides: Optional[Any] = Field(None, description="""overrides source schema slots""")
    is_a: Optional[str] = Field(None)
    mixins: Optional[Dict[str, ElementDerivation]] = Field(default_factory=dict)
    value_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table that is applied directly to mappings, in order of precedence""",
    )
    expression_to_value_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table in which the keys are expressions""",
    )
    expression_to_expression_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table in which the keys and values are expressions""",
    )


class PrefixDerivation(ElementDerivation):
    name: str = Field(None, description="""Name of the element in the target schema""")
    copy_directives: Optional[Dict[str, CopyDirective]] = Field(default_factory=dict)
    overrides: Optional[Any] = Field(None, description="""overrides source schema slots""")
    is_a: Optional[str] = Field(None)
    mixins: Optional[Dict[str, ElementDerivation]] = Field(default_factory=dict)
    value_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table that is applied directly to mappings, in order of precedence""",
    )
    expression_to_value_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table in which the keys are expressions""",
    )
    expression_to_expression_mappings: Optional[Dict[str, KeyVal]] = Field(
        default_factory=dict,
        description="""A mapping table in which the keys and values are expressions""",
    )


class Inverse(ConfiguredBaseModel):
    """
    Used for back references
    """

    slot_name: Optional[str] = Field(None)
    class_name: Optional[str] = Field(None)


class KeyVal(ConfiguredBaseModel):
    key: str = Field(None)
    value: Optional[Any] = Field(None)


class CopyDirective(ConfiguredBaseModel):
    """
    Instructs a Schema Mapper in how to map to a target schema. Not used for data transformation.
    """

    element_name: str = Field(None)
    copy_all: Optional[bool] = Field(None)
    exclude_all: Optional[bool] = Field(None)
    exclude: Optional[Any] = Field(None)
    include: Optional[Any] = Field(None)
    add: Optional[Any] = Field(None)


# Update forward refs
# see https://pydantic-docs.helpmanual.io/usage/postponed_annotations/
TransformationSpecification.update_forward_refs()
ElementDerivation.update_forward_refs()
ClassDerivation.update_forward_refs()
AliasedClass.update_forward_refs()
SlotDerivation.update_forward_refs()
EnumDerivation.update_forward_refs()
PermissibleValueDerivation.update_forward_refs()
PrefixDerivation.update_forward_refs()
Inverse.update_forward_refs()
KeyVal.update_forward_refs()
CopyDirective.update_forward_refs()
