"""Content Engine — write services.

All write operations go through these functions; views must NOT call ORM
save/delete directly on ContentEngine models.
"""

from __future__ import annotations

from django.db import transaction
from django.db.models import F
from django.utils import timezone

from simorgh.apps.content.models import (
    ContentCategory,
    ContentItem,
    ContentStatus,
    ContentVersion,
)
from simorgh.core.audit import record_service_event

# ── Categories ──────────────────────────────────────────────────────────────

def create_category(
    *,
    tenant_id: int,
    organization_node_id: int,
    name: str,
    slug: str,
    parent_id: int | None = None,
    description: str = "",
    icon: str = "",
    sort_order: int = 0,
) -> ContentCategory:
    from simorgh.apps.events.bus import dispatch

    category = ContentCategory.objects.create(
        tenant_id=tenant_id,
        organization_node_id=organization_node_id,
        name=name.strip(),
        slug=slug.strip(),
        parent_id=parent_id,
        description=description,
        icon=icon,
        sort_order=sort_order,
    )
    record_service_event("content.category_created", resource=category, after={
        "name": name,
        "slug": slug,
    })
    dispatch("content.category_created", {
        "category_id": str(category.public_id),
        "tenant_id": tenant_id,
        "name": name,
    })
    return category


def update_category(
    category: ContentCategory,
    *,
    name: str | None = None,
    slug: str | None = None,
    parent_id: int | None | object = ...,  # type: ignore[assignment]
    description: str | None = None,
    icon: str | None = None,
    sort_order: int | None = None,
    is_active: bool | None = None,
) -> ContentCategory:
    from simorgh.apps.events.bus import dispatch

    _SENTINEL = object()
    changed = False

    if name is not None:
        category.name = name.strip()
        changed = True
    if slug is not None:
        category.slug = slug.strip()
        changed = True
    if parent_id is not _SENTINEL:
        category.parent_id = parent_id  # type: ignore[assignment]
        changed = True
    if description is not None:
        category.description = description
        changed = True
    if icon is not None:
        category.icon = icon
        changed = True
    if sort_order is not None:
        category.sort_order = sort_order
        changed = True
    if is_active is not None:
        category.is_active = is_active
        changed = True

    if changed:
        category.save()
        record_service_event("content.category_updated", resource=category)
        dispatch("content.category_updated", {
            "category_id": str(category.public_id),
            "tenant_id": category.tenant_id,
        })
    return category


def delete_category(category: ContentCategory) -> None:
    from simorgh.apps.events.bus import dispatch

    public_id, tenant_id = str(category.public_id), category.tenant_id
    category.delete()
    record_service_event("content.category_deleted", resource=category)
    dispatch("content.category_deleted", {
        "category_id": public_id,
        "tenant_id": tenant_id,
    })


# ── Content Items ───────────────────────────────────────────────────────────

def create_content_item(
    *,
    tenant_id: int,
    organization_node_id: int,
    title: str,
    slug: str,
    content_type: str,
    author_id: int,
    category_id: int | None = None,
    body: str = "",
    excerpt: str = "",
    visibility: str = "internal",
    workspace_id: int | None = None,
    meta_title: str = "",
    meta_description: str = "",
    meta_keywords: str = "",
) -> ContentItem:
    from simorgh.apps.events.bus import dispatch

    item = ContentItem.objects.create(
        tenant_id=tenant_id,
        organization_node_id=organization_node_id,
        title=title.strip(),
        slug=slug.strip(),
        content_type=content_type,
        author_id=author_id,
        category_id=category_id,
        body=body,
        excerpt=excerpt,
        status=ContentStatus.DRAFT,
        visibility=visibility,
        workspace_id=workspace_id,
        meta_title=meta_title,
        meta_description=meta_description,
        meta_keywords=meta_keywords,
    )
    record_service_event("content.item_created", resource=item, after={
        "title": title,
        "content_type": content_type,
        "author_id": author_id,
    })
    dispatch("content.item_created", {
        "item_id": str(item.public_id),
        "tenant_id": tenant_id,
        "content_type": content_type,
        "author_id": author_id,
    })
    return item


def update_content_item(
    item: ContentItem,
    *,
    title: str | None = None,
    slug: str | None = None,
    content_type: str | None = None,
    category_id: int | None | object = ...,  # type: ignore[assignment]
    body: str | None = None,
    excerpt: str | None = None,
    visibility: str | None = None,
    meta_title: str | None = None,
    meta_description: str | None = None,
    meta_keywords: str | None = None,
    ai_description: str | None = None,
    ai_semantic_type: str | None = None,
    ai_summary: str | None = None,
    related_content: list | None = None,
) -> ContentItem:
    from simorgh.apps.events.bus import dispatch

    _SENTINEL = object()
    changed = False

    if title is not None:
        item.title = title.strip()
        changed = True
    if slug is not None:
        item.slug = slug.strip()
        changed = True
    if content_type is not None:
        item.content_type = content_type
        changed = True
    if category_id is not _SENTINEL:
        item.category_id = category_id  # type: ignore[assignment]
        changed = True
    if body is not None:
        item.body = body
        changed = True
    if excerpt is not None:
        item.excerpt = excerpt
        changed = True
    if visibility is not None:
        item.visibility = visibility
        changed = True
    if meta_title is not None:
        item.meta_title = meta_title
        changed = True
    if meta_description is not None:
        item.meta_description = meta_description
        changed = True
    if meta_keywords is not None:
        item.meta_keywords = meta_keywords
        changed = True
    if ai_description is not None:
        item.ai_description = ai_description
        changed = True
    if ai_semantic_type is not None:
        item.ai_semantic_type = ai_semantic_type
        changed = True
    if ai_summary is not None:
        item.ai_summary = ai_summary
        changed = True
    if related_content is not None:
        item.related_content = related_content
        changed = True

    if changed:
        item.save()
        record_service_event("content.item_updated", resource=item)
        dispatch("content.item_updated", {
            "item_id": str(item.public_id),
            "tenant_id": item.tenant_id,
        })
    return item


@transaction.atomic
def submit_for_review(item: ContentItem, *, actor_id: int) -> ContentItem:
    """Submit content for review. Starts workflow and creates approval request."""
    from simorgh.apps.events.bus import dispatch

    if item.status != ContentStatus.DRAFT:
        raise ValueError("Only draft content can be submitted for review.")

    item.status = ContentStatus.REVIEW
    item.save(update_fields=["status", "updated_at"])

    # Start workflow if not already started
    _ensure_workflow_started(item, actor_id)

    # Create approval request
    _create_approval_for_content(item, actor_id)

    record_service_event("content.item_submitted_for_review", resource=item, after={
        "actor_id": actor_id,
    })
    dispatch("content.item_submitted_for_review", {
        "item_id": str(item.public_id),
        "tenant_id": item.tenant_id,
        "actor_id": actor_id,
    })
    return item


@transaction.atomic
def approve_content(item: ContentItem, *, actor_id: int, note: str = "") -> ContentItem:
    """Approve content after review."""
    from simorgh.apps.events.bus import dispatch

    if item.status != ContentStatus.REVIEW:
        raise ValueError("Only content in review can be approved.")

    item.status = ContentStatus.APPROVED
    item.save(update_fields=["status", "updated_at"])

    record_service_event("content.item_approved", resource=item, after={
        "actor_id": actor_id,
    })
    dispatch("content.item_approved", {
        "item_id": str(item.public_id),
        "tenant_id": item.tenant_id,
        "actor_id": actor_id,
    })
    return item


@transaction.atomic
def publish_content(item: ContentItem, *, actor_id: int, change_summary: str = "") -> ContentItem:
    """Publish content — creates a version snapshot."""
    from simorgh.apps.events.bus import dispatch

    if item.status not in (ContentStatus.APPROVED, ContentStatus.DRAFT):
        raise ValueError("Only approved or draft content can be published directly.")

    item.status = ContentStatus.PUBLISHED
    item.published_at = timezone.now()
    item.save(update_fields=["status", "published_at", "updated_at"])

    # Create immutable version snapshot
    next_version = item.versions.count() + 1
    ContentVersion.objects.create(
        tenant_id=item.tenant_id,
        organization_node_id=item.organization_node_id,
        content_item=item,
        version=next_version,
        title=item.title,
        body=item.body,
        excerpt=item.excerpt,
        status=item.status,
        changed_by_id=actor_id,
        change_summary=change_summary,
    )

    record_service_event("content.item_published", resource=item, after={
        "actor_id": actor_id,
        "version": next_version,
    })
    dispatch("content.item_published", {
        "item_id": str(item.public_id),
        "tenant_id": item.tenant_id,
        "actor_id": actor_id,
        "version": next_version,
    })
    dispatch("content.version_created", {
        "item_id": str(item.public_id),
        "tenant_id": item.tenant_id,
        "version": next_version,
    })
    return item


@transaction.atomic
def archive_content(item: ContentItem, *, actor_id: int) -> ContentItem:
    """Archive published content."""
    from simorgh.apps.events.bus import dispatch

    if item.status != ContentStatus.PUBLISHED:
        raise ValueError("Only published content can be archived.")

    item.status = ContentStatus.ARCHIVED
    item.archived_at = timezone.now()
    item.save(update_fields=["status", "archived_at", "updated_at"])

    record_service_event("content.item_archived", resource=item, after={
        "actor_id": actor_id,
    })
    dispatch("content.item_archived", {
        "item_id": str(item.public_id),
        "tenant_id": item.tenant_id,
        "actor_id": actor_id,
    })
    return item


@transaction.atomic
def revert_to_draft(item: ContentItem, *, actor_id: int) -> ContentItem:
    """Revert published or archived content back to draft."""
    from simorgh.apps.events.bus import dispatch

    if item.status not in (ContentStatus.PUBLISHED, ContentStatus.ARCHIVED, ContentStatus.REVIEW, ContentStatus.APPROVED):
        raise ValueError("Only published, archived, review, or approved content can be reverted to draft.")

    item.status = ContentStatus.DRAFT
    item.published_at = None
    item.archived_at = None
    item.save(update_fields=["status", "published_at", "archived_at", "updated_at"])

    record_service_event("content.item_reverted_to_draft", resource=item, after={
        "actor_id": actor_id,
    })
    dispatch("content.item_reverted_to_draft", {
        "item_id": str(item.public_id),
        "tenant_id": item.tenant_id,
        "actor_id": actor_id,
    })
    return item


def delete_content_item(item: ContentItem) -> None:
    """Soft-delete a content item."""
    item.delete()


def record_feedback(item: ContentItem, *, user_id: int | None = None, is_helpful: bool, comment: str = "") -> None:
    """Record helpful/not-helpful feedback and update denormalized counters."""
    if is_helpful:
        ContentItem.objects.filter(pk=item.pk).update(helpful_count=F("helpful_count") + 1)
        item.refresh_from_db(fields=["helpful_count"])
    else:
        ContentItem.objects.filter(pk=item.pk).update(not_helpful_count=F("not_helpful_count") + 1)
        item.refresh_from_db(fields=["not_helpful_count"])


def increment_view_count(item: ContentItem) -> None:
    ContentItem.objects.filter(pk=item.pk).update(view_count=F("view_count") + 1)
    item.refresh_from_db(fields=["view_count"])


# ── Helpers ─────────────────────────────────────────────────────────────────

def _ensure_workflow_started(item: ContentItem, actor_id: int) -> None:
    """Start a content_publishing workflow instance if not already running."""
    if item.workflow_instance_id:
        return

    try:
        from simorgh.apps.workflow.engine import start_instance
        instance = start_instance(
            definition_name="content_publishing",
            subject=item,
            actor_id=actor_id,
        )
        item.workflow_instance = instance
        item.save(update_fields=["workflow_instance"])
    except Exception:
        import structlog
        _log = structlog.get_logger("simorgh.content")
        _log.warning("content.workflow_start_failed", item_id=str(item.public_id), exc_info=True)


def _create_approval_for_content(item: ContentItem, actor_id: int) -> None:
    """Create an ApprovalRequest for this content item."""
    try:
        from django.contrib.contenttypes.models import ContentType

        from simorgh.apps.approval_engine.services import create_approval_request
        create_approval_request(
            tenant_id=item.tenant_id,
            organization_node_id=item.organization_node_id,
            title=f"Review: {item.title}",
            description=f"Content item '{item.title}' requires review before publishing.",
            requested_by_id=actor_id,
            approval_type="single",
            priority="medium",
            content_type_id=ContentType.objects.get_for_model(ContentItem).pk,
            object_id=item.pk,
        )
    except Exception:
        import structlog
        _log = structlog.get_logger("simorgh.content")
        _log.warning("content.approval_create_failed", item_id=str(item.public_id), exc_info=True)
