
from django.db import models
from django.contrib.auth import get_user_model
from decimal import Decimal
import uuid
from django.utils import timezone
from datetime import timedelta
import string
import secrets

User = get_user_model()

class CustomerTag(models.Model):
    name = models.CharField(max_length=50, unique=True)
    description = models.TextField(blank=True)
    color = models.CharField(max_length=7, default='#007bff', help_text="Hex color code for the tag")
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['name']

class Service(models.Model):
    BILLING_CYCLE_CHOICES = [
        ('monthly', 'Monthly'),
        ('quarterly', 'Quarterly'),
        ('annually', 'Annually'),
    ]

    name = models.CharField(max_length=100)
    speed_mbps = models.IntegerField(help_text="Speed in Mbps")
    fup_gb = models.IntegerField(help_text="Fair Usage Policy in GB", null=True, blank=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    billing_cycle = models.CharField(max_length=20, choices=BILLING_CYCLE_CHOICES, default='monthly')
    installation_fee = models.DecimalField(max_digits=10, decimal_places=2, default=Decimal('0.00'))
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.name} - {self.speed_mbps}Mbps"

    class Meta:
        verbose_name = "Service"
        verbose_name_plural = "Services"

# Keep Package as an alias for backward compatibility
Package = Service

class Customer(models.Model):
    CUSTOMER_TYPE_CHOICES = [
        ('individual', 'Individual'),
        ('institution', 'Institution'),
    ]
    
    SERVICE_STATUS_CHOICES = [
        ('active', 'Active'),
        ('suspended', 'Suspended'),
        ('disconnected', 'Disconnected'),
        ('trial', 'Trial Period'),
        ('pending_activation', 'Pending Activation'),
        ('inactive', 'Inactive'),
    ]
    
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='customer_profile', null=True, blank=True)
    customer_id = models.CharField(max_length=20, unique=True, editable=False)
    customer_type = models.CharField(max_length=20, choices=CUSTOMER_TYPE_CHOICES, default='individual')
    
    # Sub-account support
    parent_customer = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='sub_accounts')
    is_sub_account = models.BooleanField(default=False)
    sub_account_number = models.PositiveIntegerField(null=True, blank=True)
    
    # Individual customer fields
    full_name = models.CharField(max_length=200, blank=True, help_text="For individual customers")
    id_passport = models.CharField(max_length=50, blank=True, null=True)
    
    # Institution customer fields
    company_name = models.CharField(max_length=200, blank=True, help_text="For institutional customers")
    contact_person_first_name = models.CharField(max_length=100, blank=True, help_text="Contact person's first name")
    contact_person_last_name = models.CharField(max_length=100, blank=True, help_text="Contact person's last name")
    
    # Additional institution fields
    business_registration_number = models.CharField(max_length=50, blank=True, null=True, help_text="Business registration number")
    vat_number = models.CharField(max_length=20, blank=True, null=True, help_text="VAT registration number")
    company_address = models.TextField(blank=True, help_text="Registered company address")
    billing_contact_name = models.CharField(max_length=200, blank=True, help_text="Billing contact person")
    billing_contact_email = models.EmailField(blank=True, help_text="Billing contact email")
    billing_contact_phone = models.CharField(max_length=15, blank=True, help_text="Billing contact phone")
    
    # Common fields
    kra_pin = models.CharField(max_length=20, blank=True, null=True)
    phone = models.CharField(max_length=15)
    email = models.EmailField()

    # Location
    gps_latitude = models.DecimalField(max_digits=10, decimal_places=8, null=True, blank=True)
    gps_longitude = models.DecimalField(max_digits=11, decimal_places=8, null=True, blank=True)
    physical_address = models.TextField()

    # Network Details - Auto-populated from MikroTik
    router_mac = models.CharField(max_length=17, blank=True, editable=False, help_text="Auto-populated from MikroTik on first connection")

    # PPPoE Credentials
    pppoe_username = models.CharField(max_length=50, blank=True, help_text="PPPoE username for MikroTik authentication")
    pppoe_password = models.CharField(max_length=20, blank=True, help_text="PPPoE password for MikroTik authentication")

    service = models.ForeignKey(Service, on_delete=models.SET_NULL, null=True)
    service_status = models.CharField(max_length=20, choices=SERVICE_STATUS_CHOICES, default='pending_activation')
    
    # Keep package as an alias for backward compatibility
    @property
    def package(self):
        return self.service
    
    # Service management dates
    installation_date = models.DateTimeField(null=True, blank=True, editable=False, help_text="Auto-populated on first connection")
    trial_start_date = models.DateTimeField(null=True, blank=True, help_text="Trial period start")
    trial_end_date = models.DateTimeField(null=True, blank=True, help_text="Trial period end")
    service_start_date = models.DateTimeField(null=True, blank=True, help_text="Actual service start after trial")
    last_suspension_date = models.DateTimeField(null=True, blank=True)
    manual_suspension_override = models.DateTimeField(null=True, blank=True, help_text="Manual override for suspension")
    
    notes = models.TextField(blank=True)

    # Tags for grouping and notifications
    tags = models.ManyToManyField(CustomerTag, blank=True, related_name='customers')

    # Account Balance
    account_balance = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal('0.00'), help_text="Customer's account balance for advance payments")
    
    # Status
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def generate_pppoe_password(self):
        """Generate a random 6-character password with alphanumeric and special characters"""
        characters = string.ascii_letters + string.digits + "!@#$%&*"
        return ''.join(secrets.choice(characters) for _ in range(6))

    def save(self, *args, **kwargs):
        if not self.customer_id:
            if self.is_sub_account and self.parent_customer:
                # Generate sub-account ID
                parent_id = self.parent_customer.customer_id
                if not self.sub_account_number:
                    # Get the next sub-account number
                    last_sub = Customer.objects.filter(
                        parent_customer=self.parent_customer,
                        is_sub_account=True
                    ).order_by('-sub_account_number').first()
                    
                    self.sub_account_number = (last_sub.sub_account_number + 1) if last_sub else 1
                
                self.customer_id = f"{parent_id}-{self.sub_account_number}"
            else:
                # Generate main customer ID with OPT prefix
                last_customer = Customer.objects.filter(
                    customer_id__startswith='OPT',
                    is_sub_account=False
                ).order_by('-customer_id').first()
                
                if last_customer:
                    last_number = int(last_customer.customer_id[3:])
                    new_number = last_number + 1
                else:
                    new_number = 1
                self.customer_id = f"OPT{new_number:06d}"

        # Auto-generate PPPoE credentials if not set
        if not self.pppoe_username and self.customer_id:
            self.pppoe_username = self.customer_id

        if not self.pppoe_password:
            self.pppoe_password = self.generate_pppoe_password()

        super().save(*args, **kwargs)
    
    def create_user_account(self, password=None):
        """Create a user account for this customer"""
        if self.user:
            return self.user
        
        if not password:
            password = self.generate_pppoe_password()
        
        # Determine first and last name based on customer type
        if self.customer_type == 'institution':
            first_name = self.contact_person_first_name
            last_name = self.contact_person_last_name
        else:
            first_name = self.full_name.split(' ')[0] if self.full_name else ''
            last_name = ' '.join(self.full_name.split(' ')[1:]) if self.full_name and len(self.full_name.split(' ')) > 1 else ''
        
        user = User.objects.create_user(
            username=self.customer_id,
            email=self.email,
            password=password,
            first_name=first_name,
            last_name=last_name,
            role='client'
        )
        
        # Link customer to user
        self.user = user
        self.save()
        
        return user

    @property
    def display_name(self):
        """Returns appropriate name based on customer type"""
        if self.customer_type == 'institution':
            return self.company_name or f"{self.contact_person_first_name} {self.contact_person_last_name}".strip()
        else:
            return self.full_name
    
    @property
    def contact_person_full_name(self):
        """Returns full name of contact person for institutions"""
        if self.customer_type == 'institution':
            return f"{self.contact_person_first_name} {self.contact_person_last_name}".strip()
        return ""

    def clean(self):
        """Validate customer data based on type"""
        from django.core.exceptions import ValidationError
        
        if self.customer_type == 'individual':
            if not self.full_name:
                raise ValidationError({'full_name': 'Full name is required for individual customers.'})
        elif self.customer_type == 'institution':
            if not self.company_name:
                raise ValidationError({'company_name': 'Company name is required for institutional customers.'})
            if not self.contact_person_first_name:
                raise ValidationError({'contact_person_first_name': 'Contact person first name is required for institutional customers.'})
            if not self.contact_person_last_name:
                raise ValidationError({'contact_person_last_name': 'Contact person last name is required for institutional customers.'})

    def __str__(self):
        return f"{self.customer_id} - {self.display_name}"

    @property
    def current_balance(self):
        """Returns account balance (positive = credit, negative = debt)"""
        return self.account_balance
    
    @property
    def outstanding_amount(self):
        """Returns total amount of unpaid invoices"""
        from billing.models import Invoice
        unpaid_invoices = Invoice.objects.filter(customer=self, status='unpaid')
        return sum(invoice.total_amount for invoice in unpaid_invoices)

    @property
    def is_overdue(self):
        from billing.models import Invoice
        from django.utils import timezone
        overdue_invoices = Invoice.objects.filter(
            customer=self,
            status='unpaid',
            due_date__lt=timezone.now().date()
        )
        return overdue_invoices.exists()
    
    @property
    def net_balance(self):
        """Returns net balance (outstanding - account balance). Positive = owes money, Negative = has credit"""
        return self.outstanding_amount - self.account_balance
    
    @property
    def current_service_status(self):
        """Returns current service status using ServicePeriodManager"""
        from billing.services import ServicePeriodManager
        return ServicePeriodManager.get_customer_current_service_status(self)
    
    @property
    def service_expires_at(self):
        """Returns when current service expires"""
        status = self.current_service_status
        if status['status'] == 'active':
            return status['expires_at']
        return None
    
    @property
    def can_auto_renew(self):
        """Returns True if customer has sufficient balance for auto-renewal"""
        return self.service and self.account_balance >= self.service.price
    
    @property
    def is_trial_active(self):
        """Check if customer is in trial period"""
        if not self.trial_start_date or not self.trial_end_date:
            return False
        now = timezone.now()
        return self.trial_start_date <= now <= self.trial_end_date
    
    @property
    def trial_remaining_hours(self):
        """Get remaining trial hours"""
        if not self.is_trial_active:
            return 0
        remaining = self.trial_end_date - timezone.now()
        return max(0, remaining.total_seconds() / 3600)
    
    def create_sub_account(self, **kwargs):
        """Create a sub-account under this customer"""
        if self.is_sub_account:
            raise ValueError("Sub-accounts cannot create their own sub-accounts")
        
        sub_account = Customer.objects.create(
            parent_customer=self,
            is_sub_account=True,
            customer_type=self.customer_type,
            email=kwargs.get('email', ''),
            phone=kwargs.get('phone', ''),
            physical_address=kwargs.get('physical_address', self.physical_address),
            **kwargs
        )
        return sub_account
    
    def get_all_sub_accounts(self):
        """Get all sub-accounts under this customer"""
        if self.is_sub_account:
            return Customer.objects.none()
        return self.sub_accounts.all()
    
    def get_main_account(self):
        """Get the main account (for sub-accounts)"""
        return self.parent_customer if self.is_sub_account else self

class CustomerInvitation(models.Model):
    customer = models.OneToOneField(Customer, on_delete=models.CASCADE, related_name='invitation')
    token = models.UUIDField(default=uuid.uuid4, unique=True)
    created_at = models.DateTimeField(auto_now_add=True)
    expires_at = models.DateTimeField()
    is_used = models.BooleanField(default=False)
    used_at = models.DateTimeField(null=True, blank=True)

    def save(self, *args, **kwargs):
        if not self.expires_at:
            self.expires_at = timezone.now() + timedelta(days=7)  # Token expires in 7 days
        super().save(*args, **kwargs)

    @property
    def is_expired(self):
        return timezone.now() > self.expires_at

    @property
    def is_valid(self):
        return not self.is_used and not self.is_expired

    def __str__(self):
        return f"Invitation for {self.customer.full_name} - {'Used' if self.is_used else 'Pending'}"

class CustomerBalanceTransaction(models.Model):
    TRANSACTION_TYPE_CHOICES = [
        ('credit', 'Credit (Payment/Top-up)'),
        ('debit', 'Debit (Bill Payment)'),
        ('refund', 'Refund'),
        ('adjustment', 'Manual Adjustment'),
    ]
    
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='balance_transactions')
    transaction_type = models.CharField(max_length=20, choices=TRANSACTION_TYPE_CHOICES)
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    description = models.CharField(max_length=200)
    reference_id = models.CharField(max_length=100, blank=True, help_text="Payment ID, Invoice ID, etc.")
    balance_before = models.DecimalField(max_digits=12, decimal_places=2)
    balance_after = models.DecimalField(max_digits=12, decimal_places=2)
    processed_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return f"{self.customer.customer_id} - {self.get_transaction_type_display()}: {self.amount}"
    
    class Meta:
        ordering = ['-created_at']



class MarketingWeeklyReport(models.Model):
    REPORT_STATUS_CHOICES = [
        ('draft', 'Draft'),
        ('submitted', 'Submitted'),
        ('reviewed', 'Reviewed'),
        ('approved', 'Approved'),
    ]

    # Report metadata
    marketing_staff = models.ForeignKey(User, on_delete=models.CASCADE, related_name='marketing_reports')
    week_starting = models.DateField(help_text="Monday of the reporting week")
    week_ending = models.DateField(help_text="Sunday of the reporting week")
    status = models.CharField(max_length=20, choices=REPORT_STATUS_CHOICES, default='draft')
    
    # Field Activity Metrics
    customers_contacted = models.IntegerField(default=0, help_text="Number of customers/prospects contacted")
    site_visits_conducted = models.IntegerField(default=0, help_text="Number of physical site visits")
    demos_conducted = models.IntegerField(default=0, help_text="Number of service demonstrations")
    
    # Lead Generation
    new_leads_generated = models.IntegerField(default=0)
    qualified_leads = models.IntegerField(default=0)
    leads_converted = models.IntegerField(default=0)
    
    # Customer Feedback Summary
    customer_feedback_summary = models.TextField(
        blank=True,
        help_text="Summary of customer feedback, concerns, and suggestions"
    )
    
    # Market Intelligence
    competitor_activities = models.TextField(
        blank=True,
        help_text="Observed competitor activities, pricing, or strategies"
    )
    
    market_trends_observed = models.TextField(
        blank=True,
        help_text="New market trends or customer behavior patterns"
    )
    
    # Recommendations
    service_improvement_recommendations = models.TextField(
        blank=True,
        help_text="Suggestions for improving services based on customer feedback"
    )
    
    pricing_recommendations = models.TextField(
        blank=True,
        help_text="Pricing strategy suggestions based on market conditions"
    )
    
    marketing_strategy_recommendations = models.TextField(
        blank=True,
        help_text="Marketing approach improvements or new strategies"
    )
    
    # Challenges and Solutions
    challenges_faced = models.TextField(
        blank=True,
        help_text="Main challenges encountered during the week"
    )
    
    solutions_implemented = models.TextField(
        blank=True,
        help_text="Solutions tried or implemented for challenges"
    )
    
    # Goals and Planning
    next_week_targets = models.TextField(
        blank=True,
        help_text="Planned activities and targets for next week"
    )
    
    support_needed = models.TextField(
        blank=True,
        help_text="Support or resources needed from management"
    )
    
    # Review and approval
    reviewed_by = models.ForeignKey(
        User, 
        on_delete=models.SET_NULL, 
        null=True, 
        blank=True, 
        related_name='reviewed_marketing_reports'
    )
    reviewed_at = models.DateTimeField(null=True, blank=True)
    review_comments = models.TextField(blank=True)
    
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    def clean(self):
        from django.core.exceptions import ValidationError
        if self.week_ending <= self.week_starting:
            raise ValidationError("Week ending date must be after week starting date")
        
        # Ensure it's a weekly period (7 days)
        if (self.week_ending - self.week_starting).days != 6:
            raise ValidationError("Report period must be exactly 7 days (Monday to Sunday)")
    
    def save(self, *args, **kwargs):
        # Auto-calculate week ending if not provided
        if self.week_starting and not self.week_ending:
            self.week_ending = self.week_starting + timedelta(days=6)
        super().save(*args, **kwargs)
    
    @property
    def conversion_rate(self):
        if self.new_leads_generated > 0:
            return round((self.leads_converted / self.new_leads_generated) * 100, 2)
        return 0
    
    @property
    def qualification_rate(self):
        if self.new_leads_generated > 0:
            return round((self.qualified_leads / self.new_leads_generated) * 100, 2)
        return 0
    
    def __str__(self):
        return f"Marketing Report - {self.marketing_staff.get_full_name()} ({self.week_starting} to {self.week_ending})"
    
    class Meta:
        unique_together = ['marketing_staff', 'week_starting']
        ordering = ['-week_starting', 'marketing_staff__first_name']


class MarketingLeadInteraction(models.Model):
    INTERACTION_TYPE_CHOICES = [
        ('phone_call', 'Phone Call'),
        ('site_visit', 'Site Visit'),
        ('email', 'Email'),
        ('demo', 'Service Demo'),
        ('follow_up', 'Follow Up'),
        ('complaint_resolution', 'Complaint Resolution'),
        ('other', 'Other'),
    ]
    
    OUTCOME_CHOICES = [
        ('positive', 'Positive'),
        ('neutral', 'Neutral'),
        ('negative', 'Negative'),
        ('converted', 'Converted to Customer'),
        ('lost', 'Lost Lead'),
    ]
    
    weekly_report = models.ForeignKey(
        MarketingWeeklyReport, 
        on_delete=models.CASCADE, 
        related_name='lead_interactions'
    )
    
    # Interaction details
    interaction_date = models.DateField()
    interaction_type = models.CharField(max_length=30, choices=INTERACTION_TYPE_CHOICES)
    
    # Lead/Customer information
    lead_name = models.CharField(max_length=200)
    lead_phone = models.CharField(max_length=15)
    lead_location = models.CharField(max_length=200)
    existing_customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, null=True, blank=True)
    
    # Interaction outcome
    outcome = models.CharField(max_length=20, choices=OUTCOME_CHOICES)
    notes = models.TextField(help_text="Detailed notes about the interaction")
    
    # Service interest
    service_interested = models.ForeignKey(Service, on_delete=models.SET_NULL, null=True, blank=True)
    estimated_value = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
    
    # Follow-up
    follow_up_required = models.BooleanField(default=False)
    follow_up_date = models.DateField(null=True, blank=True)
    follow_up_notes = models.TextField(blank=True)
    
    created_at = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return f"{self.lead_name} - {self.get_interaction_type_display()} ({self.interaction_date})"
    
    class Meta:
        ordering = ['-interaction_date']


class MarketingReportTemplate(models.Model):
    """Templates for common marketing report sections"""
    name = models.CharField(max_length=100)
    section = models.CharField(max_length=50, help_text="Which section of the report this template applies to")
    template_content = models.TextField()
    is_active = models.BooleanField(default=True)
    created_by = models.ForeignKey(User, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return f"{self.name} ({self.section})"
    
    class Meta:
        ordering = ['section', 'name']


class CustomerActionLog(models.Model):
    ACTION_TYPE_CHOICES = [
        ('created', 'Customer Created'),
        ('updated', 'Customer Updated'),
        ('suspended', 'Service Suspended'),
        ('disconnected', 'Service Disconnected'),
        ('reconnected', 'Service Reconnected'),
        ('trial_started', 'Trial Period Started'),
        ('trial_ended', 'Trial Period Ended'),
        ('service_activated', 'Service Activated'),
        ('payment_received', 'Payment Received'),
        ('balance_adjusted', 'Balance Adjusted'),
        ('service_changed', 'Service Package Changed'),
        ('notes_updated', 'Notes Updated'),
        ('credentials_reset', 'Credentials Reset'),
        ('manual_override', 'Manual Override Applied'),
        ('sub_account_created', 'Sub-Account Created'),
        ('linked_to_parent', 'Linked to Parent Account'),
        ('contact_updated', 'Contact Information Updated'),
        ('installation_completed', 'Installation Completed'),
    ]
    
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='action_logs')
    action_type = models.CharField(max_length=30, choices=ACTION_TYPE_CHOICES)
    description = models.TextField(help_text="Detailed description of the action")
    
    # Context data
    old_value = models.JSONField(null=True, blank=True, help_text="Previous value (for updates)")
    new_value = models.JSONField(null=True, blank=True, help_text="New value (for updates)")
    ip_address = models.GenericIPAddressField(null=True, blank=True)
    user_agent = models.TextField(blank=True)
    
    # Action metadata
    performed_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
    system_action = models.BooleanField(default=False, help_text="True if action was performed by system")
    reference_id = models.CharField(max_length=100, blank=True, help_text="Reference to related object (payment ID, ticket ID, etc.)")
    
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['customer', '-created_at']),
            models.Index(fields=['action_type', '-created_at']),
            models.Index(fields=['performed_by', '-created_at']),
        ]
    
    def __str__(self):
        performer = self.performed_by.get_full_name() if self.performed_by else "System"
        return f"{self.customer.customer_id} - {self.get_action_type_display()} by {performer}"
