"""
Tenant Models — Full enterprise tenant management.

Includes:
- Tenant (extended with lifecycle, subscription, security, branding)
- Domain
- TenantSettings (structured JSON settings)
- TenantQuotaUsage (resource tracking)
- TenantAwareModel / TenantAwareManager (base classes for tenant-scoped models)
"""
import re
from django.db import models
from django.utils import timezone
from django_tenants.models import DomainMixin, TenantMixin


# ─── Choices ─────────────────────────────────────────────────────────────


class TenantType(models.TextChoices):
    COMPANY = "company", "Company"
    SUBSIDIARY = "subsidiary", "Subsidiary"
    PARTNER = "partner", "Partner"
    INTERNAL = "internal", "Internal"
    TRIAL = "trial", "Trial"


class TenantStatus(models.TextChoices):
    PENDING = "pending", "Pending"
    ACTIVE = "active", "Active"
    SUSPENDED = "suspended", "Suspended"
    ARCHIVED = "archived", "Archived"


class IsolationType(models.TextChoices):
    SHARED_DB = "shared_db", "Shared DB + Tenant ID"
    SEPARATE_SCHEMA = "separate_schema", "Separate Schema"
    SEPARATE_DB = "separate_db", "Separate Database"


# ─── Tenant ──────────────────────────────────────────────────────────────


class Tenant(TenantMixin):
    """Enterprise-grade Tenant model with full lifecycle management."""

    # Basic info
    name = models.CharField(max_length=255)
    legal_name = models.CharField(max_length=255, blank=True)
    slug = models.SlugField(max_length=100, unique=True)

    # Classification
    type = models.CharField(
        max_length=20,
        choices=TenantType.choices,
        default=TenantType.COMPANY,
    )
    status = models.CharField(
        max_length=20,
        choices=TenantStatus.choices,
        default=TenantStatus.PENDING,
    )

    # Hierarchy (holding → subsidiary)
    parent_tenant = models.ForeignKey(
        "self",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="children",
    )

    # Owner user — the admin/creator of this tenant
    owner = models.ForeignKey(
        "core_auth.User",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="owned_tenants",
        verbose_name="Owner",
        help_text="The owner/admin user of this tenant.",
    )

    # Subscription / plan
    subscription_plan = models.CharField(max_length=100, blank=True)
    max_users = models.PositiveIntegerField(default=10)
    max_storage_mb = models.PositiveIntegerField(default=1024)

    # Localisation (CharField — deprecated, use FK fields below)
    country = models.CharField(max_length=10, default="IR")
    default_language = models.CharField(max_length=10, default="fa")
    timezone = models.CharField(max_length=60, default="Asia/Tehran")
    currency = models.CharField(max_length=10, default="IRR")

    # Localisation FK references (preferred — linked to localization module)
    locale_country = models.ForeignKey(
        'localization.Country',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='tenants',
    )
    locale_language = models.ForeignKey(
        'localization.Language',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='tenants',
    )
    locale_timezone = models.ForeignKey(
        'localization.Timezone',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='tenants',
    )
    locale_currency = models.ForeignKey(
        'localization.Currency',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='tenants',
    )
    locale_calendar = models.ForeignKey(
        'localization.CalendarSystem',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='tenants',
    )

    # Isolation strategy
    isolation_type = models.CharField(
        max_length=20,
        choices=IsolationType.choices,
        default=IsolationType.SEPARATE_SCHEMA,
    )

    # Legacy flag kept for simple boolean checks
    is_active = models.BooleanField(default=True)

    # Lifecycle timestamps
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    activated_at = models.DateTimeField(null=True, blank=True)
    suspended_at = models.DateTimeField(null=True, blank=True)

    # Flexible data bags
    metadata = models.JSONField(default=dict, blank=True)
    settings = models.JSONField(default=dict, blank=True)

    auto_create_schema = True
    auto_drop_schema = True

    class Meta:
        db_table = "tenants"
        verbose_name = "Tenant"
        verbose_name_plural = "Tenants"

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        # Auto-generate schema_name from slug if not set
        if not self.schema_name:
            # Make slug safe for PostgreSQL schema name: lowercase, only a-z0-9_
            safe = re.sub(r"[^a-z0-9]", "_", self.slug.lower()).strip("_")
            if not safe or safe[0].isdigit():
                safe = f"t_{safe}"
            self.schema_name = safe
        super().save(*args, **kwargs)

    # ── Lifecycle helpers ──────────────────────────────────────────

    def activate(self):
        self.status = TenantStatus.ACTIVE
        self.is_active = True
        self.activated_at = timezone.now()
        self.suspended_at = None
        self.save(update_fields=["status", "is_active", "activated_at", "suspended_at", "updated_at"])

    def suspend(self, reason: str = ""):
        self.status = TenantStatus.SUSPENDED
        self.is_active = False
        self.suspended_at = timezone.now()
        if reason:
            self.metadata["suspend_reason"] = reason
        self.save(update_fields=["status", "is_active", "suspended_at", "metadata", "updated_at"])

    def archive(self):
        self.status = TenantStatus.ARCHIVED
        self.is_active = False
        self.save(update_fields=["status", "is_active", "updated_at"])


class Domain(DomainMixin):
    """Domain routing for tenants with optional SSL."""

    ssl_enabled = models.BooleanField(default=False)

    class Meta:
        db_table = "tenant_domains"
        verbose_name = "Domain"
        verbose_name_plural = "Domains"


# ─── Tenant Settings ─────────────────────────────────────────────────────


class TenantSettings(models.Model):
    """Structured per-tenant settings: features, security, branding, etc."""

    tenant = models.OneToOneField(
        Tenant,
        on_delete=models.CASCADE,
        related_name="tenant_settings",
    )

    # Feature flags — dict[str, bool]
    feature_flags = models.JSONField(default=dict, blank=True)

    # Security policy
    security_policy = models.JSONField(
        default=dict,
        blank=True,
        help_text=(
            '{"ip_whitelist": [], "enforce_2fa": false, '
            '"password_min_length": 8, "session_timeout_minutes": 30}'
        ),
    )

    # Branding / white-label
    branding = models.JSONField(
        default=dict,
        blank=True,
        help_text='{"logo_url": "", "primary_color": "#4f46e5", "secondary_color": "#e000ad"}',
    )

    # Localization overrides
    localization = models.JSONField(default=dict, blank=True)

    # Third-party integration keys (encrypted at rest recommended)
    integration_keys = models.JSONField(default=dict, blank=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "tenant_settings"
        verbose_name = "Tenant Settings"
        verbose_name_plural = "Tenant Settings"

    def __str__(self):
        return f"Settings for {self.tenant.name}"


# ─── Quota Usage ─────────────────────────────────────────────────────────


class TenantQuotaUsage(models.Model):
    """Tracks resource consumption per tenant."""

    tenant = models.OneToOneField(
        Tenant,
        on_delete=models.CASCADE,
        related_name="quota_usage",
    )

    users_count = models.PositiveIntegerField(default=0)
    storage_usage_mb = models.FloatField(default=0)
    api_calls = models.PositiveIntegerField(default=0)
    projects_count = models.PositiveIntegerField(default=0)
    records_count = models.PositiveIntegerField(default=0)

    last_calculated_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "tenant_quota_usage"
        verbose_name = "Tenant Quota Usage"
        verbose_name_plural = "Tenant Quota Usage"

    def __str__(self):
        return f"Quota for {self.tenant.name}"

    @property
    def users_percent(self):
        if self.tenant.max_users:
            return round(self.users_count / self.tenant.max_users * 100, 1)
        return 0

    @property
    def storage_percent(self):
        if self.tenant.max_storage_mb:
            return round(self.storage_usage_mb / self.tenant.max_storage_mb * 100, 1)
        return 0

    @property
    def is_near_user_limit(self):
        return self.users_percent >= 80

    @property
    def is_near_storage_limit(self):
        return self.storage_percent >= 80


# ─── Tenant-Aware Base ───────────────────────────────────────────────────


class TenantAwareManager(models.Manager):
    """Custom Manager that automatically scopes queries to the current tenant."""

    def get_queryset(self):
        qs = super().get_queryset()
        from apps.core.tenant.middleware import get_current_tenant

        tenant = get_current_tenant()
        if tenant:
            qs = qs.filter(tenant=tenant)
        return qs


class TenantAwareModel(models.Model):
    """Abstract base model for tenant-aware models (Tenant ID in rows)."""

    tenant = models.ForeignKey(
        Tenant,
        on_delete=models.CASCADE,
        related_name="%(class)s_set",
        db_index=True,
    )

    objects = TenantAwareManager()
    all_tenants = models.Manager()

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        if not self.tenant_id:
            from apps.core.tenant.middleware import get_current_tenant

            tenant = get_current_tenant()
            if tenant:
                self.tenant = tenant
        super().save(*args, **kwargs)
