from __future__ import annotations

import sys as _sys
from typing import Any as _Any

from typing_extensions import Sentinel

from ._pydantic_core import (
    ArgsKwargs,
    MultiHostUrl,
    PydanticCustomError,
    PydanticKnownError,
    PydanticOmit,
    PydanticSerializationError,
    PydanticSerializationUnexpectedValue,
    PydanticUndefined,
    PydanticUndefinedType,
    PydanticUseDefault,
    SchemaError,
    SchemaSerializer,
    SchemaValidator,
    Some,
    TzInfo,
    Url,
    ValidationError,
    __version__,
    from_json,
    to_json,
    to_jsonable_python,
)
from .core_schema import CoreConfig, CoreSchema, CoreSchemaType, ErrorType

if _sys.version_info < (3, 11):
    from typing_extensions import NotRequired as _NotRequired
else:
    from typing import NotRequired as _NotRequired

if _sys.version_info < (3, 12):
    from typing_extensions import TypedDict as _TypedDict
else:
    from typing import TypedDict as _TypedDict

__all__ = [
    '__version__',
    'UNSET',
    'CoreConfig',
    'CoreSchema',
    'CoreSchemaType',
    'SchemaValidator',
    'SchemaSerializer',
    'Some',
    'Url',
    'MultiHostUrl',
    'ArgsKwargs',
    'PydanticUndefined',
    'PydanticUndefinedType',
    'SchemaError',
    'ErrorDetails',
    'InitErrorDetails',
    'ValidationError',
    'PydanticCustomError',
    'PydanticKnownError',
    'PydanticOmit',
    'PydanticUseDefault',
    'PydanticSerializationError',
    'PydanticSerializationUnexpectedValue',
    'TzInfo',
    'to_json',
    'from_json',
    'to_jsonable_python',
]


class ErrorDetails(_TypedDict):
    type: str
    """
    The type of error that occurred, this is an identifier designed for
    programmatic use that will change rarely or never.

    `type` is unique for each error message, and can hence be used as an identifier to build custom error messages.
    """
    loc: tuple[int | str, ...]
    """Tuple of strings and ints identifying where in the schema the error occurred."""
    msg: str
    """A human readable error message."""
    input: _Any
    """The input data at this `loc` that caused the error."""
    ctx: _NotRequired[dict[str, _Any]]
    """
    Values which are required to render the error message, and could hence be useful in rendering custom error messages.
    Also useful for passing custom error data forward.
    """
    url: _NotRequired[str]
    """
    The documentation URL giving information about the error. No URL is available if
    a [`PydanticCustomError`][pydantic_core.PydanticCustomError] is used.
    """


class InitErrorDetails(_TypedDict):
    type: str | PydanticCustomError
    """The type of error that occurred, this should be a "slug" identifier that changes rarely or never."""
    loc: _NotRequired[tuple[int | str, ...]]
    """Tuple of strings and ints identifying where in the schema the error occurred."""
    input: _Any
    """The input data at this `loc` that caused the error."""
    ctx: _NotRequired[dict[str, _Any]]
    """
    Values which are required to render the error message, and could hence be useful in rendering custom error messages.
    Also useful for passing custom error data forward.
    """


class ErrorTypeInfo(_TypedDict):
    """
    Gives information about errors.
    """

    type: ErrorType
    """The type of error that occurred, this should be a "slug" identifier that changes rarely or never."""
    message_template_python: str
    """String template to render a human readable error message from using context, when the input is Python."""
    example_message_python: str
    """Example of a human readable error message, when the input is Python."""
    message_template_json: _NotRequired[str]
    """String template to render a human readable error message from using context, when the input is JSON data."""
    example_message_json: _NotRequired[str]
    """Example of a human readable error message, when the input is JSON data."""
    example_context: dict[str, _Any] | None
    """Example of context values."""


class MultiHostHost(_TypedDict):
    """
    A host part of a multi-host URL.
    """

    username: str | None
    """The username part of this host, or `None`."""
    password: str | None
    """The password part of this host, or `None`."""
    host: str | None
    """The host part of this host, or `None`."""
    port: int | None
    """The port part of this host, or `None`."""


MISSING = Sentinel('MISSING')
"""A singleton indicating a field value was not provided during validation.

This singleton can be used a default value, as an alternative to `None` when it has
an explicit meaning. During serialization, any field with `MISSING` as a value is excluded
from the output.

Example:
    ```python
    from pydantic import BaseModel

    from pydantic_core import MISSING


    class Configuration(BaseModel):
        timeout: int | None | MISSING = MISSING


    # configuration defaults, stored somewhere else:
    defaults = {'timeout': 200}

    conf = Configuration.model_validate({...})
    timeout = conf.timeout if timeout.timeout is not MISSING else defaults['timeout']
"""
