import uuid

from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils import timezone


# ---------------------------------------------------------------------------
# Abstract Base Model
# ---------------------------------------------------------------------------

class BaseModel(models.Model):
    """Abstract base model providing UUID primary key and timestamps."""

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True
        ordering = ["-created_at"]


# ---------------------------------------------------------------------------
# User & Roles
# ---------------------------------------------------------------------------

class User(AbstractUser):
    """Custom user model with role-based access control."""

    class Role(models.TextChoices):
        OWNER = "owner", "Owner"
        ADMIN = "admin", "Admin"
        MANAGER = "manager", "Manager"
        TRAINER = "trainer", "Trainer"
        FRONT_DESK = "front_desk", "Front Desk"
        MEMBER = "member", "Member"

    class Gender(models.TextChoices):
        MALE = "male", "Male"
        FEMALE = "female", "Female"
        OTHER = "other", "Other"
        PREFER_NOT_TO_SAY = "prefer_not_to_say", "Prefer not to say"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    # Profile fields
    phone = models.CharField(max_length=20, blank=True)
    role = models.CharField(
        max_length=20,
        choices=Role.choices,
        default=Role.MEMBER,
    )
    avatar = models.ImageField(upload_to="avatars/", blank=True, null=True)
    date_of_birth = models.DateField(blank=True, null=True)
    gender = models.CharField(
        max_length=20,
        choices=Gender.choices,
        blank=True,
    )

    # Address fields
    address_line_1 = models.CharField(max_length=255, blank=True)
    address_line_2 = models.CharField(max_length=255, blank=True)
    city = models.CharField(max_length=100, blank=True)
    state = models.CharField(max_length=100, blank=True)
    postal_code = models.CharField(max_length=20, blank=True)
    country = models.CharField(max_length=100, blank=True)

    # Gym ownership — staff & members point to the owner who created them.
    # NULL for gym owners themselves (they ARE the gym).
    gym = models.ForeignKey(
        "self",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="gym_users",
    )

    # Membership flag
    is_active_member = models.BooleanField(default=False)

    # Timestamps
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = "User"
        verbose_name_plural = "Users"
        ordering = ["-date_joined"]

    def __str__(self):
        return f"{self.get_full_name() or self.username} ({self.get_role_display()})"


# ---------------------------------------------------------------------------
# OTP Verification
# ---------------------------------------------------------------------------

class OTPCode(BaseModel):
    """One-time password for email verification during login."""

    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="otp_codes",
    )
    code = models.CharField(max_length=6)
    expires_at = models.DateTimeField()
    is_used = models.BooleanField(default=False)

    class Meta(BaseModel.Meta):
        verbose_name = "OTP Code"
        verbose_name_plural = "OTP Codes"

    def __str__(self):
        return f"OTP for {self.user.email} ({'used' if self.is_used else 'active'})"

    @property
    def is_expired(self):
        return timezone.now() > self.expires_at

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


# ---------------------------------------------------------------------------
# Multi-Location
# ---------------------------------------------------------------------------

class Location(BaseModel):
    """Physical gym location."""

    gym = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="gym_locations",
        help_text="The gym owner this location belongs to.",
    )
    name = models.CharField(max_length=255)
    address_line_1 = models.CharField(max_length=255, blank=True)
    address_line_2 = models.CharField(max_length=255, blank=True)
    city = models.CharField(max_length=100, blank=True)
    state = models.CharField(max_length=100, blank=True)
    postal_code = models.CharField(max_length=20, blank=True)
    country = models.CharField(max_length=100, blank=True)
    phone = models.CharField(max_length=20, blank=True)
    email = models.EmailField(blank=True)
    timezone = models.CharField(max_length=50, default="Africa/Nairobi")
    is_active = models.BooleanField(default=True)
    capacity = models.PositiveIntegerField(default=0, help_text="Maximum occupancy")
    opening_hours = models.JSONField(
        default=dict,
        blank=True,
        help_text="Opening hours per day of week",
    )

    class Meta(BaseModel.Meta):
        verbose_name = "Location"
        verbose_name_plural = "Locations"

    def __str__(self):
        return self.name


class LocationSettings(BaseModel):
    """Per-location branding and business rule overrides."""

    location = models.OneToOneField(
        Location,
        on_delete=models.CASCADE,
        related_name="settings",
    )

    # Branding
    logo = models.ImageField(upload_to="locations/logos/", blank=True, null=True)
    cover_photo = models.ImageField(
        upload_to="locations/covers/", blank=True, null=True
    )
    primary_color = models.CharField(max_length=7, default="#1E40AF", blank=True)
    secondary_color = models.CharField(max_length=7, default="#F59E0B", blank=True)

    # Business rules
    business_rules = models.JSONField(
        default=dict,
        blank=True,
        help_text="Location-specific business rules (e.g. cancellation policy, late fees)",
    )

    class Meta(BaseModel.Meta):
        verbose_name = "Location Settings"
        verbose_name_plural = "Location Settings"

    def __str__(self):
        return f"Settings for {self.location.name}"


# ---------------------------------------------------------------------------
# Settings & Configuration
# ---------------------------------------------------------------------------

class GymSettings(BaseModel):
    """Per-gym configuration keyed to a gym owner."""

    gym = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="gym_settings",
        null=True,
        blank=True,
        help_text="The gym owner this settings record belongs to.",
    )
    gym_name = models.CharField(max_length=255, default="FitMtaani")
    logo = models.ImageField(upload_to="gym/logo/", blank=True, null=True)
    favicon = models.ImageField(upload_to="gym/favicon/", blank=True, null=True)
    primary_color = models.CharField(max_length=7, default="#1E40AF")
    secondary_color = models.CharField(max_length=7, default="#F59E0B")
    currency = models.CharField(max_length=3, default="KES")
    tax_rate = models.DecimalField(max_digits=5, decimal_places=2, default=16.00)
    timezone = models.CharField(max_length=50, default="Africa/Nairobi")
    business_hours = models.JSONField(
        default=dict,
        blank=True,
        help_text="Default business hours for all locations",
    )
    contact_email = models.EmailField(blank=True)
    contact_phone = models.CharField(max_length=20, blank=True)
    website_url = models.URLField(blank=True)

    class Meta(BaseModel.Meta):
        verbose_name = "Gym Settings"
        verbose_name_plural = "Gym Settings"

    def __str__(self):
        return self.gym_name


# ---------------------------------------------------------------------------
# Compliance & Security
# ---------------------------------------------------------------------------

class Waiver(BaseModel):
    """Waiver / liability document template."""

    gym = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="gym_waivers",
    )
    title = models.CharField(max_length=255)
    content = models.TextField(help_text="Full waiver text (HTML or Markdown)")
    version = models.CharField(max_length=20, default="1.0")
    is_active = models.BooleanField(default=True)

    class Meta(BaseModel.Meta):
        verbose_name = "Waiver"
        verbose_name_plural = "Waivers"

    def __str__(self):
        return f"{self.title} (v{self.version})"


class SignedWaiver(BaseModel):
    """Record of a member signing a waiver."""

    member = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="signed_waivers",
    )
    waiver = models.ForeignKey(
        Waiver,
        on_delete=models.CASCADE,
        related_name="signatures",
    )
    signature = models.TextField(
        blank=True,
        help_text="Base64-encoded signature image or typed name",
    )
    signed_at = models.DateTimeField(default=timezone.now)
    ip_address = models.GenericIPAddressField(blank=True, null=True)

    class Meta(BaseModel.Meta):
        verbose_name = "Signed Waiver"
        verbose_name_plural = "Signed Waivers"

    def __str__(self):
        return f"{self.member} signed {self.waiver}"


class Contract(BaseModel):
    """Membership or service contract."""

    gym = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="gym_contracts",
    )

    class Status(models.TextChoices):
        DRAFT = "draft", "Draft"
        ACTIVE = "active", "Active"
        EXPIRED = "expired", "Expired"
        CANCELLED = "cancelled", "Cancelled"
        TERMINATED = "terminated", "Terminated"

    member = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="contracts",
    )
    plan = models.ForeignKey(
        "members.MembershipPlan",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="contracts",
    )
    start_date = models.DateField()
    end_date = models.DateField(blank=True, null=True)
    status = models.CharField(
        max_length=20,
        choices=Status.choices,
        default=Status.DRAFT,
    )
    document = models.FileField(upload_to="contracts/", blank=True, null=True)
    signed_at = models.DateTimeField(blank=True, null=True)

    class Meta(BaseModel.Meta):
        verbose_name = "Contract"
        verbose_name_plural = "Contracts"

    def __str__(self):
        return f"Contract {self.id} - {self.member} ({self.get_status_display()})"


class IncidentReport(BaseModel):
    """Safety or operational incident report."""

    class Severity(models.TextChoices):
        LOW = "low", "Low"
        MEDIUM = "medium", "Medium"
        HIGH = "high", "High"
        CRITICAL = "critical", "Critical"

    class Status(models.TextChoices):
        OPEN = "open", "Open"
        INVESTIGATING = "investigating", "Investigating"
        RESOLVED = "resolved", "Resolved"
        CLOSED = "closed", "Closed"

    location = models.ForeignKey(
        Location,
        on_delete=models.CASCADE,
        related_name="incidents",
    )
    reported_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        related_name="incident_reports",
    )
    title = models.CharField(max_length=255)
    description = models.TextField()
    severity = models.CharField(
        max_length=20,
        choices=Severity.choices,
        default=Severity.MEDIUM,
    )
    status = models.CharField(
        max_length=20,
        choices=Status.choices,
        default=Status.OPEN,
    )
    resolved_at = models.DateTimeField(blank=True, null=True)

    class Meta(BaseModel.Meta):
        verbose_name = "Incident Report"
        verbose_name_plural = "Incident Reports"

    def __str__(self):
        return f"[{self.get_severity_display()}] {self.title}"


class AuditLog(BaseModel):
    """Immutable audit trail for sensitive actions."""

    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        related_name="audit_logs",
    )
    action = models.CharField(max_length=255)
    model_name = models.CharField(max_length=255, blank=True)
    object_id = models.CharField(max_length=255, blank=True)
    changes = models.JSONField(default=dict, blank=True)
    ip_address = models.GenericIPAddressField(blank=True, null=True)
    timestamp = models.DateTimeField(default=timezone.now)

    class Meta(BaseModel.Meta):
        verbose_name = "Audit Log"
        verbose_name_plural = "Audit Logs"
        ordering = ["-timestamp"]

    def __str__(self):
        return f"{self.user} - {self.action} @ {self.timestamp}"


# ---------------------------------------------------------------------------
# Automation
# ---------------------------------------------------------------------------

class AutomationRule(BaseModel):
    """Configurable automation / workflow rule."""

    gym = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="gym_automation_rules",
    )
    name = models.CharField(max_length=255)
    trigger_event = models.CharField(
        max_length=100,
        help_text="Event that triggers this rule (e.g. 'membership_expired', 'check_in')",
    )
    conditions = models.JSONField(
        default=dict,
        blank=True,
        help_text="Conditions that must be met for the rule to fire",
    )
    actions = models.JSONField(
        default=dict,
        blank=True,
        help_text="Actions to execute when the rule fires",
    )
    is_active = models.BooleanField(default=True)
    last_triggered = models.DateTimeField(blank=True, null=True)

    class Meta(BaseModel.Meta):
        verbose_name = "Automation Rule"
        verbose_name_plural = "Automation Rules"

    def __str__(self):
        return self.name


# ---------------------------------------------------------------------------
# Notifications
# ---------------------------------------------------------------------------

class Notification(BaseModel):
    """In-app notification for a user."""

    class NotificationType(models.TextChoices):
        INFO = "info", "Info"
        SUCCESS = "success", "Success"
        WARNING = "warning", "Warning"
        ERROR = "error", "Error"
        REMINDER = "reminder", "Reminder"

    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="notifications",
    )
    title = models.CharField(max_length=255)
    message = models.TextField()
    notification_type = models.CharField(
        max_length=20,
        choices=NotificationType.choices,
        default=NotificationType.INFO,
    )
    is_read = models.BooleanField(default=False)
    read_at = models.DateTimeField(blank=True, null=True)
    data = models.JSONField(
        default=dict,
        blank=True,
        help_text="Arbitrary payload (e.g. link, related object id)",
    )

    class Meta(BaseModel.Meta):
        verbose_name = "Notification"
        verbose_name_plural = "Notifications"

    def __str__(self):
        return f"{self.title} -> {self.user}"


# ---------------------------------------------------------------------------
# Holidays
# ---------------------------------------------------------------------------

class Holiday(BaseModel):
    """Holiday / closure date that may affect scheduling."""

    gym = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="gym_holidays",
    )
    name = models.CharField(max_length=255)
    date = models.DateField()
    location = models.ForeignKey(
        Location,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="holidays",
        help_text="Leave blank for gym-wide holiday",
    )
    is_recurring = models.BooleanField(
        default=False,
        help_text="Repeats every year on the same date",
    )
    affects_schedule = models.BooleanField(
        default=True,
        help_text="Whether classes / bookings are cancelled on this date",
    )

    class Meta(BaseModel.Meta):
        verbose_name = "Holiday"
        verbose_name_plural = "Holidays"
        ordering = ["date"]

    def __str__(self):
        return f"{self.name} ({self.date})"
