"""Document expiry FCM reminders for fleet owners and individual drivers."""

from datetime import timedelta

from django.conf import settings
from django.utils import timezone

from accounts.models import (
    DocumentExpiryReminderLog,
    KYCDocument,
    TransporterProfile,
    UserRole,
    VehicleDocument,
)
from api.notify import notify_users

TRANSPORTER_EXPIRY_ACCOUNT_TYPES = (
    TransporterProfile.AccountType.DRIVER,
    TransporterProfile.AccountType.FLEET_OWNER,
)

DAY_WINDOW_HOUR = DocumentExpiryReminderLog.REMINDER_HOUR_DAY_WINDOW
DAY_WINDOW_ON = DocumentExpiryReminderLog.REMINDER_ON_DAY_WINDOW
HOURLY_DAYS_BEFORE = 0


def _reminder_days_before():
    raw = getattr(settings, 'DOCUMENT_EXPIRY_REMINDER_DAYS_BEFORE', (31, 7, 1))
    return tuple(int(d) for d in raw)


def _expiry_reminder_body(document_label: str, expiry_date, days_before: int, *, today=None) -> str:
    today = today or timezone.localdate()
    if expiry_date < today:
        return f'Your {document_label} expired on {expiry_date}. Please update it.'
    if days_before == HOURLY_DAYS_BEFORE:
        return f'Your {document_label} expires today ({expiry_date}).'
    if days_before == 1:
        return f'Your {document_label} expires tomorrow ({expiry_date}).'
    return f'Your {document_label} expires in {days_before} days on {expiry_date}.'


def _vehicle_expiry_reminder_body(document_label: str, expiry_date, days_before: int, *, today=None) -> str:
    today = today or timezone.localdate()
    if expiry_date < today:
        return f'A vehicle document ({document_label}) expired on {expiry_date}. Please update it.'
    if days_before == HOURLY_DAYS_BEFORE:
        return f'A vehicle document ({document_label}) expires today ({expiry_date}).'
    if days_before == 1:
        return f'A vehicle document ({document_label}) expires tomorrow ({expiry_date}).'
    return f'A vehicle document ({document_label}) expires in {days_before} days on {expiry_date}.'


def _notify_kyc_expiry(doc, *, days_before: int, reminder_hour: int, reminder_on, today) -> None:
    label = doc.get_document_type_display()
    payload = {
        'kind': 'KYC',
        'document_id': str(doc.pk),
        'document_type': str(doc.document_type),
        'expiry_date': doc.expiry_date.isoformat(),
        'days_before': str(days_before),
    }
    if reminder_hour >= 0:
        payload['reminder_hour'] = str(reminder_hour)
        payload['reminder_on'] = reminder_on.isoformat()
    if doc.expiry_date < today:
        payload['expired'] = 'true'
    notify_users(
        [doc.user_id],
        'DOCUMENT_EXPIRING',
        payload,
        title='Document expiring' if doc.expiry_date >= today else 'Document expired',
        body=_expiry_reminder_body(label, doc.expiry_date, days_before, today=today),
    )


def _notify_vehicle_expiry(doc, *, days_before: int, reminder_hour: int, reminder_on, today) -> None:
    label = doc.get_document_type_display()
    payload = {
        'kind': 'VEHICLE',
        'document_id': str(doc.pk),
        'vehicle_id': str(doc.vehicle_id),
        'document_type': str(doc.document_type),
        'expiry_date': doc.expiry_date.isoformat(),
        'days_before': str(days_before),
    }
    if reminder_hour >= 0:
        payload['reminder_hour'] = str(reminder_hour)
        payload['reminder_on'] = reminder_on.isoformat()
    if doc.expiry_date < today:
        payload['expired'] = 'true'
    notify_users(
        [doc.vehicle.owner_id],
        'DOCUMENT_EXPIRING',
        payload,
        title='Vehicle document expiring' if doc.expiry_date >= today else 'Vehicle document expired',
        body=_vehicle_expiry_reminder_body(label, doc.expiry_date, days_before, today=today),
    )


def _try_send_reminder(
    *,
    kind,
    document_id,
    expiry_date,
    days_before,
    reminder_hour,
    reminder_on,
    notify_fn,
) -> bool:
    _, created = DocumentExpiryReminderLog.objects.get_or_create(
        kind=kind,
        document_id=document_id,
        expiry_date=expiry_date,
        days_before=days_before,
        reminder_hour=reminder_hour,
        reminder_on=reminder_on,
    )
    if not created:
        return False
    notify_fn()
    return True


def send_document_expiry_reminders_for_days_before(days_before: int) -> int:
    """Notify transporters whose documents expire exactly `days_before` days from today."""
    today = timezone.localdate()
    target_date = today + timedelta(days=days_before)
    sent = 0

    kyc_qs = KYCDocument.objects.filter(
        user__role__role=UserRole.Role.TRANSPORTER,
        user__transporter_profile__account_type__in=TRANSPORTER_EXPIRY_ACCOUNT_TYPES,
        expiry_date=target_date,
    ).select_related('user')

    for doc in kyc_qs.iterator():
        if _try_send_reminder(
            kind=DocumentExpiryReminderLog.DocKind.KYC,
            document_id=doc.pk,
            expiry_date=doc.expiry_date,
            days_before=days_before,
            reminder_hour=DAY_WINDOW_HOUR,
            reminder_on=DAY_WINDOW_ON,
            notify_fn=lambda d=doc, db=days_before, td=today: _notify_kyc_expiry(
                d,
                days_before=db,
                reminder_hour=DAY_WINDOW_HOUR,
                reminder_on=DAY_WINDOW_ON,
                today=td,
            ),
        ):
            sent += 1

    vdoc_qs = VehicleDocument.objects.filter(
        vehicle__owner__role__role=UserRole.Role.TRANSPORTER,
        vehicle__owner__transporter_profile__account_type__in=TRANSPORTER_EXPIRY_ACCOUNT_TYPES,
        expiry_date=target_date,
    ).select_related('vehicle__owner')

    for doc in vdoc_qs.iterator():
        if _try_send_reminder(
            kind=DocumentExpiryReminderLog.DocKind.VEHICLE,
            document_id=doc.pk,
            expiry_date=doc.expiry_date,
            days_before=days_before,
            reminder_hour=DAY_WINDOW_HOUR,
            reminder_on=DAY_WINDOW_ON,
            notify_fn=lambda d=doc, db=days_before, td=today: _notify_vehicle_expiry(
                d,
                days_before=db,
                reminder_hour=DAY_WINDOW_HOUR,
                reminder_on=DAY_WINDOW_ON,
                today=td,
            ),
        ):
            sent += 1

    return sent


def send_expiry_hourly_reminders() -> int:
    """
    On expiry day and after: at most one reminder per document per local hour per day.
    Celery may run every minute; idempotency uses (expiry_date, reminder_hour, reminder_on).
    """
    today = timezone.localdate()
    reminder_hour = timezone.localtime().hour
    sent = 0

    kyc_qs = KYCDocument.objects.filter(
        user__role__role=UserRole.Role.TRANSPORTER,
        user__transporter_profile__account_type__in=TRANSPORTER_EXPIRY_ACCOUNT_TYPES,
        expiry_date__lte=today,
    ).select_related('user')

    for doc in kyc_qs.iterator():
        if _try_send_reminder(
            kind=DocumentExpiryReminderLog.DocKind.KYC,
            document_id=doc.pk,
            expiry_date=doc.expiry_date,
            days_before=HOURLY_DAYS_BEFORE,
            reminder_hour=reminder_hour,
            reminder_on=today,
            notify_fn=lambda d=doc, rh=reminder_hour, td=today: _notify_kyc_expiry(
                d,
                days_before=HOURLY_DAYS_BEFORE,
                reminder_hour=rh,
                reminder_on=td,
                today=td,
            ),
        ):
            sent += 1

    vdoc_qs = VehicleDocument.objects.filter(
        vehicle__owner__role__role=UserRole.Role.TRANSPORTER,
        vehicle__owner__transporter_profile__account_type__in=TRANSPORTER_EXPIRY_ACCOUNT_TYPES,
        expiry_date__lte=today,
    ).select_related('vehicle__owner')

    for doc in vdoc_qs.iterator():
        if _try_send_reminder(
            kind=DocumentExpiryReminderLog.DocKind.VEHICLE,
            document_id=doc.pk,
            expiry_date=doc.expiry_date,
            days_before=HOURLY_DAYS_BEFORE,
            reminder_hour=reminder_hour,
            reminder_on=today,
            notify_fn=lambda d=doc, rh=reminder_hour, td=today: _notify_vehicle_expiry(
                d,
                days_before=HOURLY_DAYS_BEFORE,
                reminder_hour=rh,
                reminder_on=td,
                today=td,
            ),
        ):
            sent += 1

    return sent


# Backwards-compatible alias
send_expiry_day_hourly_reminders = send_expiry_hourly_reminders


def run_all_document_expiry_reminders() -> dict:
    """Run day-window reminders (31/7/1) and hourly on/after expiry."""
    results = {'days_before': {}, 'expiry_hourly': 0}
    for days_before in sorted(_reminder_days_before(), reverse=True):
        results['days_before'][days_before] = send_document_expiry_reminders_for_days_before(days_before)
    results['expiry_hourly'] = send_expiry_hourly_reminders()
    return results
