import re

from django.db import models
from django.contrib.auth.models import (
    AbstractBaseUser,
    PermissionsMixin,
    BaseUserManager,
)
from django.db.models.functions import Lower

from checkpoint.models import CheckPoint

from iems_backend.models import UUIDTimeStampedModel


class CustomUserManager(BaseUserManager):
    def normalize_email(self, email):
        email = super().normalize_email(email)
        if email:
            return email.strip().lower()
        return email

    def get_by_natural_key(self, username):
        return self.get(**{f"{self.model.USERNAME_FIELD}__iexact": username})

    def create_user(self, email, password=None, **extra_fields):
        if not email:
            raise ValueError("The Email field must be set")

        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        if password:
            user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password=None, **extra_fields):
        extra_fields.setdefault("is_staff", True)
        extra_fields.setdefault("is_superuser", True)
        extra_fields.setdefault("is_active", True)
        extra_fields.setdefault("role", "admin")

        if extra_fields.get("is_staff") is not True:
            raise ValueError("Superuser must have is_staff=True.")
        if extra_fields.get("is_superuser") is not True:
            raise ValueError("Superuser must have is_superuser=True.")

        return self.create_user(email, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin, UUIDTimeStampedModel):
    """
    Custom User model following industry standards for JWT authentication.
    """

    class Role(models.TextChoices):
        ADMIN = "admin", "Admin"
        STAFF = "staff", "Staff"

    class Status(models.TextChoices):
        ACTIVE = "active", "Active"
        INACTIVE = "inactive", "Inactive"
        SUSPENDED = "suspended", "Suspended"

    # We use 'email' as the unique identifier for JWT login natively
    email = models.EmailField(max_length=255, unique=True)
    name = models.CharField(max_length=255)

    checkpoint = models.ForeignKey(
        CheckPoint,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="users",
        help_text="The checkpoint this user is assigned to.",
    )

    role = models.CharField(max_length=50, choices=Role.choices, default=Role.STAFF)
    status = models.CharField(
        max_length=50, choices=Status.choices, default=Status.ACTIVE
    )
    designation = models.CharField(max_length=255, blank=True, null=True)

    # Required Django fields for Admin & Auth
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)

    objects = CustomUserManager()

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["name"]

    class Meta:
        db_table = "users"
        ordering = ["-created_at"]
        verbose_name = "User"
        verbose_name_plural = "Users"
        constraints = [
            models.UniqueConstraint(Lower("email"), name="users_email_ci_unique"),
        ]

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

    def save(self, *args, **kwargs):
        if self.email:
            self.email = self.__class__.objects.normalize_email(self.email)
        super().save(*args, **kwargs)


class AuditLog(UUIDTimeStampedModel):
    class ActionChoices(models.TextChoices):
        LOGIN = "LOGIN", "Login"
        LOGOUT = "LOGOUT", "Logout"
        CREATE = "CREATE", "Create"
        UPDATE = "UPDATE", "Update"
        DELETE = "DELETE", "Delete"
        STATUS_CHANGE = "STATUS_CHANGE", "Status Change"
        OTHER = "OTHER", "Other"

    user = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="audit_logs",
        help_text="The user who performed the action.",
    )
    action = models.CharField(
        max_length=20,
        choices=ActionChoices.choices,
        default=ActionChoices.OTHER,
        db_index=True,
        help_text="Category of the action performed.",
    )
    action_description = models.CharField(
        max_length=500,
        blank=True,
        default="",
        help_text="Human-readable summary, e.g. 'Created Import Submission TRK-2083-001'.",
    )
    method = models.CharField(max_length=10, help_text="HTTP method (e.g., GET, POST)")
    path = models.CharField(max_length=500, help_text="Requested URL path")
    payload = models.JSONField(
        null=True,
        blank=True,
        help_text="Request payload data (sensitive fields scrubbed)",
    )
    status_code = models.IntegerField(
        null=True, blank=True, help_text="HTTP response status code"
    )
    ip_address = models.GenericIPAddressField(
        null=True, blank=True, help_text="IP address of the user"
    )

    # Target object info for deep-linking
    target_model = models.CharField(
        max_length=100,
        blank=True,
        default="",
        db_index=True,
        help_text="Model name of the affected object, e.g. 'ImportSubmission'.",
    )
    target_object_id = models.UUIDField(
        null=True,
        blank=True,
        db_index=True,
        help_text="UUID of the affected object for deep-linking.",
    )

    # Extra context
    query_params = models.JSONField(
        null=True, blank=True, help_text="URL query parameters"
    )
    user_agent = models.CharField(
        max_length=500,
        blank=True,
        default="",
        help_text="Browser/device user agent string",
    )
    response_summary = models.JSONField(
        null=True,
        blank=True,
        help_text="Key response data (e.g. id, tracking_code) from the response body.",
    )

    class Meta:
        db_table = "users_audit_log"
        ordering = ["-created_at"]
        verbose_name = "Audit Log"
        verbose_name_plural = "Audit Logs"

    def __str__(self):
        desc = self.action_description or f"{self.method} {self.path}"
        return f"{self.user} - {desc} ({self.status_code})"

    @property
    def actor_name(self):
        if self.user_id and self.user:
            return self.user.name or self.user.email

        if isinstance(self.payload, dict) and self.payload.get("email"):
            return self.payload["email"]

        return "Unknown user"

    @property
    def target_label(self):
        target = self._humanized_target_model()
        identifier = self._target_identifier()

        if target and identifier:
            return f"{target} {identifier}"
        if target:
            return target
        if identifier:
            return str(identifier)
        return ""

    @property
    def activity_message(self):
        actor = self.actor_name

        if self.action == self.ActionChoices.LOGIN:
            if self.status_code and self.status_code >= 400:
                return f"{actor} failed to log in to the system."
            return f"{actor} logged in to the system."

        if self.action == self.ActionChoices.LOGOUT:
            return f"{actor} logged out of the system."

        description = self._activity_description_fragment()
        if description:
            return f"{actor} {description}."

        target = self.target_label or "an item"
        action_messages = {
            self.ActionChoices.CREATE: f"created {target}",
            self.ActionChoices.UPDATE: f"updated {target}",
            self.ActionChoices.DELETE: f"deleted {target}",
            self.ActionChoices.STATUS_CHANGE: f"changed the status of {target}",
        }
        return f"{actor} {action_messages.get(self.action, 'performed an action')}."

    def _activity_description_fragment(self):
        description = (self.action_description or "").strip().rstrip(".")
        if not description:
            return ""

        if self._is_submission_target() and description.startswith("Created "):
            return f"submitted {description.removeprefix('Created ')}"

        replacements = (
            ("Created ", "created "),
            ("Updated ", "updated "),
            ("Deleted ", "deleted "),
            ("Changed status of ", "changed the status of "),
            ("Uploaded ", "uploaded "),
            ("Registered ", "registered "),
            ("Approved ", "approved "),
            ("Denied ", "denied "),
            ("Failed ", "failed "),
        )
        for prefix, replacement in replacements:
            if description.startswith(prefix):
                return f"{replacement}{description.removeprefix(prefix)}"

        return f"{description[:1].lower()}{description[1:]}"

    def _humanized_target_model(self):
        if not self.target_model:
            return ""
        return re.sub(r"(?<!^)(?=[A-Z])", " ", self.target_model).lower()

    def _is_submission_target(self):
        return self.target_model in {
            "Submission",
            "ImportSubmission",
            "ExportSubmission",
            "RevenueFineSubmission",
            "HealthInspectionSubmission",
            "DestructionSubmission",
        }

    def _target_identifier(self):
        if isinstance(self.response_summary, dict):
            for key in (
                "tracking_code",
                "refrence_no",
                "reference_no",
                "code",
                "name",
                "email",
            ):
                value = self.response_summary.get(key)
                if value:
                    return value

        if self.target_object_id:
            return self.target_object_id

        return ""


class ActivityLog(AuditLog):
    class Meta:
        proxy = True
        verbose_name = "Activity Log"
        verbose_name_plural = "Activity Logs"
