from django.db import models
from django.contrib.auth.models import User, Group, Permission
from django.core.exceptions import ValidationError
from django.utils.text import slugify
from django.conf import settings
from django.core.validators import validate_image_file_extension, validate_ipv46_address
import json
import uuid
import base64
import re
from cryptography.fernet import Fernet

# Gunakan key dari settings (harus disimpan aman!)
cipher_suite = Fernet(settings.ENCRYPTION_KEY)

# ------------------------------
# TeamMember
# ------------------------------
class TeamMember(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    profile_photo = models.TextField(blank=True, null=True)
    linkedin_url = models.URLField(max_length=500, blank=True, null=True)
    role = models.CharField(max_length=255)

    def clean(self):
        if self.profile_photo:
            try:
                base64.b64decode(self.profile_photo)
            except (ValueError, TypeError):
                raise ValidationError("Profile photo must be a valid base64 string.")

    def save(self, *args, **kwargs):
        if self.profile_photo and not self.profile_photo.startswith('gAAAA'):  # Skip if already encrypted
            self.profile_photo = cipher_suite.encrypt(self.profile_photo.encode()).decode()
        super().save(*args, **kwargs)

    def get_profile_photo(self):
        try:
            return cipher_suite.decrypt(self.profile_photo.encode()).decode()
        except Exception:
            return None

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['name']
        verbose_name = "Team Member"
        verbose_name_plural = "Team Members"


# ------------------------------
# Year & Category
# ------------------------------
class Year(models.Model):
    year = models.PositiveIntegerField(unique=True)

    def __str__(self):
        return str(self.year)


class Category(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField(blank=True, null=True)

    def __str__(self):
        return self.name


# ------------------------------
# MyModel (Regulasi)
# ------------------------------
class MyModel(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
    data = models.JSONField()
    slug = models.SlugField(unique=True, blank=True)
    category = models.ForeignKey(Category, on_delete=models.CASCADE, blank=True, null=True)
    year = models.ForeignKey(Year, on_delete=models.CASCADE, blank=True, null=True, db_index=True)
    is_verified = models.BooleanField(default=False)

    def clean(self):
        if self.category and self.category.name == "Konstitusi":
            return

        if not self.data or 'Nomor' not in self.data or 'Tahun' not in self.data or 'Tentang' not in self.data:
            raise ValidationError("JSON harus mengandung 'Nomor', 'Tahun', dan 'Tentang'.")

        try:
            tahun = int(self.data['Tahun'])
            self.year, _ = Year.objects.get_or_create(year=tahun)
        except (ValueError, KeyError):
            raise ValidationError("Format tahun tidak valid dalam JSON.")

    def save(self, *args, **kwargs):
        if not self.slug and 'Nomor' in self.data and 'Tahun' in self.data:
            self.slug = slugify(f"{self.data['Nomor']}-{self.data['Tahun']}")
        super().save(*args, **kwargs)

    def __str__(self):
        nomor = self.data.get('Nomor', 'Unknown')
        tahun = self.year.year if self.year else 'Unknown'
        tentang = self.data.get('Tentang', 'Unknown')
        return f"UU No {nomor} Tahun {tahun} Tentang {tentang}"


# ------------------------------
# Term
# ------------------------------
class Term(models.Model):
    name = models.CharField(max_length=255)
    definition = models.TextField()
    my_models = models.ManyToManyField(MyModel, related_name='terms')
    slug = models.SlugField(max_length=255, unique=True, blank=True, null=True)

    def clean(self):
        if not self.slug:
            self.slug = slugify(self.name)
        if not re.match(r'^[a-z0-9-]+$', self.slug):
            raise ValidationError("Slug hanya boleh berisi huruf kecil, angka, dan strip.")

    def __str__(self):
        return self.name


# ------------------------------
# RecentlyUpdated
# ------------------------------
class RecentlyUpdated(models.Model):
    my_model = models.ForeignKey(MyModel, on_delete=models.CASCADE, related_name='recent_updates')
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-updated_at']

    def __str__(self):
        nomor = self.my_model.data.get('Nomor', 'Unknown')
        tahun = self.my_model.year.year if self.my_model.year else 'Unknown'
        tentang = self.my_model.data.get('Tentang', 'Unknown')
        return f"Updated: UU No {nomor} Tahun {tahun} Tentang {tentang}"


# ------------------------------
# MyModelVisits & VisitorIP
# ------------------------------
class MyModelVisits(models.Model):
    uu = models.ForeignKey(MyModel, on_delete=models.CASCADE)
    user_ip = models.CharField(max_length=15, validators=[validate_ipv46_address])
    visited_at = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True)
    device_type = models.CharField(max_length=255, null=True)
    platform_type = models.CharField(max_length=255, null=True)
    browser_type = models.CharField(max_length=255, null=True)


class VisitorIP(models.Model):
    ip_address = models.GenericIPAddressField(validators=[validate_ipv46_address])
    accessed_url = models.CharField(max_length=255)
    timestamp = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.ip_address} accessed {self.accessed_url}"


# ------------------------------
# Popularity
# ------------------------------
class Popularity(models.Model):
    uu = models.OneToOneField(MyModel, on_delete=models.CASCADE)
    view_count = models.PositiveIntegerField(default=0)

    def __str__(self):
        return f"{self.uu} - {self.view_count} views"


# ------------------------------
# SearchQuery & SearchTermQuery
# ------------------------------
class SearchQuery(models.Model):
    query = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.query


class SearchTermQuery(models.Model):
    query = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)
    status = models.CharField(max_length=50, choices=[('found', 'Found'), ('not_found', 'Not Found')], default='found')

    def __str__(self):
        return self.query


# ------------------------------
# Bookmark
# ------------------------------
class Bookmark(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    regulation = models.ForeignKey(MyModel, on_delete=models.CASCADE)
    last_read_position = models.FloatField(default=0)
    url = models.URLField()

    class Meta:
        unique_together = ('user', 'regulation')

    def __str__(self):
        return f"{self.user.username} - {self.regulation}"


# ------------------------------
# UserProfile
# ------------------------------
def load_provinces():
    with open(settings.BASE_DIR / 'provinces.json') as f:
        data = json.load(f)
        if not isinstance(data, list):
            raise ValidationError("Invalid provinces JSON format.")
        return data

def load_jobs():
    with open(settings.BASE_DIR / 'jobs.json') as f:
        data = json.load(f)
        if not isinstance(data, list):
            raise ValidationError("Invalid jobs JSON format.")
        return data

PROVINSI_CHOICES = [(prov, prov) for prov in load_provinces()]
JOB_CHOICES = [(job, job) for job in load_jobs()]

class UserProfile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    first_name = models.CharField(max_length=255, blank=True, null=True)
    last_name = models.CharField(max_length=255, blank=True, null=True)
    job = models.CharField(max_length=255, choices=JOB_CHOICES, blank=True, null=True)
    province = models.CharField(max_length=255, choices=PROVINSI_CHOICES, blank=True, null=True)
    birth_date = models.DateField(blank=True, null=True)
    profile_picture = models.TextField(blank=True, null=True)
    groups = models.ManyToManyField(Group, related_name='userprofile_groups', blank=True)
    user_permissions = models.ManyToManyField(Permission, related_name='userprofile_permissions', blank=True)

    def clean(self):
        if self.profile_picture:
            try:
                base64.b64decode(self.profile_picture)
            except Exception:
                raise ValidationError("Invalid base64 profile picture")

    def __str__(self):
        return f"{self.first_name} {self.last_name}"


# ------------------------------
# Subscriber
# ------------------------------
class Subscriber(models.Model):
    email = models.EmailField(unique=True)
    subscribed_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.email


# ------------------------------
# ContactMessage
# ------------------------------
class ContactMessage(models.Model):
    STATUS_CHOICES = [
        ('unread', 'Unread'),
        ('read', 'Read'),
        ('archived', 'Archived'),
    ]

    name = models.CharField(max_length=100)
    email = models.EmailField()
    message = models.TextField()
    submitted_at = models.DateTimeField(auto_now_add=True, db_index=True)
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='unread')
    updated_at = models.DateTimeField(auto_now=True)

    def clean(self):
        if len(self.message.strip()) < 10:
            raise ValidationError("Message must be at least 10 characters.")

    def mark_as_read(self):
        self.status = 'read'
        self.save()

    def __str__(self):
        return f"From {self.name}"


# ------------------------------
# SuspiciousLog
# ------------------------------
class SuspiciousLog(models.Model):
    ip = models.GenericIPAddressField(unique=True)
    count = models.IntegerField(default=0)
    timestamp = models.DateTimeField(auto_now_add=True)
    blocked = models.BooleanField(default=False)

    def __str__(self):
        status = "Blocked" if self.blocked else "Safe"
        return f"{self.ip} - {self.count} times - {status}"


# ------------------------------
# Report
# ------------------------------
class Report(models.Model):
    url = models.URLField(max_length=500)
    description = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True)
    ip_address = models.GenericIPAddressField(null=True, blank=True)

    @classmethod
    def is_report_limit_reached(cls, url, description):
        return cls.objects.filter(url=url).count() >= 3 or cls.objects.filter(description=description).count() >= 3

    def __str__(self):
        return f"Report by {self.user or 'Anon'} on {self.created_at}"


# ------------------------------
# EmbeddingChunk
# ------------------------------
class EmbeddingChunk(models.Model):
    regulation = models.ForeignKey(MyModel, on_delete=models.CASCADE, related_name='chunks')
    chunk_text = models.TextField()
    embedding = models.JSONField()
    chunk_index = models.PositiveIntegerField()

    class Meta:
        unique_together = ('regulation', 'chunk_index')
        ordering = ['regulation', 'chunk_index']

    def __str__(self):
        return f"Chunk {self.chunk_index} of {self.regulation}"

class ArticleSuggestion(models.Model):
    STATUS_CHOICES = [
        ('pending',  'Menunggu'),
        ('approved', 'Disetujui'),
        ('rejected', 'Ditolak'),
    ]
    regulation      = models.ForeignKey('MyModel', on_delete=models.CASCADE, related_name='suggestions')
    user            = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL,
                                        null=True, blank=True)
    article_key     = models.CharField(max_length=255)
    article_path    = models.CharField(max_length=500, blank=True)
    original_text   = models.TextField()
    suggested_text  = models.TextField()
    reason          = models.TextField(blank=True)
    status          = models.CharField(max_length=10, choices=STATUS_CHOICES, default='pending')
    created_at      = models.DateTimeField(auto_now_add=True)
    reviewed_at     = models.DateTimeField(null=True, blank=True)
    reviewed_by     = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL,
                                        null=True, blank=True, related_name='reviewed_suggestions')
 
    class Meta:
        ordering = ['-created_at']
 
    def __str__(self):
        return f"Saran: {self.article_key} — {self.regulation}"