"""Helpers for configuring cattrs with our default
good-practices converters.
"""

from typing import get_args, overload
import cattrs
import cattrs.strategies
from datetype import (
    AwareDateTime,
    DateTime,
    NaiveDateTime,
    Date,
    AwareTime,
    NaiveTime,
    Time,
)

from coolqlite._common import SqliteType
from coolqlite.from_sql.datetime_ import (
    aware_dt_from_iso,
    aware_time_from_iso,
    date_from_iso,
    dt_from_iso,
    naive_dt_from_iso,
    naive_time_from_iso,
    time_from_iso,
)


def configure_converter_datetimes(conv: cattrs.BaseConverter):
    """Configure the given converter with structure hooks for
    the various datetype types.
    """
    conv.register_structure_hook(DateTime, dt_from_iso)
    conv.register_structure_hook(AwareDateTime, aware_dt_from_iso)
    conv.register_structure_hook(NaiveDateTime, naive_dt_from_iso)
    conv.register_structure_hook(NaiveDateTime | AwareDateTime, dt_from_iso)

    conv.register_structure_hook(Time, time_from_iso)
    conv.register_structure_hook(AwareTime, aware_time_from_iso)
    conv.register_structure_hook(NaiveTime, naive_time_from_iso)
    conv.register_structure_hook(NaiveTime | AwareTime, time_from_iso)

    conv.register_structure_hook(Date, date_from_iso)


# TODO: ints as floats
def _strict_primitive_hook(strict_type: type):
    def _hook(val, requested_type):
        # TODO: why does the example have it doing `requested_type` instead of int?
        # cuz then the return type is... well, _correct_, but...
        if not isinstance(val, requested_type):
            raise ValueError(f"{val!r} not an instance of {requested_type}")
        return val

    return _hook


def configure_strict_primitives(conv: cattrs.BaseConverter):
    for type_ in get_args(SqliteType):
        conv.register_structure_hook(type_, _strict_primitive_hook(type_))


def configure_union_passthrough(conv: cattrs.BaseConverter):
    cattrs.strategies.configure_union_passthrough(SqliteType, conv)


@overload
def configure_converter() -> cattrs.Converter: ...
@overload
def configure_converter[T: cattrs.BaseConverter = cattrs.BaseConverter](
    conv: T | None = None,
) -> T: ...
def configure_converter[T: cattrs.BaseConverter = cattrs.BaseConverter](
    conv: T | None = None,
) -> T | cattrs.Converter:
    """Configure a converter with our good-practices defaults.
    If not given, creates a new ``cattrs.Converter`` .
    """
    _conv = cattrs.Converter() if conv is None else conv
    # _conv.register_structure_hook(Union, )
    configure_strict_primitives(_conv)
    configure_union_passthrough(_conv)
    configure_converter_datetimes(_conv)
    return _conv
