import secrets
import time

from django.conf import settings
from django.db import models
from django.utils import timezone


def uuid7() -> str:
    """Time-ordered UUID-ish id (hex, 32 chars)."""
    nanoseconds = time.time_ns()
    return f"{((nanoseconds << 16) | secrets.randbits(48)):032x}"


PLATFORM_CHOICES = [
    ("twitter", "Twitter / X"),
    ("instagram", "Instagram"),
    ("facebook", "Facebook"),
    ("linkedin", "LinkedIn"),
    ("tiktok", "TikTok"),
    ("youtube", "YouTube"),
    ("threads", "Threads"),
    ("other", "Other"),
]


class Channel(models.Model):
    """Connected social media account / posting destination."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    name = models.CharField(max_length=120)
    platform = models.CharField(max_length=20, choices=PLATFORM_CHOICES)
    handle = models.CharField(max_length=120, blank=True, help_text="@handle or page slug")
    profile_url = models.URLField(blank=True)
    avatar_url = models.URLField(blank=True)
    active = models.BooleanField(default=True)
    notes = models.TextField(blank=True)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, related_name="dissemination_channels")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "dissemination_channels"
        ordering = ["platform", "name"]
        indexes = [models.Index(fields=["platform", "active"])]

    def __str__(self):
        return f"{self.get_platform_display()} · {self.name}"


class Campaign(models.Model):
    """Named campaign grouping multiple posts."""
    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    name = models.CharField(max_length=180)
    objective = models.CharField(max_length=255, blank=True)
    description = models.TextField(blank=True)
    color = models.CharField(max_length=7, default="#6b7280")
    start_date = models.DateField(null=True, blank=True)
    end_date = models.DateField(null=True, blank=True)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, related_name="dissemination_campaigns")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "dissemination_campaigns"
        ordering = ["-start_date", "name"]

    def __str__(self):
        return self.name


class MediaAsset(models.Model):
    """Reusable image/video/file asset."""
    KIND_CHOICES = [("image", "Image"), ("video", "Video"), ("document", "Document"), ("other", "Other")]

    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    title = models.CharField(max_length=180, blank=True)
    kind = models.CharField(max_length=20, choices=KIND_CHOICES, default="image")
    file = models.FileField(upload_to="disseminations/media/")
    width = models.PositiveIntegerField(null=True, blank=True)
    height = models.PositiveIntegerField(null=True, blank=True)
    size_bytes = models.PositiveBigIntegerField(default=0)
    tags = models.JSONField(default=list, blank=True)
    uploaded_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, related_name="dissemination_media")
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "dissemination_media_assets"
        ordering = ["-created_at"]

    def __str__(self):
        return self.title or f"{self.kind} {self.id}"


class Post(models.Model):
    """A piece of content scheduled or published across one or more channels."""
    STATUS_CHOICES = [
        ("draft", "Draft"),
        ("scheduled", "Scheduled"),
        ("publishing", "Publishing"),
        ("published", "Published"),
        ("failed", "Failed"),
        ("archived", "Archived"),
    ]

    id = models.UUIDField(primary_key=True, default=uuid7, editable=False)
    title = models.CharField(max_length=200, blank=True)
    body = models.TextField(blank=True)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="draft", db_index=True)

    channels = models.ManyToManyField(Channel, related_name="posts", blank=True)
    campaign = models.ForeignKey(Campaign, on_delete=models.SET_NULL, null=True, blank=True, related_name="posts")
    media = models.ManyToManyField(MediaAsset, related_name="posts", blank=True)

    scheduled_at = models.DateTimeField(null=True, blank=True, db_index=True)
    published_at = models.DateTimeField(null=True, blank=True, db_index=True)
    timezone_name = models.CharField(max_length=64, default="UTC")

    tags = models.JSONField(default=list, blank=True)
    link_url = models.URLField(blank=True)
    tracking_url = models.ForeignKey(
        "tools.URL", on_delete=models.SET_NULL, null=True, blank=True,
        related_name="dissemination_posts",
        help_text="Short link generated for link_url; used for click tracking.",
    )
    metrics = models.JSONField(default=dict, blank=True, help_text="reach/likes/comments/shares per channel")
    error_message = models.TextField(blank=True)

    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, related_name="dissemination_posts")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "dissemination_posts"
        ordering = ["-scheduled_at", "-created_at"]
        indexes = [
            models.Index(fields=["status", "scheduled_at"]),
            models.Index(fields=["campaign", "status"]),
        ]

    def __str__(self):
        return self.title or (self.body[:50] if self.body else f"Post {self.id}")

    def mark_published(self):
        self.status = "published"
        self.published_at = timezone.now()
        self.error_message = ""
        self.save(update_fields=["status", "published_at", "error_message", "updated_at"])

    def mark_failed(self, message: str):
        self.status = "failed"
        self.error_message = message[:2000]
        self.save(update_fields=["status", "error_message", "updated_at"])
