from __future__ import annotations

from django.db import models
from django.contrib.auth import get_user_model
from django.utils.text import slugify
from textwrap import dedent
from django.utils import timezone
from taggit.managers import TaggableManager
from mptt.models import MPTTModel, TreeForeignKey
from markdownfield.models import MarkdownField, RenderedMarkdownField
from markdownfield.validators import VALIDATOR_STANDARD
from orgs.models import Organization

from typing import Optional
User = get_user_model()


class PostCategory(MPTTModel):
    organization = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name="cms_categories", null=True, blank=True)
    name = models.CharField(max_length=120)
    slug = models.SlugField(max_length=140, unique=False, blank=True)
    description = models.TextField(blank=True)
    parent = TreeForeignKey(
        "self", on_delete=models.CASCADE, null=True, blank=True, related_name="children"
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class MPTTMeta:
        order_insertion_by = ["name"]

    class Meta:
        ordering = ("tree_id", "lft", "name")
        indexes = [
            models.Index(fields=["slug"]),
        ]
        unique_together = (("organization", "slug"),)

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.name)[:140]
        super().save(*args, **kwargs)

    def __str__(self) -> str:  # pragma: no cover - trivial
        return self.name


class Post(models.Model):
    STATUS_DRAFT = "draft"
    STATUS_PUBLISHED = "published"
    STATUS_CHOICES = (
        (STATUS_DRAFT, "Draft"),
        (STATUS_PUBLISHED, "Published"),
    )

    organization = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name="cms_posts", null=True, blank=True)
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=220, unique=False, blank=True)
    category = TreeForeignKey(PostCategory, on_delete=models.PROTECT, related_name="posts")
    author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name="posts")
    excerpt = models.TextField(blank=True)
    feature_image = models.ImageField(upload_to="posts/feature/%Y/%m/%d/", null=True, blank=True)
    # Markdown content source + auto-rendered HTML
    content = MarkdownField(rendered_field="content_html", validator=VALIDATOR_STANDARD)
    content_html = RenderedMarkdownField(blank=True, null=True)
    status = models.CharField(max_length=12, choices=STATUS_CHOICES, default=STATUS_DRAFT)
    published_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    # Optional tags, consistent with project usage
    tags = TaggableManager(blank=True)

    class Meta:
        ordering = ("-published_at", "-created_at", "-id")
        indexes = [
            models.Index(fields=["status", "published_at"]),
            models.Index(fields=["slug"]),
        ]
        unique_together = (("organization", "slug"),)

    def save(self, *args, **kwargs):
        if not self.slug and self.title:
            base = slugify(self.title)[:220].strip('-')
            slug = base or "post"
            # Ensure uniqueness within organization by appending -2, -3, ... if needed
            i = 2
            qs = Post.objects
            if self.organization_id:
                qs = qs.filter(organization_id=self.organization_id)
            while qs.filter(slug=slug).exclude(pk=self.pk).exists():
                suffix = f"-{i}"
                slug = (base[: 220 - len(suffix)] + suffix).strip('-') or f"post-{i}"
                i += 1
            self.slug = slug
        if self.status == self.STATUS_PUBLISHED and not self.published_at:
            self.published_at = timezone.now()
        super().save(*args, **kwargs)

    def __str__(self) -> str:  # pragma: no cover - trivial
        return self.title


# --- Demo data helpers -------------------------------------------------------

def create_demo_data(org: Optional[Organization] = None) -> dict:
    """Create demo categories and posts for CMS.

    Idempotent: safe to call multiple times. Returns a summary counts dict.
    """
    created = {"categories": 0, "posts": 0}

    # Categories
    cat_specs = [
        {"name": "News", "description": "Company news and announcements"},
        {"name": "Guides", "description": "How-to guides and tutorials"},
        {"name": "Updates", "description": "Product updates and release notes"},
    ]
    cats: dict[str, PostCategory] = {}
    for spec in cat_specs:
        slug = slugify(spec["name"])[:140]
        lookup = {"slug": slug}
        if org is not None:
            lookup["organization"] = org
        obj, was_created = PostCategory.objects.get_or_create(
            **lookup,
            defaults={
                "name": spec["name"],
                "description": spec.get("description", ""),
                "organization": org,
            },
        )
        if was_created:
            created["categories"] += 1
        cats[obj.name] = obj

    # Choose an author if available
    try:
        author = User.objects.filter(is_staff=True).order_by("id").first() or User.objects.order_by("id").first()
    except Exception:
        author = None

    # Posts
    posts_specs = [
        {
            "title": "Welcome to Ecoloop",
            "category": cats.get("News"),
            "excerpt": "Introducing our recycling operations platform.",
            "content": "# Welcome to Ecoloop\n\nEcoloop helps factories and recyclers manage pickups, pricing, and a scrap marketplace.\nThis is a demo post seeded for your environment.",
            "status": Post.STATUS_PUBLISHED,
            "tags": ["ecoloop", "news"],
        },
        {
            "title": "Getting Started Guide",
            "category": cats.get("Guides"),
            "excerpt": "How to create materials, customers, and pickups.",
            "content": "# Getting Started\n\n1. Create your organization materials and price list.\n2. Add customers and sites.\n3. Create a pickup and record a weigh ticket.\n4. Generate invoices and payouts.",
            "status": Post.STATUS_PUBLISHED,
            "tags": ["guide", "howto"],
        },
        {
            "title": "September Release Notes",
            "category": cats.get("Updates"),
            "excerpt": "Highlights from the latest release.",
            "content": "# Release Notes\n\n- New scrap listings and bids\n- Auto award expired listings\n- Admin UI for pickups and invoices",
            "status": Post.STATUS_DRAFT,
            "tags": ["updates", "releases"],
        },
    ]

    for spec in posts_specs:
        base_slug = slugify(spec["title"])[:220]
        lookup = {"slug": base_slug}
        if org is not None:
            lookup["organization"] = org
        # Normalize multiline markdown content to avoid code-block wrapping
        raw_content = spec.get("content", "")
        content_md = dedent(raw_content).strip()
        excerpt_text = spec.get("excerpt", "")
        if isinstance(excerpt_text, str):
            excerpt_text = dedent(excerpt_text).strip()
        post, was_created = Post.objects.get_or_create(
            **lookup,
            defaults={
                "title": spec["title"],
                "category": spec.get("category") or next(iter(cats.values())),
                "author": author,
                "excerpt": excerpt_text,
                "content": content_md,
                "status": spec.get("status", Post.STATUS_DRAFT),
                "organization": org,
            },
        )
        if was_created:
            created["posts"] += 1
            try:
                tags = spec.get("tags") or []
                if tags:
                    post.tags.set(tags)
            except Exception:
                pass

    return created
