"""
Multi-Account and Authentication Models
"""
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils.translation import gettext_lazy as _
from django.core.validators import MinValueValidator, MaxValueValidator


class AccountBaseModel(models.Model):
    """
    Abstract base model for all account-isolated models.
    All models should inherit from this to ensure account isolation.
    """
    account = models.ForeignKey('igny8_core_auth.Account', on_delete=models.CASCADE, related_name='%(class)s_set', db_index=True, db_column='tenant_id')
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True
        indexes = [
            models.Index(fields=['account', 'created_at']),
        ]



class SiteSectorBaseModel(AccountBaseModel):
    """
    Abstract base model for models that belong to a Site and Sector.
    Provides automatic filtering by site/sector based on user access.
    Models like Keywords and Clusters should inherit from this.
    """
    site = models.ForeignKey('igny8_core_auth.Site', on_delete=models.CASCADE, related_name='%(class)s_set', db_index=True)
    sector = models.ForeignKey('igny8_core_auth.Sector', on_delete=models.CASCADE, related_name='%(class)s_set', db_index=True)
    
    class Meta:
        abstract = True
        indexes = [
            models.Index(fields=['account', 'site', 'sector']),
            models.Index(fields=['site', 'sector']),
        ]
    
    def save(self, *args, **kwargs):
        """Ensure site and sector belong to same account."""
        # Set account from site
        if self.site:
            self.account = self.site.account
        # Ensure sector belongs to site
        if self.sector and self.sector.site != self.site:
            from django.core.exceptions import ValidationError
            raise ValidationError("Sector must belong to the same site")
        super().save(*args, **kwargs)


class Account(models.Model):
    """
    Account/Organization model for multi-account support.
    """
    STATUS_CHOICES = [
        ('active', 'Active'),
        ('suspended', 'Suspended'),
        ('trial', 'Trial'),
        ('cancelled', 'Cancelled'),
    ]

    name = models.CharField(max_length=255)
    slug = models.SlugField(unique=True, max_length=255)
    owner = models.ForeignKey('igny8_core_auth.User', on_delete=models.PROTECT, related_name='owned_accounts')
    stripe_customer_id = models.CharField(max_length=255, blank=True, null=True)
    plan = models.ForeignKey('igny8_core_auth.Plan', on_delete=models.PROTECT, related_name='accounts')
    credits = models.IntegerField(default=0, validators=[MinValueValidator(0)])
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='trial')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'igny8_tenants'
        verbose_name = 'Account'
        verbose_name_plural = 'Accounts'
        indexes = [
            models.Index(fields=['slug']),
            models.Index(fields=['status']),
        ]

    def __str__(self):
        return self.name
    
    def is_system_account(self):
        """Check if this account is a system account with highest access level."""
        # System accounts bypass all filtering restrictions
        return self.slug in ['aws-admin', 'default-account', 'default']


class Plan(models.Model):
    """
    Subscription plan model with comprehensive limits and features.
    Plans define limits for users, sites, content generation, AI usage, and billing.
    """
    BILLING_CYCLE_CHOICES = [
        ('monthly', 'Monthly'),
        ('annual', 'Annual'),
    ]
    
    # Plan Info
    name = models.CharField(max_length=255)
    slug = models.SlugField(unique=True, max_length=255)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    billing_cycle = models.CharField(max_length=20, choices=BILLING_CYCLE_CHOICES, default='monthly')
    features = models.JSONField(default=list, blank=True, help_text="Plan features as JSON array (e.g., ['ai_writer', 'image_gen', 'auto_publish'])")
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    
    # User / Site / Scope Limits
    max_users = models.IntegerField(default=1, validators=[MinValueValidator(1)], help_text="Total users allowed per account")
    max_sites = models.IntegerField(
        default=1,
        validators=[MinValueValidator(1)],
        help_text="Maximum number of sites allowed"
    )
    max_industries = models.IntegerField(default=None, null=True, blank=True, validators=[MinValueValidator(1)], help_text="Optional limit for industries/sectors")
    max_author_profiles = models.IntegerField(default=5, validators=[MinValueValidator(0)], help_text="Limit for saved writing styles")
    
    # Planner Limits
    max_keywords = models.IntegerField(default=1000, validators=[MinValueValidator(0)], help_text="Total keywords allowed (global limit)")
    max_clusters = models.IntegerField(default=100, validators=[MinValueValidator(0)], help_text="Total clusters allowed (global)")
    max_content_ideas = models.IntegerField(default=300, validators=[MinValueValidator(0)], help_text="Total content ideas allowed (global limit)")
    daily_cluster_limit = models.IntegerField(default=10, validators=[MinValueValidator(0)], help_text="Max clusters that can be created per day")
    daily_keyword_import_limit = models.IntegerField(default=100, validators=[MinValueValidator(0)], help_text="SeedKeywords import limit per day")
    monthly_cluster_ai_credits = models.IntegerField(default=50, validators=[MinValueValidator(0)], help_text="AI credits allocated for clustering")
    
    # Writer Limits
    daily_content_tasks = models.IntegerField(default=10, validators=[MinValueValidator(0)], help_text="Max number of content tasks (blogs) per day")
    daily_ai_requests = models.IntegerField(default=50, validators=[MinValueValidator(0)], help_text="Total AI executions (content + idea + image) allowed per day")
    monthly_word_count_limit = models.IntegerField(default=50000, validators=[MinValueValidator(0)], help_text="Monthly word limit (for generated content)")
    monthly_content_ai_credits = models.IntegerField(default=200, validators=[MinValueValidator(0)], help_text="AI credit pool for content generation")
    
    # Image Generation Limits
    monthly_image_count = models.IntegerField(default=100, validators=[MinValueValidator(0)], help_text="Max images per month")
    daily_image_generation_limit = models.IntegerField(default=25, validators=[MinValueValidator(0)], help_text="Max images that can be generated per day")
    monthly_image_ai_credits = models.IntegerField(default=100, validators=[MinValueValidator(0)], help_text="AI credit pool for image generation")
    max_images_per_task = models.IntegerField(default=4, validators=[MinValueValidator(1)], help_text="Max images per content task")
    image_model_choices = models.JSONField(default=list, blank=True, help_text="Allowed image models (e.g., ['dalle3', 'hidream'])")
    
    # AI Request Controls
    daily_ai_request_limit = models.IntegerField(default=100, validators=[MinValueValidator(0)], help_text="Global daily AI request cap")
    monthly_ai_credit_limit = models.IntegerField(default=500, validators=[MinValueValidator(0)], help_text="Unified credit ceiling per month (all AI functions)")
    
    # Billing & Add-ons
    included_credits = models.IntegerField(default=0, validators=[MinValueValidator(0)], help_text="Monthly credits included")
    extra_credit_price = models.DecimalField(max_digits=10, decimal_places=2, default=0.01, help_text="Price per additional credit")
    allow_credit_topup = models.BooleanField(default=True, help_text="Can user purchase more credits?")
    auto_credit_topup_threshold = models.IntegerField(default=None, null=True, blank=True, validators=[MinValueValidator(0)], help_text="Auto top-up trigger point (optional)")
    auto_credit_topup_amount = models.IntegerField(default=None, null=True, blank=True, validators=[MinValueValidator(1)], help_text="How many credits to auto-buy")
    
    # Stripe Integration
    stripe_product_id = models.CharField(max_length=255, blank=True, null=True, help_text="For Stripe plan sync")
    stripe_price_id = models.CharField(max_length=255, blank=True, null=True, help_text="Monthly price ID for Stripe")
    
    # Legacy field for backward compatibility
    credits_per_month = models.IntegerField(default=0, validators=[MinValueValidator(0)], help_text="DEPRECATED: Use included_credits instead")

    class Meta:
        db_table = 'igny8_plans'
        ordering = ['price']

    def __str__(self):
        return self.name
    
    def clean(self):
        """Validate plan limits."""
        from django.core.exceptions import ValidationError
        if self.max_sites < 1:
            raise ValidationError("max_sites must be >= 1")
        if self.included_credits < 0:
            raise ValidationError("included_credits must be >= 0")
    
    def get_effective_credits_per_month(self):
        """Get effective credits per month (use included_credits if set, otherwise credits_per_month for backward compatibility)."""
        return self.included_credits if self.included_credits > 0 else self.credits_per_month


class Subscription(models.Model):
    """
    Account subscription model linking to Stripe.
    """
    STATUS_CHOICES = [
        ('active', 'Active'),
        ('past_due', 'Past Due'),
        ('canceled', 'Canceled'),
        ('trialing', 'Trialing'),
    ]

    account = models.OneToOneField('igny8_core_auth.Account', on_delete=models.CASCADE, related_name='subscription', db_column='tenant_id')
    stripe_subscription_id = models.CharField(max_length=255, unique=True)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES)
    current_period_start = models.DateTimeField()
    current_period_end = models.DateTimeField()
    cancel_at_period_end = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'igny8_subscriptions'
        indexes = [
            models.Index(fields=['status']),
        ]

    def __str__(self):
        return f"{self.account.name} - {self.status}"
    


class Site(AccountBaseModel):
    """
    Site model - Each account can have multiple sites based on their plan.
    Each site belongs to ONE industry and can have 1-5 sectors from that industry.
    """
    STATUS_CHOICES = [
        ('active', 'Active'),
        ('inactive', 'Inactive'),
        ('suspended', 'Suspended'),
    ]
    
    name = models.CharField(max_length=255)
    slug = models.SlugField(max_length=255)
    domain = models.URLField(blank=True, null=True, help_text="Primary domain URL")
    description = models.TextField(blank=True, null=True)
    industry = models.ForeignKey(
        'igny8_core_auth.Industry',
        on_delete=models.PROTECT,
        related_name='sites',
        null=True,
        blank=True,
        help_text="Industry this site belongs to"
    )
    is_active = models.BooleanField(default=True, db_index=True)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    # WordPress integration fields
    wp_url = models.URLField(blank=True, null=True, help_text="WordPress site URL")
    wp_username = models.CharField(max_length=255, blank=True, null=True)
    wp_app_password = models.CharField(max_length=255, blank=True, null=True)
    
    class Meta:
        db_table = 'igny8_sites'
        unique_together = [['account', 'slug']]  # Slug unique per account
        ordering = ['-created_at']  # Order by creation date for consistent pagination
        indexes = [
            models.Index(fields=['account', 'is_active']),
            models.Index(fields=['account', 'status']),
            models.Index(fields=['industry']),
        ]
    
    def __str__(self):
        return f"{self.account.name} - {self.name}"
    
    
    def get_active_sectors_count(self):
        """Get count of active sectors for this site."""
        return self.sectors.filter(is_active=True).count()
    
    def get_max_sectors_limit(self):
        """Get the maximum sectors allowed for this site based on plan, defaulting to 5 if not set."""
        try:
            if self.account and self.account.plan and self.account.plan.max_industries is not None:
                return self.account.plan.max_industries
        except (AttributeError, Exception):
            pass
        # Default limit: 5 sectors per site
        return 5
    
    def can_add_sector(self):
        """Check if site can add another sector based on plan limits."""
        return self.get_active_sectors_count() < self.get_max_sectors_limit()


class Industry(models.Model):
    """
    Industry model - Global industry templates.
    These are predefined industry definitions that sites can reference.
    """
    name = models.CharField(max_length=255, unique=True)
    slug = models.SlugField(unique=True, max_length=255, db_index=True)
    description = models.TextField(blank=True, null=True)
    is_active = models.BooleanField(default=True, db_index=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'igny8_industries'
        ordering = ['name']
        verbose_name = 'Industry'
        verbose_name_plural = 'Industries'
        indexes = [
            models.Index(fields=['slug']),
            models.Index(fields=['is_active']),
        ]
    
    def __str__(self):
        return self.name


class IndustrySector(models.Model):
    """
    Industry Sector model - Sector templates within industries.
    These define the available sectors for each industry.
    """
    industry = models.ForeignKey('igny8_core_auth.Industry', on_delete=models.CASCADE, related_name='sectors')
    name = models.CharField(max_length=255)
    slug = models.SlugField(max_length=255, db_index=True)
    description = models.TextField(blank=True, null=True)
    suggested_keywords = models.JSONField(default=list, help_text='List of suggested keywords for this sector template')
    is_active = models.BooleanField(default=True, db_index=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'igny8_industry_sectors'
        unique_together = [['industry', 'slug']]  # Slug unique per industry
        verbose_name = 'Industry Sector'
        verbose_name_plural = 'Industry Sectors'
        indexes = [
            models.Index(fields=['industry', 'is_active']),
            models.Index(fields=['slug']),
        ]
        ordering = ['industry', 'name']
    
    def __str__(self):
        return f"{self.industry.name} - {self.name}"


class SeedKeyword(models.Model):
    """
    Global, permanent keyword suggestions scoped by industry + sector.
    These are canonical keywords that can be imported into account-specific Keywords.
    Non-deletable global reference data.
    """
    INTENT_CHOICES = [
        ('informational', 'Informational'),
        ('navigational', 'Navigational'),
        ('commercial', 'Commercial'),
        ('transactional', 'Transactional'),
    ]
    
    keyword = models.CharField(max_length=255, db_index=True)
    industry = models.ForeignKey('igny8_core_auth.Industry', on_delete=models.CASCADE, related_name='seed_keywords')
    sector = models.ForeignKey('igny8_core_auth.IndustrySector', on_delete=models.CASCADE, related_name='seed_keywords')
    volume = models.IntegerField(default=0, help_text='Search volume estimate')
    difficulty = models.IntegerField(
        default=0,
        validators=[MinValueValidator(0), MaxValueValidator(100)],
        help_text='Keyword difficulty (0-100)'
    )
    intent = models.CharField(max_length=50, choices=INTENT_CHOICES, default='informational')
    is_active = models.BooleanField(default=True, db_index=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'igny8_seed_keywords'
        unique_together = [['keyword', 'industry', 'sector']]
        verbose_name = 'Seed Keyword'
        verbose_name_plural = 'Seed Keywords'
        indexes = [
            models.Index(fields=['keyword']),
            models.Index(fields=['industry', 'sector']),
            models.Index(fields=['industry', 'sector', 'is_active']),
            models.Index(fields=['intent']),
        ]
        ordering = ['keyword']
    
    def __str__(self):
        return f"{self.keyword} ({self.industry.name} - {self.sector.name})"


class Sector(AccountBaseModel):
    """
    Sector model - Each site can have 1-5 sectors.
    Sectors are site-specific instances that reference an IndustrySector template.
    Sectors contain keywords and clusters.
    """
    STATUS_CHOICES = [
        ('active', 'Active'),
        ('inactive', 'Inactive'),
    ]
    
    site = models.ForeignKey('igny8_core_auth.Site', on_delete=models.CASCADE, related_name='sectors')
    industry_sector = models.ForeignKey(
        'igny8_core_auth.IndustrySector',
        on_delete=models.PROTECT,
        related_name='site_sectors',
        null=True,
        blank=True,
        help_text="Reference to the industry sector template"
    )
    name = models.CharField(max_length=255)
    slug = models.SlugField(max_length=255)
    description = models.TextField(blank=True, null=True)
    is_active = models.BooleanField(default=True, db_index=True)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'igny8_sectors'
        unique_together = [['site', 'slug']]  # Slug unique per site
        indexes = [
            models.Index(fields=['site', 'is_active']),
            models.Index(fields=['account', 'site']),
            models.Index(fields=['industry_sector']),
        ]
    
    def __str__(self):
        return f"{self.site.name} - {self.name}"
    
    @property
    def industry(self):
        """Get the industry for this sector."""
        return self.industry_sector.industry if self.industry_sector else None
    
    def save(self, *args, **kwargs):
        """Ensure site belongs to same account, validate sector limit, and industry match."""
        # Set account from site
        if self.site:
            self.account = self.site.account
        
        # Validate that sector's industry_sector belongs to site's industry
        if self.site and self.site.industry and self.industry_sector:
            if self.industry_sector.industry != self.site.industry:
                from django.core.exceptions import ValidationError
                raise ValidationError(
                    f"Sector must belong to site's industry ({self.site.industry.name}). "
                    f"Selected sector belongs to {self.industry_sector.industry.name}."
                )
        
        super().save(*args, **kwargs)
        
        # Validate sector limit based on plan - only for new active sectors
        if self.is_active:
            max_sectors = self.site.get_max_sectors_limit()
            if self.site.get_active_sectors_count() > max_sectors:
                from django.core.exceptions import ValidationError
                raise ValidationError(f"Maximum {max_sectors} sectors allowed per site for this plan")


class SiteUserAccess(models.Model):
    """
    Many-to-many relationship between Users and Sites.
    Controls which users can access which sites.
    Owners and Admins have access to all sites automatically.
    """
    user = models.ForeignKey('igny8_core_auth.User', on_delete=models.CASCADE, related_name='site_access')
    site = models.ForeignKey('igny8_core_auth.Site', on_delete=models.CASCADE, related_name='user_access')
    granted_at = models.DateTimeField(auto_now_add=True)
    granted_by = models.ForeignKey(
        'igny8_core_auth.User',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='granted_site_accesses'
    )
    
    class Meta:
        db_table = 'igny8_site_user_access'
        unique_together = [['user', 'site']]
        verbose_name = 'Site User Access'
        verbose_name_plural = 'Site User Access'
        indexes = [
            models.Index(fields=['user', 'site']),
        ]
    
    def __str__(self):
        return f"{self.user.email} -> {self.site.name}"


class PasswordResetToken(models.Model):
    """Password reset token model for password reset flow"""
    user = models.ForeignKey('igny8_core_auth.User', on_delete=models.CASCADE, related_name='password_reset_tokens')
    token = models.CharField(max_length=255, unique=True, db_index=True)
    expires_at = models.DateTimeField()
    used = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        db_table = 'igny8_password_reset_tokens'
        indexes = [
            models.Index(fields=['token']),
            models.Index(fields=['user', 'used']),
            models.Index(fields=['expires_at']),
        ]
        ordering = ['-created_at']
    
    def __str__(self):
        return f"Password reset token for {self.user.email}"
    
    def is_valid(self):
        """Check if token is valid (not used and not expired)"""
        from django.utils import timezone
        return not self.used and self.expires_at > timezone.now()


class User(AbstractUser):
    """
    Custom user model with account relationship and role support.
    """
    ROLE_CHOICES = [
        ('developer', 'Developer / Super Admin'),
        ('owner', 'Owner'),
        ('admin', 'Admin'),
        ('editor', 'Editor'),
        ('viewer', 'Viewer'),
        ('system_bot', 'System Bot'),
    ]

    account = models.ForeignKey('igny8_core_auth.Account', on_delete=models.CASCADE, related_name='users', null=True, blank=True, db_column='tenant_id')
    role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='viewer')
    email = models.EmailField(_('email address'), unique=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']

    class Meta:
        db_table = 'igny8_users'
        indexes = [
            models.Index(fields=['account', 'role']),
            models.Index(fields=['email']),
        ]

    def __str__(self):
        return self.email

    def has_role(self, *roles):
        """Check if user has any of the specified roles."""
        return self.role in roles

    def is_owner_or_admin(self):
        """Check if user is owner or admin."""
        return self.role in ['owner', 'admin']
    
    def is_developer(self):
        """Check if user is a developer/super admin with full access."""
        return self.role == 'developer' or self.is_superuser
    
    def is_admin_or_developer(self):
        """Check if user is admin or developer with override privileges."""
        # ADMIN/DEV OVERRIDE: Both admin and developer roles bypass account/site/sector restrictions
        return self.role in ['admin', 'developer'] or self.is_superuser
    
    def is_system_account_user(self):
        """Check if user belongs to a system account with highest access level."""
        try:
            return self.account and self.account.is_system_account()
        except (AttributeError, Exception):
            # If account access fails (e.g., column mismatch), return False
            return False
    
    def get_accessible_sites(self):
        """Get all sites the user can access."""
        # System account users can access all sites across all accounts
        if self.is_system_account_user():
            return Site.objects.filter(is_active=True).distinct()
        
        # Developers/super admins can access all sites across all accounts
        # ADMIN/DEV OVERRIDE: Admins also bypass account restrictions (see is_admin_or_developer)
        if self.is_developer():
            return Site.objects.filter(is_active=True).distinct()
        
        try:
            if not self.account:
                return Site.objects.none()
            
            # Owners and admins can access all sites in their account
            if self.role in ['owner', 'admin']:
                return Site.objects.filter(account=self.account, is_active=True)
            
            # Other users can only access sites explicitly granted via SiteUserAccess
            return Site.objects.filter(
                account=self.account,
                is_active=True,
                user_access__user=self
            ).distinct()
        except (AttributeError, Exception):
            # If account access fails (e.g., column mismatch), return empty queryset
            return Site.objects.none()
    
