import datetime

from django.db import models
from django.conf import settings


class UserRole(models.Model):
    class Role(models.TextChoices):
        SHIPPER = 'SHIPPER', 'Shipper'
        TRANSPORTER = 'TRANSPORTER', 'Transporter'
        ADMIN = 'ADMIN', 'Admin'

    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='role'
    )
    role = models.CharField(max_length=20, choices=Role.choices)
    phone = models.CharField(max_length=50, blank=True)
    language = models.CharField(max_length=10, default='en')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = 'User role'
        verbose_name_plural = 'User roles'

    def __str__(self):
        return f'{self.user.get_username()} ({self.get_role_display()})'


class ShipperProfile(models.Model):
    class AccountType(models.TextChoices):
        INDIVIDUAL = 'INDIVIDUAL', 'Individual'
        BUSINESS = 'BUSINESS', 'Business / Corporate'

    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='shipper_profile'
    )
    account_type = models.CharField(
        max_length=20,
        choices=AccountType.choices,
        default=AccountType.INDIVIDUAL
    )
    company_name = models.CharField(max_length=255, blank=True)
    business_name = models.CharField(
        max_length=255,
        blank=True,
        default='',
        help_text='Optional business / trading name (shipper signup).',
    )
    tax_id = models.CharField(
        max_length=64,
        blank=True,
        default='',
        help_text='Optional tax / VAT ID (shipper signup).',
    )
    national_id_number = models.CharField(
        max_length=64,
        blank=True,
        default='',
        help_text='Optional national ID number collected at shipper signup.',
    )
    national_id_expiry_date = models.DateField(
        null=True,
        blank=True,
        help_text='Optional national ID expiry date collected at shipper signup.',
    )
    passport_number = models.CharField(
        max_length=64,
        blank=True,
        default='',
        help_text='Optional passport number collected at shipper signup.',
    )
    passport_expiry_date = models.DateField(
        null=True,
        blank=True,
        help_text='Optional passport expiry date collected at shipper signup.',
    )
    kyc_verified = models.BooleanField(default=False)
    kyc_submitted_at = models.DateTimeField(null=True, blank=True)
    credit_approved = models.BooleanField(
        default=False,
        help_text='Admin: allow CREDIT payment method for business shippers.',
    )
    shipment_preferences = models.JSONField(default=dict, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = 'Shipper profile'
        verbose_name_plural = 'Shipper profiles'

    def __str__(self):
        return f'Shipper: {self.user.get_username()}'


class TransporterProfile(models.Model):
    class AccountType(models.TextChoices):
        DRIVER = 'DRIVER', 'Individual driver'
        TRANSPORTER_DRIVER = 'TRANSPORTER_DRIVER', 'Fleet driver (created by fleet owner)'
        FLEET_OWNER = 'FLEET_OWNER', 'Transporter / Fleet owner'

    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='transporter_profile'
    )
    account_type = models.CharField(
        max_length=20,
        choices=AccountType.choices,
        default=AccountType.DRIVER
    )
    company_name = models.CharField(max_length=255, blank=True)
    office_number = models.CharField(
        max_length=50,
        blank=True,
        default='',
        help_text='Company office phone (fleet owner).',
    )
    mobile_number = models.CharField(
        max_length=50,
        blank=True,
        default='',
        help_text='Company mobile contact (fleet owner).',
    )
    company_location = models.CharField(
        max_length=500,
        blank=True,
        default='',
        help_text='Company location / address (fleet owner).',
    )
    avatar_url = models.URLField(
        max_length=500,
        blank=True,
        default='',
        help_text='Absolute URL of the transporter/driver avatar image.',
    )
    country_to_country = models.BooleanField(
        default=False,
        help_text='Transporter accepts cross-border loads; mutually exclusive with local.',
    )
    local = models.BooleanField(
        default=True,
        help_text='Transporter accepts local loads only; mutually exclusive with country_to_country.',
    )
    documents_verified = models.BooleanField(default=False)
    tc_id = models.CharField(max_length=50, blank=True)
    tc_u_id = models.CharField(max_length=10, blank=True, unique=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = 'Transporter profile'
        verbose_name_plural = 'Transporter profiles'
        constraints = [
            models.CheckConstraint(
                condition=models.Q(country_to_country=True, local=False)
                | models.Q(country_to_country=False, local=True),
                name='transporter_load_type_xor',
            ),
        ]

    def __str__(self):
        return f'Transporter: {self.user.get_username()}'


class TransporterDriverLink(models.Model):
    """
    Fleet owner <-> driver relationship (explicit, independent of vehicle assignment).
    """

    transporter = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='fleet_drivers',
    )
    driver = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='fleet_driver_links',
    )
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['transporter', 'driver'],
                name='uniq_transporter_driver_link',
            )
        ]

    def __str__(self):
        return f'{self.transporter_id} -> {self.driver_id} (active={self.is_active})'


class KYCDocument(models.Model):
    class DocumentType(models.TextChoices):
        # Shipper
        ID = 'ID', 'Identity document'
        BUSINESS_REGISTRATION = 'BUSINESS_REGISTRATION', 'Business registration'
        OTHER = 'OTHER', 'Other'
        # Individual driver (transporter account_type=DRIVER)
        DRIVER_LICENSE = 'DRIVER_LICENSE', 'Driver license'
        VEHICLE_REGISTRATION = 'VEHICLE_REGISTRATION', 'Vehicle registration'
        PASSPORT_COPY = 'PASSPORT_COPY', 'Passport copy'
        PERMIT = 'PERMIT', 'Permit (if applicable)'
        COUNTRY_GCC = 'COUNTRY_GCC', 'GCC ID'
        NOC = 'NOC', 'NOC'
        # Fleet owner / company transporter (account_type=FLEET_OWNER)
        COMPANY_LICENSE = 'COMPANY_LICENSE', 'Company License'
        # Legacy fleet types (kept for existing rows; no longer accepted for new uploads)
        COMPANY_REGISTRATION = 'COMPANY_REGISTRATION', 'Company Registration Certificate'
        COMPANY_OWNERSHIP = 'COMPANY_OWNERSHIP', 'Company Ownership Documents'
        COMPANY_ADDRESS_PROOF = 'COMPANY_ADDRESS_PROOF', 'Company Address Proof'
        OTHER_COMPLIANCE = 'OTHER_COMPLIANCE', 'Other transporter-specific compliance documents'
        TRADE_LICENSE = 'TRADE_LICENSE', 'Trade license copy'

    class ReviewStatus(models.TextChoices):
        PENDING = 'PENDING', 'Pending'
        APPROVED = 'APPROVED', 'Approved'
        REJECTED = 'REJECTED', 'Rejected'

    DRIVER_DOC_TYPES = frozenset({
        DocumentType.DRIVER_LICENSE,
        DocumentType.PASSPORT_COPY,
        DocumentType.PERMIT,
        DocumentType.COUNTRY_GCC,
        DocumentType.NOC,  # optional
    })

    # Shown / required as personal KYC (vehicle registration is vehicle docs only).
    DRIVER_PERSON_KYC_TYPES = frozenset({
        DocumentType.DRIVER_LICENSE,
        DocumentType.PASSPORT_COPY,
        DocumentType.PERMIT,
        DocumentType.COUNTRY_GCC,
    })

    LEGACY_VEHICLE_KYC_TYPES = frozenset({
        DocumentType.VEHICLE_REGISTRATION,
    })

    # Fleet owner signup docs: Company License, GCC ID (+ country), Owner Passport.
    FLEET_OWNER_DOC_TYPES = frozenset({
        DocumentType.COMPANY_LICENSE,
        DocumentType.COUNTRY_GCC,
        DocumentType.PASSPORT_COPY,
    })

    TRANSPORTER_DOC_TYPES = DRIVER_DOC_TYPES | FLEET_OWNER_DOC_TYPES | LEGACY_VEHICLE_KYC_TYPES

    SHIPPER_DOC_TYPES = frozenset({
        DocumentType.ID,
        DocumentType.BUSINESS_REGISTRATION,
        DocumentType.OTHER,
        DocumentType.PASSPORT_COPY,
    })

    TRANSPORTER_TWO_SIDED_TYPES = frozenset({
        DocumentType.DRIVER_LICENSE,
        DocumentType.COUNTRY_GCC,
    })

    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='kyc_documents'
    )
    document_type = models.CharField(max_length=30, choices=DocumentType.choices)
    gcc_country = models.CharField(
        max_length=2,
        blank=True,
        default='',
        help_text='ISO country code for COUNTRY_GCC uploads (GCC members only).',
    )
    passport_number = models.CharField(
        max_length=64,
        blank=True,
        null=True,
        unique=True,
        help_text='Passport number for PASSPORT_COPY; unique across all users.',
    )
    file = models.FileField(upload_to='kyc/%Y/%m/%d/', help_text='Front side (or sole file for single-sided docs).')
    file_back = models.FileField(
        upload_to='kyc/%Y/%m/%d/',
        blank=True,
        help_text='Back side for two-sided transporter documents.',
    )
    verified = models.BooleanField(
        default=False,
        help_text='Synced alias of review_status == APPROVED.',
    )
    review_status = models.CharField(
        max_length=20,
        choices=ReviewStatus.choices,
        default=ReviewStatus.PENDING,
    )
    review_notes = models.TextField(
        blank=True,
        help_text='Admin rejection reason or review comment.',
    )
    submitted_at = models.DateTimeField(auto_now_add=True)
    reviewed_at = models.DateTimeField(null=True, blank=True)
    reviewed_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        related_name='reviewed_kyc_documents',
        null=True,
        blank=True,
    )
    expiry_date = models.DateField(
        null=True,
        blank=True,
        help_text='Optional; used for compliance reminders.',
    )

    class Meta:
        verbose_name = 'KYC document'
        verbose_name_plural = 'KYC documents'

    def __str__(self):
        return f'{self.user.get_username()} - {self.get_document_type_display()}'

    def sync_verified_flag(self):
        self.verified = self.review_status == self.ReviewStatus.APPROVED


class Vehicle(models.Model):
    owner = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='fleet_vehicles'
    )
    assigned_driver = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        related_name='assigned_fleet_vehicles',
        null=True,
        blank=True
    )
    vehicle_type = models.CharField(max_length=100)
    vehicle_types = models.JSONField(
        default=list,
        blank=True,
        help_text='All vehicle types this unit can serve; vehicle_type is kept as the primary (first) entry.',
    )
    registration_number = models.CharField(max_length=100)
    load_capacity = models.DecimalField(max_digits=12, decimal_places=2)
    max_length_m = models.DecimalField(
        max_digits=8, decimal_places=3, null=True, blank=True,
        help_text='Max cargo length (m); optional, for dimension matching.',
    )
    max_width_m = models.DecimalField(
        max_digits=8, decimal_places=3, null=True, blank=True,
        help_text='Max cargo width (m); optional, for dimension matching.',
    )
    max_height_m = models.DecimalField(
        max_digits=8, decimal_places=3, null=True, blank=True,
        help_text='Max cargo height (m); optional, for dimension matching.',
    )
    special_features = models.TextField(blank=True)
    avatar_url = models.URLField(
        max_length=500,
        blank=True,
        default='',
        help_text='Absolute URL of the vehicle photo/avatar.',
    )
    is_active = models.BooleanField(default=False)
    is_verified = models.BooleanField(default=False)
    verified_at = models.DateTimeField(null=True, blank=True)
    verified_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        related_name='verified_fleet_vehicles',
        null=True,
        blank=True
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-created_at']
        constraints = [
            models.UniqueConstraint(
                fields=['owner', 'registration_number'],
                name='uniq_vehicle_registration_per_owner'
            )
        ]

    def type_list(self) -> list[str]:
        """Normalized non-empty type strings for matching and display."""
        types = self.vehicle_types if isinstance(self.vehicle_types, list) else []
        cleaned = [str(t).strip() for t in types if str(t).strip()]
        if cleaned:
            return cleaned
        primary = (self.vehicle_type or '').strip()
        return [primary] if primary else []

    def save(self, *args, **kwargs):
        types = self.type_list()
        if types:
            self.vehicle_types = types
            self.vehicle_type = types[0]
        elif self.vehicle_type:
            self.vehicle_types = [self.vehicle_type.strip()]
            self.vehicle_type = self.vehicle_types[0]
        super().save(*args, **kwargs)

    def __str__(self):
        types = self.type_list()
        label = ', '.join(types) if types else self.vehicle_type
        return f'{self.registration_number} ({label})'


class VehicleDocument(models.Model):
    class DocumentType(models.TextChoices):
        NOC = 'NOC', 'NOC'
        INSURANCE = 'INSURANCE', 'Insurance'
        PERMIT = 'PERMIT', 'Permit'
        FITNESS = 'FITNESS', 'Fitness certificate'
        OTHER = 'OTHER', 'Other'

    class ReviewStatus(models.TextChoices):
        PENDING = 'PENDING', 'Pending'
        APPROVED = 'APPROVED', 'Approved'
        REJECTED = 'REJECTED', 'Rejected'

    vehicle = models.ForeignKey(
        Vehicle,
        on_delete=models.CASCADE,
        related_name='documents'
    )
    document_type = models.CharField(max_length=20, choices=DocumentType.choices)
    file = models.FileField(upload_to='vehicle-docs/%Y/%m/%d/')
    review_status = models.CharField(
        max_length=20,
        choices=ReviewStatus.choices,
        default=ReviewStatus.PENDING
    )
    review_notes = models.TextField(blank=True)
    submitted_at = models.DateTimeField(auto_now_add=True)
    reviewed_at = models.DateTimeField(null=True, blank=True)
    reviewed_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        related_name='reviewed_vehicle_documents',
        null=True,
        blank=True
    )
    expiry_date = models.DateField(
        null=True,
        blank=True,
        help_text='Optional; used for compliance reminders.',
    )

    class Meta:
        ordering = ['-submitted_at']

    def __str__(self):
        return f'Vehicle #{self.vehicle_id} - {self.document_type}'


class PlatformSettings(models.Model):
    """Singleton (pk=1): platform-wide defaults for discovery and related rules."""

    load_visibility_radius_km = models.DecimalField(
        max_digits=6,
        decimal_places=2,
        default=50,
        help_text='Default search radius when transporter omits radius and distance_range.',
    )
    load_visibility_max_radius_km = models.DecimalField(
        max_digits=7,
        decimal_places=2,
        default=500,
        help_text='Upper cap for client-supplied radius_km.',
    )
    default_wallet_currency = models.CharField(
        max_length=3,
        default='USD',
        help_text='ISO 4217 currency code for wallet balances and billing.',
    )
    document_reminder_days = models.PositiveIntegerField(
        default=30,
        help_text='Days ahead to notify transporters of expiring documents.',
    )
    analytics_transporter_bid_window_days = models.PositiveIntegerField(
        default=90,
        help_text='Bid window for transporter acceptance-rate analytics.',
    )
    on_time_delivery_grace_hours = models.PositiveIntegerField(
        default=24,
        help_text='Grace period after scheduled pickup for on-time delivery stats.',
    )
    nearby_load_notify_radius_km = models.DecimalField(
        max_digits=8,
        decimal_places=2,
        default=50,
        help_text='FCM radius when a shipment is published (requires Traccar).',
    )
    nearby_publish_notify_max = models.PositiveIntegerField(
        default=50,
        help_text='Max transporters notified per nearby-load publish event.',
    )
    navigation_naive_speed_kph = models.DecimalField(
        max_digits=6,
        decimal_places=2,
        default=45,
        help_text='Assumed speed for straight-line navigation ETA hints.',
    )
    pod_max_delivery_distance_km = models.DecimalField(
        max_digits=8,
        decimal_places=2,
        default=0,
        help_text='POD GPS check vs delivery coords (0 = disabled).',
    )
    admin_stale_trip_hours = models.PositiveIntegerField(
        default=48,
        help_text='Hours without trip update before dashboard stale alert.',
    )
    bidding_rate_distribution_count = models.PositiveIntegerField(
        default=1,
        help_text='Number of drivers who receive load rate requests simultaneously (1 = sequential dispatch, >1 = multi-driver broadcast).',
    )

    class Meta:
        verbose_name = 'Platform settings'
        verbose_name_plural = 'Platform settings'

    def save(self, *args, **kwargs):
        self.pk = 1
        super().save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        pass

    @classmethod
    def load(cls):
        from accounts.platform_config import env_defaults_for_platform_settings

        obj, _ = cls.objects.get_or_create(pk=1, defaults=env_defaults_for_platform_settings())
        return obj

    def __str__(self):
        return 'Platform settings'


class UserPushDevice(models.Model):
    """FCM token storage (1:1 per user; last device wins)."""
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='push_device',
    )
    fcm_token = models.TextField(blank=True)
    fcm_token_updated_at = models.DateTimeField(null=True, blank=True)

    def __str__(self):
        return f'PushDevice user={self.user_id}'


class DocumentExpiryReminderLog(models.Model):
    """One row per reminder send: day windows (31/7/1) or hourly on/after expiry day."""

    REMINDER_HOUR_DAY_WINDOW = -1
    REMINDER_ON_DAY_WINDOW = datetime.date(1970, 1, 1)

    class DocKind(models.TextChoices):
        KYC = 'KYC', 'KYC'
        VEHICLE = 'VEHICLE', 'Vehicle document'

    kind = models.CharField(max_length=10, choices=DocKind.choices)
    document_id = models.PositiveIntegerField()
    expiry_date = models.DateField()
    days_before = models.PositiveSmallIntegerField(
        default=31,
        help_text='Days before expiry for advance reminders; 0 = hourly on/after expiry.',
    )
    reminder_hour = models.SmallIntegerField(
        default=REMINDER_HOUR_DAY_WINDOW,
        help_text='Local hour (0-23) for hourly reminders; -1 for day-window reminders.',
    )
    reminder_on = models.DateField(
        default=REMINDER_ON_DAY_WINDOW,
        help_text='Calendar date for hourly reminders; sentinel for day-window reminders.',
    )
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['kind', 'document_id', 'expiry_date', 'days_before', 'reminder_hour', 'reminder_on'],
                name='uniq_document_expiry_reminder',
            ),
        ]

    def __str__(self):
        return f'{self.kind}#{self.document_id} exp={self.expiry_date}'


class PasswordResetOTP(models.Model):
    """Temporary OTP/token for password reset flow (Shipper, Transporter, Driver)."""

    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='password_reset_otps',
    )
    email = models.EmailField()
    otp = models.CharField(max_length=10)
    token = models.CharField(max_length=64, blank=True, default='')
    is_used = models.BooleanField(default=False)
    attempts = models.PositiveIntegerField(default=0)
    expires_at = models.DateTimeField()
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name = 'Password reset OTP'
        verbose_name_plural = 'Password reset OTPs'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['email', 'otp', 'is_used']),
            models.Index(fields=['token', 'is_used']),
        ]

    def __str__(self):
        return f'PasswordResetOTP user={self.user_id} email={self.email} used={self.is_used}'

    @property
    def is_expired(self) -> bool:
        from django.utils import timezone
        return timezone.now() >= self.expires_at

    @property
    def is_valid(self) -> bool:
        return not self.is_used and not self.is_expired

