"""
PREPOC ERP — Extended Base Model
All business models inherit from this.
"""
from __future__ import annotations

import uuid
from typing import TYPE_CHECKING

from django.db import models
from django.utils import timezone

if TYPE_CHECKING:
    from apps.accounts.models import User


class SoftDeleteQuerySet(models.QuerySet):
    """QuerySet that filters out soft-deleted records by default."""

    def alive(self):
        return self.filter(deleted_at__isnull=True)

    def deleted(self):
        return self.filter(deleted_at__isnull=False)

    def delete(self, deleted_by=None):
        """Soft delete all records in queryset."""
        return self.update(
            deleted_at=timezone.now(),
            deleted_by=deleted_by,
            is_active=False,
        )

    def hard_delete(self):
        """Permanently delete records."""
        return super().delete()

    def restore(self):
        """Restore soft-deleted records."""
        return self.update(deleted_at=None, deleted_by=None, is_active=True)


class SoftDeleteManager(models.Manager):
    """Default manager that excludes soft-deleted records."""

    def get_queryset(self) -> SoftDeleteQuerySet:
        return SoftDeleteQuerySet(self.model, using=self._db).alive()

    def deleted(self):
        return SoftDeleteQuerySet(self.model, using=self._db).deleted()

    def all_with_deleted(self):
        return SoftDeleteQuerySet(self.model, using=self._db)


class ExtendedBaseModel(models.Model):
    """
    Extended base model for all PREPOC ERP business entities.

    Provides:
    - UUID primary key
    - Organization FK (multi-company / SaaS isolation)
    - Created/Updated timestamps and user references
    - Soft delete with deleted_at and deleted_by
    - Remarks field
    - Automatic soft-delete manager
    """

    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False,
        db_index=True,
    )
    organization = models.ForeignKey(
        "organizations.Organization",
        on_delete=models.CASCADE,
        related_name="%(app_label)s_%(class)s_set",
        db_index=True,
    )
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)
    updated_at = models.DateTimeField(auto_now=True)
    created_by = models.ForeignKey(
        "accounts.User",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="%(app_label)s_%(class)s_created",
    )
    updated_by = models.ForeignKey(
        "accounts.User",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="%(app_label)s_%(class)s_updated",
    )
    deleted_at = models.DateTimeField(null=True, blank=True, db_index=True)
    deleted_by = models.ForeignKey(
        "accounts.User",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="%(app_label)s_%(class)s_deleted",
    )
    is_active = models.BooleanField(default=True, db_index=True)
    remarks = models.TextField(blank=True, default="")

    objects = SoftDeleteManager()
    all_objects = models.Manager()

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

    def delete(self, using=None, keep_parents=False, deleted_by=None):
        """Soft delete: set deleted_at timestamp instead of DB delete."""
        self.deleted_at = timezone.now()
        self.deleted_by = deleted_by
        self.is_active = False
        self.save(update_fields=["deleted_at", "deleted_by", "is_active", "updated_at"])

    def hard_delete(self):
        """Permanently remove from database."""
        super().delete()

    def restore(self):
        """Restore a soft-deleted record."""
        self.deleted_at = None
        self.deleted_by = None
        self.is_active = True
        self.save(update_fields=["deleted_at", "deleted_by", "is_active", "updated_at"])

    @property
    def is_deleted(self) -> bool:
        return self.deleted_at is not None


class Permission(models.Model):
    """
    Granular permission defining a specific action on a resource.
    Format: module.resource.action (e.g., hr.employee.view)
    """
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    code = models.CharField(max_length=100, unique=True, db_index=True)
    module = models.CharField(max_length=50, db_index=True)
    action = models.CharField(max_length=50)
    description = models.CharField(max_length=255)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "rbac_permissions"
        ordering = ["module", "code"]

    def __str__(self):
        return self.code


class PermissionGroup(models.Model):
    """
    Logical grouping of permissions (e.g., 'HR Management').
    """
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=100, unique=True)
    description = models.CharField(max_length=255, blank=True)
    permissions = models.ManyToManyField(Permission, related_name="groups", db_table="rbac_permission_group_permissions")
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "rbac_permission_groups"
        ordering = ["name"]

    def __str__(self):
        return self.name


class Role(models.Model):
    """
    A role assigned to a user, which inherits multiple Permission Groups.
    Can be a system default role (organization=None) or a custom org role.
    """
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    organization = models.ForeignKey(
        "organizations.Organization",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="custom_roles",
    )
    name = models.CharField(max_length=100)
    code = models.CharField(max_length=50, unique=True, db_index=True)
    description = models.CharField(max_length=255, blank=True)
    permission_groups = models.ManyToManyField(PermissionGroup, related_name="roles", db_table="rbac_role_permission_groups")
    is_active = models.BooleanField(default=True)
    is_system_default = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "rbac_roles"
        ordering = ["name"]
        unique_together = ["organization", "code"]

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

    @property
    def all_permissions(self):
        """Returns a flat list of all permission codes inherited through groups."""
        return Permission.objects.filter(groups__roles=self).values_list("code", flat=True).distinct()


class AuditLog(models.Model):
    """
    Tracks sensitive actions in the system for compliance and auditing.
    """
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    organization = models.ForeignKey(
        "organizations.Organization",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
    )
    user = models.ForeignKey(
        "accounts.User",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
    )
    action = models.CharField(max_length=255, db_index=True)
    module = models.CharField(max_length=100, db_index=True)
    resource_id = models.CharField(max_length=255, null=True, blank=True)
    ip_address = models.GenericIPAddressField(null=True, blank=True)
    details = models.JSONField(default=dict)
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)

    class Meta:
        db_table = "core_audit_logs"
        ordering = ["-created_at"]

    def __str__(self):
        return f"{self.action} on {self.module} by {self.user}"


class FeatureFlag(models.Model):
    """
    Organization-level feature flags to toggle modules dynamically.
    """
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    organization = models.ForeignKey(
        "organizations.Organization",
        on_delete=models.CASCADE,
        related_name="feature_flags",
    )
    code = models.CharField(max_length=100, db_index=True)
    is_enabled = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "core_feature_flags"
        unique_together = ["organization", "code"]

    def __str__(self):
        return f"{self.code} - {'Enabled' if self.is_enabled else 'Disabled'}"
