from datetime import datetime
from decimal import Decimal, InvalidOperation

from .constants import TransferWindowType
from .exceptions import (
    InvalidTransferFeeError,
    InvalidTransferInternationalFlagError,
    InvalidTransferRejectionReasonError,
    InvalidTransferWindowDatesError,
    InvalidTransferWindowNameError,
    InvalidTransferWindowTypeError,
)


def validate_transfer_window_dates(*, start_at: datetime, end_at: datetime) -> None:
    if not isinstance(start_at, datetime) or not isinstance(end_at, datetime):
        raise InvalidTransferWindowDatesError(
            "Transfer window dates must be datetime values."
        )

    if start_at.tzinfo is None or end_at.tzinfo is None:
        raise InvalidTransferWindowDatesError(
            "Transfer window dates must be timezone-aware."
        )

    if start_at >= end_at:
        raise InvalidTransferWindowDatesError(
            "Transfer window start_at must be before end_at."
        )


def validate_transfer_window_name(name: str) -> str:
    if not isinstance(name, str):
        raise InvalidTransferWindowNameError(
            "Transfer window name must be a string."
        )

    name = " ".join(name.strip().split())

    if not name:
        raise InvalidTransferWindowNameError(
            "Transfer window name cannot be empty."
        )

    if len(name) > 100:
        raise InvalidTransferWindowNameError(
            "Transfer window name cannot exceed 100 characters."
        )

    return name


def validate_transfer_window_type(window_type: str) -> str:
    if window_type not in TransferWindowType.values:
        raise InvalidTransferWindowTypeError(
            "Invalid transfer window type."
        )
    return window_type


def validate_transfer_fee(value) -> Decimal:
    if isinstance(value, bool):
        raise InvalidTransferFeeError("Transfer fee must be a decimal amount.")

    try:
        amount = Decimal(str(value))
    except (InvalidOperation, TypeError, ValueError) as exc:
        raise InvalidTransferFeeError(
            "Transfer fee must be a valid decimal amount."
        ) from exc

    if not amount.is_finite():
        raise InvalidTransferFeeError("Transfer fee must be finite.")

    if amount < 0:
        raise InvalidTransferFeeError("Transfer fee cannot be negative.")

    exponent = amount.as_tuple().exponent
    decimal_places = max(-exponent, 0)
    digits_before_decimal = len(amount.as_tuple().digits) - decimal_places
    digits_before_decimal = max(digits_before_decimal, 0)

    if decimal_places > 2:
        raise InvalidTransferFeeError(
            "Transfer fee cannot have more than 2 decimal places."
        )

    if digits_before_decimal > 16:
        raise InvalidTransferFeeError(
            "Transfer fee exceeds the supported database range."
        )

    return amount


def validate_rejection_reason(reason: str) -> str:
    if not isinstance(reason, str):
        raise InvalidTransferRejectionReasonError(
            "Rejection reason must be text."
        )

    reason = " ".join(reason.strip().split())
    if len(reason) > 255:
        raise InvalidTransferRejectionReasonError(
            "Rejection reason cannot exceed 255 characters."
        )
    return reason


def validate_international_flag(value) -> bool:
    if not isinstance(value, bool):
        raise InvalidTransferInternationalFlagError(
            "is_international must be a boolean."
        )
    return value
