# coding=utf-8
from typing import TypeAlias

from datetime import date, datetime

TyDate: TypeAlias = date
TyDatetime: TypeAlias = datetime
TyAoS = list[str]
TyAoDate = list[TyDate]
TyStr = str

TnAoDate = None | TyAoDate
TnDatetime = None | TyDatetime
TnStr = None | TyStr


class Str:
    """Manage String Class.

    Utility methods for string conversions.
    """

    @staticmethod
    def sh_date(string: str, fmt: TnStr = None) -> TnDatetime:
        """Parse a date string using *fmt* (default ``'%m/%d/%Y'``).

        :param str string: Date string to parse.
        :param TnStr fmt: Format string (default ``'%m/%d/%Y'``).
        :returns: Parsed datetime, or ``None`` on failure.
        :rtype: TnDatetime
        """
        try:
            if fmt is None:
                fmt = "%m/%d/%Y"
            _date: TnDatetime = datetime.strptime(string, fmt)
            return _date
        except Exception:
            return None


class AoS:
    """Manage Array of Strings.

    A static utility class for operations on lists of strings.
    """

    @staticmethod
    def nvl(aos: TyAoS) -> TyAoS:
        """Return *aos* or ``[]`` if ``None`` (SQL-style NVL).

        :param TyAoS aos: Array of strings or ``None``.
        :returns: *aos* or empty list.
        :rtype: TyAoS
        """
        if aos is None:
            return []
        return aos

    @staticmethod
    def sh_a_date(aos: TyAoS) -> TnAoDate:
        """Convert array of date strings to array of dates.

        Returns ``None`` if any element cannot be parsed.

        :param TyAoS aos: Array of date strings.
        :returns: Array of dates, or ``None`` on parse failure.
        :rtype: TnAoDate
        """
        if aos is None:
            return None
        a_date: TyAoDate = []
        if aos == []:
            return a_date
        for item in aos:
            _date = Str.sh_date(item)
            if _date is None:
                return None
            a_date.append(_date)
        if a_date == []:
            return None
        else:
            return a_date

    @staticmethod
    def to_lower(aos: TyAoS) -> TyAoS:
        """Lower-case every string in the array.

        :param TyAoS aos: Array of strings.
        :returns: Array of lower-cased strings.
        :rtype: TyAoS
        """
        if not aos:
            return []
        return [element.lower() for element in aos]

    @staticmethod
    def to_unique(aos: TyAoS) -> TyAoS:
        """Remove duplicate strings, preserving order.

        :param TyAoS aos: Array of strings.
        :returns: Deduplicated array.
        :rtype: TyAoS
        """
        aos_new = []
        for ee in aos:
            if ee not in aos_new:
                aos_new.append(ee)
        return aos_new

    @staticmethod
    def to_unique_lower(aos: TyAoS) -> TyAoS:
        """Remove duplicates and lower-case all strings.

        :param TyAoS aos: Array of strings.
        :returns: Deduplicated array of lower-cased strings.
        :rtype: TyAoS
        """
        aos_new_lower = []
        for _str in aos:
            _str_lower = _str.lower()
            if _str_lower not in aos_new_lower:
                aos_new_lower.append(_str_lower)
        return aos_new_lower

    @staticmethod
    def to_unique_lower_invariant(aos: TyAoS) -> TyAoS:
        """Remove duplicates case-insensitively, preserving original case.

        :param TyAoS aos: Array of strings.
        :returns: Deduplicated array (first occurrence kept).
        :rtype: TyAoS
        """
        aos_new = []
        aos_new_lower = []
        for _str in aos:
            _str_lower = _str.lower()
            if _str_lower not in aos_new_lower:
                aos_new_lower.append(_str_lower)
                aos_new.append(_str)
        return aos_new
