"""
Smooth Schedule Core Models
Multi-tenancy, Domain management, and Permission Grant system
"""
from django.db import models
from django.utils import timezone
from django_tenants.models import TenantMixin, DomainMixin
from datetime import timedelta


class Tenant(TenantMixin):
    """
    Tenant model for multi-schema architecture.
    Each tenant gets their own PostgreSQL schema for complete data isolation.
    """
    name = models.CharField(max_length=100)
    created_on = models.DateField(auto_now_add=True)

    # Active status
    is_active = models.BooleanField(default=True)
    # Note: Subscription and feature limits are now managed via billing.Subscription
    # Use tenant.has_feature() and tenant.get_limit() to check entitlements

    # Branding
    logo = models.ImageField(
        upload_to='tenant_logos/',
        null=True,
        blank=True,
        help_text="Website logo (recommended: 500x500px square or 500x200px wide, PNG with transparent background)"
    )
    email_logo = models.ImageField(
        upload_to='tenant_email_logos/',
        null=True,
        blank=True,
        help_text="Email logo (recommended: 600x200px wide, PNG with transparent background)"
    )
    logo_display_mode = models.CharField(
        max_length=20,
        choices=[
            ('logo-only', 'Logo Only'),
            ('text-only', 'Text Only'),
            ('logo-and-text', 'Logo and Text'),
        ],
        default='text-only',
        help_text="How to display branding in sidebar and headers"
    )
    primary_color = models.CharField(
        max_length=7,
        default='#2563eb',
        help_text="Primary brand color (hex format, e.g., #2563eb)"
    )
    secondary_color = models.CharField(
        max_length=7,
        default='#0ea5e9',
        help_text="Secondary brand color (hex format, e.g., #0ea5e9)"
    )

    # Metadata
    contact_email = models.EmailField(blank=True)
    phone = models.CharField(max_length=20, blank=True)

    # Timezone Settings
    timezone = models.CharField(
        max_length=50,
        default='America/New_York',
        help_text="Business timezone (IANA format, e.g., 'America/New_York')"
    )
    timezone_display_mode = models.CharField(
        max_length=20,
        choices=[
            ('business', 'Business Timezone'),
            ('viewer', 'Viewer Timezone'),
        ],
        default='business',
        help_text="How appointment times are displayed: 'business' shows times in the business timezone, 'viewer' shows times in each viewer's local timezone"
    )

    # Booking Settings
    booking_return_url = models.URLField(
        max_length=500,
        blank=True,
        default='',
        help_text="URL to redirect customers after they complete a booking (e.g., https://yourbusiness.com/thank-you)"
    )

    # Booking Page Customization
    service_selection_heading = models.CharField(
        max_length=100,
        blank=True,
        default='Choose your experience',
        help_text="Custom heading for service selection on booking page"
    )
    service_selection_subheading = models.CharField(
        max_length=200,
        blank=True,
        default='Select a service to begin your booking.',
        help_text="Custom subheading for service selection on booking page"
    )

    # OAuth Settings - which providers are enabled for this business
    oauth_enabled_providers = models.JSONField(
        default=list,
        blank=True,
        help_text="List of enabled OAuth providers (e.g., ['google', 'facebook', 'apple'])"
    )
    oauth_allow_registration = models.BooleanField(
        default=True,
        help_text="Allow new customers to register via OAuth"
    )
    oauth_auto_link_by_email = models.BooleanField(
        default=True,
        help_text="Automatically link OAuth accounts to existing accounts with matching email"
    )

    # Custom OAuth Credentials (for businesses that want their own branding)
    oauth_use_custom_credentials = models.BooleanField(
        default=False,
        help_text="Use business's own OAuth app credentials instead of platform credentials"
    )
    oauth_credentials = models.JSONField(
        default=dict,
        blank=True,
        help_text="Custom OAuth credentials for each provider (encrypted at rest)"
    )

    # Feature permissions and limits are now managed via billing.Subscription
    # Use tenant.has_feature('feature_code') to check boolean features
    # Use tenant.get_limit('limit_code') to get integer limits

    # Stripe Payment Configuration
    payment_mode = models.CharField(
        max_length=20,
        choices=[
            ('none', 'Not Configured'),
            ('direct_api', 'Direct API Keys (Free Tier)'),
            ('connect', 'Stripe Connect (Paid Tiers)'),
        ],
        default='none',
        help_text="How this business accepts payments"
    )

    # Stripe Connect fields (for paid tier businesses)
    stripe_connect_id = models.CharField(
        max_length=50,
        blank=True,
        default='',
        help_text="Stripe Connected Account ID (acct_xxx)"
    )
    stripe_connect_status = models.CharField(
        max_length=20,
        choices=[
            ('pending', 'Pending'),
            ('onboarding', 'Onboarding'),
            ('active', 'Active'),
            ('restricted', 'Restricted'),
            ('rejected', 'Rejected'),
        ],
        default='pending',
        help_text="Status of Stripe Connect account"
    )
    stripe_charges_enabled = models.BooleanField(
        default=False,
        help_text="Whether Stripe account can accept charges"
    )
    stripe_payouts_enabled = models.BooleanField(
        default=False,
        help_text="Whether Stripe account can receive payouts"
    )
    stripe_details_submitted = models.BooleanField(
        default=False,
        help_text="Whether onboarding details have been submitted"
    )
    stripe_onboarding_complete = models.BooleanField(
        default=False,
        help_text="Whether Stripe Connect onboarding is complete"
    )

    # Direct API Keys fields (for free tier businesses)
    stripe_secret_key = models.CharField(
        max_length=255,
        blank=True,
        default='',
        help_text="Stripe Secret Key (sk_xxx) - Encrypted"
    )
    stripe_publishable_key = models.CharField(
        max_length=255,
        blank=True,
        default='',
        help_text="Stripe Publishable Key (pk_xxx)"
    )
    stripe_api_key_status = models.CharField(
        max_length=20,
        choices=[
            ('active', 'Active'),
            ('invalid', 'Invalid'),
            ('deprecated', 'Deprecated'),
        ],
        default='active',
        blank=True,
        help_text="Status of stored API keys"
    )
    stripe_api_key_validated_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When the API keys were last validated"
    )
    stripe_api_key_account_id = models.CharField(
        max_length=50,
        blank=True,
        default='',
        help_text="Stripe Account ID from validated keys"
    )
    stripe_api_key_account_name = models.CharField(
        max_length=255,
        blank=True,
        default='',
        help_text="Stripe Account name from validated keys"
    )
    stripe_api_key_error = models.TextField(
        blank=True,
        default='',
        help_text="Validation error message if keys are invalid"
    )

    # Stripe Customer (for subscriptions/billing - separate from Connect)
    stripe_customer_id = models.CharField(
        max_length=50,
        blank=True,
        default='',
        help_text="Stripe Customer ID (cus_xxx) for billing/subscriptions"
    )

    # Onboarding tracking
    initial_setup_complete = models.BooleanField(
        default=False,
        help_text="Whether the business has completed initial onboarding"
    )

    # Twilio Integration (for SMS reminders and masked calling)
    twilio_subaccount_sid = models.CharField(
        max_length=50,
        blank=True,
        default='',
        help_text="Twilio Subaccount SID for this tenant"
    )
    twilio_subaccount_auth_token = models.CharField(
        max_length=50,
        blank=True,
        default='',
        help_text="Twilio Subaccount Auth Token"
    )
    twilio_phone_number = models.CharField(
        max_length=20,
        blank=True,
        default='',
        help_text="Assigned Twilio phone number for this tenant"
    )

    # Sandbox/Test Mode
    sandbox_schema_name = models.CharField(
        max_length=63,
        blank=True,
        help_text="PostgreSQL schema name for sandbox/test mode data"
    )
    sandbox_enabled = models.BooleanField(
        default=True,
        help_text="Whether sandbox/test mode is available for this business"
    )

    # Demo/Sales Mode
    block_emails = models.BooleanField(
        default=False,
        help_text="Block all outbound emails from this tenant (for demo accounts)"
    )

    # Auto-created fields from TenantMixin:
    # - schema_name (unique, indexed)
    # - auto_create_schema
    # - auto_drop_schema

    class Meta:
        app_label = 'core'
        ordering = ['name']

    def save(self, *args, **kwargs):
        from rest_framework.exceptions import PermissionDenied

        # Auto-generate sandbox schema name if not set
        if not self.sandbox_schema_name and self.schema_name and self.schema_name != 'public':
            self.sandbox_schema_name = f"{self.schema_name}_sandbox"

        # Check white labelling permissions when saving branding settings
        if self.pk:  # Existing tenant being updated
            try:
                old_instance = Tenant.objects.get(pk=self.pk)
                # Check if branding fields are being changed
                branding_changed = (
                    self.logo != old_instance.logo or
                    self.email_logo != old_instance.email_logo or
                    self.primary_color != old_instance.primary_color or
                    self.secondary_color != old_instance.secondary_color or
                    self.logo_display_mode != old_instance.logo_display_mode
                )
                if branding_changed and not self.has_feature('can_white_label'):
                    raise PermissionDenied(
                        "Your current plan does not include White Labeling. "
                        "Please upgrade your subscription to customize branding."
                    )
            except Tenant.DoesNotExist:
                pass  # New tenant, allow

        super().save(*args, **kwargs)

    def __str__(self):
        return self.name

    def has_feature(self, permission_key):
        """
        Check if this tenant has a specific feature permission.

        Uses the billing EntitlementService as the single source of truth.
        Features are resolved from: PlanVersion > Add-ons > Overrides

        Args:
            permission_key: The feature code to check (e.g., 'can_use_sms_reminders',
                          'can_use_custom_domain', 'can_white_label')

        Returns:
            bool: True if the tenant has the feature, False otherwise
        """
        from smoothschedule.billing.services.entitlements import EntitlementService

        # Single source of truth: billing entitlements
        if hasattr(self, 'billing_subscription') and self.billing_subscription:
            return EntitlementService.has_feature(self, permission_key)

        # No subscription = no features
        return False

    def get_limit(self, feature_code):
        """
        Get the limit value for an integer feature.

        Uses the billing EntitlementService as the single source of truth.

        Args:
            feature_code: The feature code to check (e.g., 'max_users', 'max_resources')

        Returns:
            int or None: The limit value, or None if not set/no subscription
        """
        from smoothschedule.billing.services.entitlements import EntitlementService

        if hasattr(self, 'billing_subscription') and self.billing_subscription:
            return EntitlementService.get_limit(self, feature_code)

        return None


class Domain(DomainMixin):
    """
    Domain model for tenant routing.
    Supports both subdomain (tenant.chronoflow.com) and custom domains.
    """
    # Inherited from DomainMixin:
    # - domain (unique, primary key)
    # - tenant (ForeignKey to Tenant)
    # - is_primary (boolean)
    
    # Route53 integration fields for custom domains
    is_custom_domain = models.BooleanField(
        default=False,
        help_text="True if this is a custom domain (not a subdomain of smoothschedule.com)"
    )
    
    route53_zone_id = models.CharField(
        max_length=100,
        blank=True,
        null=True,
        help_text="AWS Route53 Hosted Zone ID for this custom domain"
    )
    
    route53_record_set_id = models.CharField(
        max_length=100,
        blank=True,
        null=True,
        help_text="Route53 Record Set ID for DNS verification"
    )
    
    # SSL certificate management (for future AWS ACM integration)
    ssl_certificate_arn = models.CharField(
        max_length=200,
        blank=True,
        null=True,
        help_text="AWS ACM Certificate ARN for HTTPS"
    )
    
    verified_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When the custom domain was verified via DNS"
    )

    class Meta:
        app_label = 'core'
        ordering = ['domain']
    
    def __str__(self):
        domain_type = "Custom" if self.is_custom_domain else "Subdomain"
        return f"{self.domain} ({domain_type})"
    
    def is_verified(self):
        """Check if custom domain is verified"""
        if not self.is_custom_domain:
            return True  # Subdomains are always verified
        return self.verified_at is not None

    def save(self, *args, **kwargs):
        """Override save to check custom domain permissions."""
        from rest_framework.exceptions import PermissionDenied

        # Check permissions when creating a custom domain
        if self.is_custom_domain and not self.pk:  # New custom domain
            if self.tenant and not self.tenant.has_feature('can_use_custom_domain'):
                raise PermissionDenied(
                    "Your current plan does not include Custom Domains. "
                    "Please upgrade your subscription to access this feature."
                )

        super().save(*args, **kwargs)


class PermissionGrant(models.Model):
    """
    Time-limited permission grants (30-minute window).
    Used for temporary elevated access without permanently changing user permissions.
    
    Example use cases:
    - Support agent needs temporary access to tenant data
    - Sales demo requiring elevated permissions
    - Cross-tenant operations during migrations
    """
    grantor = models.ForeignKey(
        'users.User',
        on_delete=models.CASCADE,
        related_name='granted_permissions',
        help_text="User who granted the permission"
    )
    
    grantee = models.ForeignKey(
        'users.User',
        on_delete=models.CASCADE,
        related_name='received_permissions',
        help_text="User who received the permission"
    )
    
    action = models.CharField(
        max_length=100,
        help_text="Specific action or permission being granted (e.g., 'view_billing', 'edit_settings')"
    )
    
    reason = models.TextField(
        blank=True,
        help_text="Justification for granting this permission (audit trail)"
    )
    
    granted_at = models.DateTimeField(auto_now_add=True)
    
    expires_at = models.DateTimeField(
        help_text="When this permission grant expires"
    )
    
    revoked_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="If manually revoked before expiration"
    )
    
    # Audit metadata
    ip_address = models.GenericIPAddressField(
        null=True,
        blank=True,
        help_text="IP address where grant was created"
    )
    
    user_agent = models.TextField(
        blank=True,
        help_text="Browser/client user agent"
    )

    class Meta:
        app_label = 'core'
        ordering = ['-granted_at']
        indexes = [
            models.Index(fields=['grantee', 'expires_at']),
            models.Index(fields=['grantor', 'granted_at']),
        ]
    
    def __str__(self):
        return f"{self.grantor} → {self.grantee}: {self.action} (expires {self.expires_at})"
    
    def is_active(self):
        """
        Check if this permission grant is currently active.
        Returns False if expired or revoked.
        """
        now = timezone.now()
        
        # Check if revoked
        if self.revoked_at is not None:
            return False
        
        # Check if expired
        if now >= self.expires_at:
            return False
        
        return True
    
    def revoke(self):
        """Manually revoke this permission grant"""
        if self.revoked_at is None:
            self.revoked_at = timezone.now()
            self.save(update_fields=['revoked_at'])
    
    def time_remaining(self):
        """Get timedelta of remaining active time (or None if expired/revoked)"""
        if not self.is_active():
            return None
        
        now = timezone.now()
        return self.expires_at - now
    
    @classmethod
    def create_grant(cls, grantor, grantee, action, reason="", duration_minutes=30, ip_address=None, user_agent=""):
        """
        Factory method to create a time-limited permission grant.
        
        Args:
            grantor: User granting the permission
            grantee: User receiving the permission
            action: Permission action string
            reason: Justification for audit trail
            duration_minutes: How long the grant is valid (default 30)
            ip_address: IP address of the request
            user_agent: Browser user agent
        
        Returns:
            PermissionGrant instance
        """
        now = timezone.now()
        expires_at = now + timedelta(minutes=duration_minutes)
        
        return cls.objects.create(
            grantor=grantor,
            grantee=grantee,
            action=action,
            reason=reason,
            expires_at=expires_at,
            ip_address=ip_address,
            user_agent=user_agent
        )


class OAuthCredential(models.Model):
    """
    Reusable OAuth credentials for service-level integrations.

    Used for:
    - Email access (Gmail XOAUTH2, Microsoft XOAUTH2)
    - Calendar sync (Google Calendar, Outlook Calendar)
    - Other API integrations requiring OAuth tokens

    Supports on-demand token refresh when tokens expire.
    """

    class Provider(models.TextChoices):
        GOOGLE = 'google', 'Google'
        MICROSOFT = 'microsoft', 'Microsoft'

    class Purpose(models.TextChoices):
        EMAIL = 'email', 'Email Access (IMAP/SMTP)'
        CALENDAR = 'calendar', 'Calendar Sync'
        DRIVE = 'drive', 'File Storage'

    # Owner - can be platform-level (null tenant) or tenant-specific
    tenant = models.ForeignKey(
        'core.Tenant',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='service_oauth_credentials',
        help_text="Tenant this credential belongs to. Null for platform-level credentials."
    )

    # Which user authorized this (for audit/revocation)
    authorized_by = models.ForeignKey(
        'users.User',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='authorized_oauth_credentials',
        help_text="User who authorized this OAuth connection."
    )

    # OAuth provider and purpose
    provider = models.CharField(
        max_length=20,
        choices=Provider.choices,
        help_text="OAuth provider (google, microsoft)"
    )
    purpose = models.CharField(
        max_length=20,
        choices=Purpose.choices,
        help_text="What this credential is used for"
    )

    # The email/account this credential is for
    email = models.EmailField(
        help_text="Email address associated with this OAuth credential"
    )

    # OAuth tokens (should be encrypted at rest in production)
    access_token = models.TextField(
        help_text="OAuth access token"
    )
    refresh_token = models.TextField(
        blank=True,
        default='',
        help_text="OAuth refresh token (for token renewal)"
    )
    token_expiry = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When the access token expires"
    )

    # Scopes granted
    scopes = models.JSONField(
        default=list,
        blank=True,
        help_text="OAuth scopes granted by the user"
    )

    # Status tracking
    is_valid = models.BooleanField(
        default=True,
        help_text="Whether this credential is currently valid"
    )
    last_used_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When this credential was last used"
    )
    last_error = models.TextField(
        blank=True,
        default='',
        help_text="Last error message if token refresh failed"
    )

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

    class Meta:
        app_label = 'core'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['tenant', 'provider', 'purpose']),
            models.Index(fields=['email']),
        ]
        # One credential per email/purpose combination per tenant
        unique_together = [['tenant', 'email', 'purpose']]

    def __str__(self):
        scope = self.tenant.name if self.tenant else "Platform"
        return f"{self.email} ({self.get_provider_display()} - {self.get_purpose_display()}) [{scope}]"

    def is_expired(self):
        """Check if the access token has expired."""
        if not self.token_expiry:
            return False
        return timezone.now() >= self.token_expiry

    def is_expiring_soon(self, minutes=5):
        """Check if the access token will expire within the given minutes."""
        if not self.token_expiry:
            return False
        threshold = timezone.now() + timedelta(minutes=minutes)
        return threshold >= self.token_expiry

    def needs_refresh(self):
        """Check if this credential needs token refresh."""
        return self.is_expired() or self.is_expiring_soon()

    def mark_used(self):
        """Update last_used_at timestamp."""
        self.last_used_at = timezone.now()
        self.save(update_fields=['last_used_at'])

    def mark_invalid(self, error_message=''):
        """Mark this credential as invalid (e.g., after refresh failure)."""
        self.is_valid = False
        self.last_error = error_message
        self.save(update_fields=['is_valid', 'last_error', 'updated_at'])

    def update_tokens(self, access_token, refresh_token=None, expires_in=None):
        """
        Update tokens after a successful refresh.

        Args:
            access_token: New access token
            refresh_token: New refresh token (optional, some providers rotate)
            expires_in: Token lifetime in seconds
        """
        self.access_token = access_token
        if refresh_token:
            self.refresh_token = refresh_token
        if expires_in:
            self.token_expiry = timezone.now() + timedelta(seconds=expires_in)
        self.is_valid = True
        self.last_error = ''
        self.save(update_fields=[
            'access_token', 'refresh_token', 'token_expiry',
            'is_valid', 'last_error', 'updated_at'
        ])


class QuotaOverage(models.Model):
    """
    Tracks quota overages when a tenant exceeds their plan limits.

    Created when:
    - Tenant downgrades their plan
    - Tenant's plan expires/lapses to free tier
    - Plan limits are reduced administratively

    Grace period: 30 days to resolve the overage by:
    1. Selecting which resources to archive
    2. Upgrading their plan
    3. Deleting excess resources

    After grace period expires, excess resources are automatically archived.
    """

    QUOTA_TYPES = [
        ('MAX_ADDITIONAL_USERS', 'Additional Users'),
        ('MAX_RESOURCES', 'Resources'),
        ('MAX_SERVICES', 'Services'),
        ('MAX_EMAIL_TEMPLATES', 'Email Templates'),
        ('MAX_AUTOMATED_TASKS', 'Automated Tasks'),
    ]

    STATUS_CHOICES = [
        ('ACTIVE', 'Active - Grace period in effect'),
        ('RESOLVED', 'Resolved - User reduced usage or upgraded'),
        ('ARCHIVED', 'Archived - Grace period expired, resources archived'),
        ('CANCELLED', 'Cancelled - Admin intervention'),
    ]

    tenant = models.ForeignKey(
        Tenant,
        on_delete=models.CASCADE,
        related_name='quota_overages'
    )

    quota_type = models.CharField(
        max_length=50,
        choices=QUOTA_TYPES,
        help_text="Which quota limit was exceeded"
    )

    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='ACTIVE'
    )

    # Snapshot at time of overage
    current_usage = models.IntegerField(
        help_text="Usage count when overage was detected"
    )
    allowed_limit = models.IntegerField(
        help_text="New limit after plan change"
    )
    overage_amount = models.IntegerField(
        help_text="Number of items over the limit (usage - limit)"
    )

    # Grace period tracking
    grace_period_days = models.IntegerField(
        default=30,
        help_text="Number of days before auto-archive"
    )
    detected_at = models.DateTimeField(
        auto_now_add=True,
        help_text="When the overage was first detected"
    )
    grace_period_ends_at = models.DateTimeField(
        help_text="When the grace period expires"
    )

    # Notification tracking
    initial_email_sent_at = models.DateTimeField(
        null=True, blank=True,
        help_text="When the initial overage notification was sent"
    )
    week_reminder_sent_at = models.DateTimeField(
        null=True, blank=True,
        help_text="When the 7-day warning was sent"
    )
    day_reminder_sent_at = models.DateTimeField(
        null=True, blank=True,
        help_text="When the 1-day warning was sent"
    )

    # Resolution tracking
    resolved_at = models.DateTimeField(
        null=True, blank=True,
        help_text="When the overage was resolved"
    )
    resolution_method = models.CharField(
        max_length=50,
        null=True, blank=True,
        choices=[
            ('USER_ARCHIVED', 'User selected resources to archive'),
            ('USER_DELETED', 'User deleted excess resources'),
            ('USER_UPGRADED', 'User upgraded their plan'),
            ('AUTO_ARCHIVED', 'Auto-archived after grace period'),
            ('ADMIN_RESOLVED', 'Resolved by admin'),
        ]
    )

    # JSON field to track which resources were archived
    archived_resource_ids = models.JSONField(
        default=list,
        blank=True,
        help_text="IDs of resources that were archived due to this overage"
    )

    class Meta:
        app_label = 'core'
        ordering = ['-detected_at']
        indexes = [
            models.Index(fields=['tenant', 'status']),
            models.Index(fields=['grace_period_ends_at', 'status']),
        ]

    def __str__(self):
        return f"{self.tenant.name} - {self.get_quota_type_display()} overage ({self.overage_amount} over)"

    def save(self, *args, **kwargs):
        # Auto-calculate grace period end date on creation
        if not self.pk and not self.grace_period_ends_at:
            self.grace_period_ends_at = timezone.now() + timedelta(days=self.grace_period_days)

        # Auto-calculate overage amount
        self.overage_amount = max(0, self.current_usage - self.allowed_limit)

        super().save(*args, **kwargs)

    @property
    def days_remaining(self):
        """Returns the number of days remaining in the grace period."""
        if self.status != 'ACTIVE':
            return 0
        remaining = (self.grace_period_ends_at - timezone.now()).days
        return max(0, remaining)

    @property
    def is_grace_period_expired(self):
        """Check if the grace period has expired."""
        return timezone.now() >= self.grace_period_ends_at

    def resolve(self, method, archived_ids=None):
        """Mark this overage as resolved."""
        self.status = 'RESOLVED' if method != 'AUTO_ARCHIVED' else 'ARCHIVED'
        self.resolved_at = timezone.now()
        self.resolution_method = method
        if archived_ids:
            self.archived_resource_ids = archived_ids
        self.save()


class TenantStorageUsage(models.Model):
    """
    Track storage usage for each tenant.

    Lives in public schema since it needs to track usage across all tenants.
    Updated atomically when files are uploaded or deleted.
    """
    tenant = models.OneToOneField(
        Tenant,
        on_delete=models.CASCADE,
        related_name='storage_usage',
        help_text="The tenant this usage record belongs to"
    )
    bytes_used = models.BigIntegerField(
        default=0,
        help_text="Total storage used in bytes"
    )
    file_count = models.PositiveIntegerField(
        default=0,
        help_text="Total number of media files"
    )
    last_calculated = models.DateTimeField(
        auto_now=True,
        help_text="When usage was last updated"
    )

    class Meta:
        app_label = 'core'
        verbose_name = 'Tenant Storage Usage'
        verbose_name_plural = 'Tenant Storage Usage'

    def __str__(self):
        mb_used = self.bytes_used / (1024 * 1024)
        return f"{self.tenant.name}: {mb_used:.2f} MB ({self.file_count} files)"

    def add_file(self, file_size_bytes):
        """Add a file to the usage counter."""
        from django.db.models import F
        TenantStorageUsage.objects.filter(pk=self.pk).update(
            bytes_used=F('bytes_used') + file_size_bytes,
            file_count=F('file_count') + 1
        )
        self.refresh_from_db()

    def remove_file(self, file_size_bytes):
        """Remove a file from the usage counter."""
        from django.db.models import F
        TenantStorageUsage.objects.filter(pk=self.pk).update(
            bytes_used=F('bytes_used') - file_size_bytes,
            file_count=F('file_count') - 1
        )
        self.refresh_from_db()
        # Ensure we don't go negative
        if self.bytes_used < 0:
            self.bytes_used = 0
            self.save(update_fields=['bytes_used'])
        if self.file_count < 0:
            self.file_count = 0
            self.save(update_fields=['file_count'])

