"""Assignment Engine services — business logic.

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

from __future__ import annotations

from datetime import datetime

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


def create_assignment_tag(*, tenant_id: int, name: str, color: str = "") -> AssignmentTag:
    from simorgh.apps.assignments.models import AssignmentTag
    return AssignmentTag.objects.create(tenant_id=tenant_id, name=name.strip(), color=color)


def update_assignment_tag(tag: AssignmentTag, *, name: str | None = None, color: str | None = None) -> AssignmentTag:
    if name is not None:
        tag.name = name.strip()
    if color is not None:
        tag.color = color
    tag.save()
    return tag


def delete_assignment_tag(tag: AssignmentTag) -> None:
    tag.delete()


def create_assignment(
    *,
    tenant_id: int,
    organization_node_id: int,
    title: str,
    assigned_by_id: int,
    description: str = "",
    assignment_type: str = "for_action",
    priority: str = "medium",
    assigned_to_id: int | None = None,
    assigned_role_id: int | None = None,
    assigned_org_unit_id: int | None = None,
    due_date: datetime | None = None,
    is_confidential: bool = False,
    content_type_id: int | None = None,
    object_id: int | None = None,
    tag_ids: list[int] | None = None,
) -> Assignment:
    from simorgh.apps.assignments.models import Assignment, AssignmentTag
    from simorgh.apps.events.bus import dispatch
    from simorgh.core.audit import record_service_event

    with transaction.atomic():
        assignment = Assignment.objects.create(
            tenant_id=tenant_id,
            organization_node_id=organization_node_id,
            title=title.strip(),
            description=description,
            assignment_type=assignment_type,
            status="pending",
            priority=priority,
            assigned_by_id=assigned_by_id,
            assigned_to_id=assigned_to_id,
            assigned_role_id=assigned_role_id,
            assigned_org_unit_id=assigned_org_unit_id,
            due_date=due_date,
            is_confidential=is_confidential,
            content_type_id=content_type_id,
            object_id=object_id,
        )
        if tag_ids:
            tags = AssignmentTag.objects.filter(tenant_id=tenant_id, pk__in=tag_ids)
            assignment.tags.set(tags)

    record_service_event("assignments.assignment.created", resource=assignment, after={
        "assigned_to_id": assigned_to_id,
        "assigned_by_id": assigned_by_id,
    })
    dispatch("assignment.created", {
        "assignment_id":   str(assignment.public_id),
        "assigned_to_id":  assigned_to_id,
        "assigned_by_id":  assigned_by_id,
        "tenant_id":       tenant_id,
    })
    return assignment


def update_assignment(
    assignment: Assignment,
    *,
    title: str | None = None,
    description: str | None = None,
    priority: str | None = None,
    due_date: datetime | None = ...,  # type: ignore[assignment]
    is_confidential: bool | None = None,
    tag_ids: list[int] | None = None,
) -> Assignment:
    _SENTINEL = ...  # type: ignore[assignment]

    if title is not None:
        assignment.title = title.strip()
    if description is not None:
        assignment.description = description
    if priority is not None:
        assignment.priority = priority
    if due_date is not _SENTINEL:
        assignment.due_date = due_date  # type: ignore[assignment]
    if is_confidential is not None:
        assignment.is_confidential = is_confidential

    assignment.save()

    if tag_ids is not None:
        from simorgh.apps.assignments.models import AssignmentTag
        tags = AssignmentTag.objects.filter(tenant_id=assignment.tenant_id, pk__in=tag_ids)
        assignment.tags.set(tags)

    return assignment


def delete_assignment(assignment: Assignment) -> None:
    assignment.delete()


def complete_assignment(assignment: Assignment, *, completed_by_id: int) -> Assignment:
    from simorgh.apps.events.bus import dispatch
    from simorgh.core.audit import record_service_event

    if assignment.status in (AssignmentStatus.COMPLETED, AssignmentStatus.CANCELLED):
        raise ValueError("Cannot complete an assignment that is already completed or cancelled.")

    assignment.status = AssignmentStatus.COMPLETED
    assignment.completed_at = timezone.now()
    assignment.completed_by_id = completed_by_id
    assignment.save(update_fields=["status", "completed_at", "completed_by_id", "updated_at"])

    record_service_event("assignments.assignment.completed", resource=assignment, after={
        "completed_by_id": completed_by_id,
    })
    dispatch("assignment.completed", {
        "assignment_id":    str(assignment.public_id),
        "completed_by_id":  completed_by_id,
        "tenant_id":        assignment.tenant_id,
    })
    return assignment


def reassign_assignment(assignment: Assignment, *, to_user_id: int, actor_id: int | None = None) -> Assignment:
    from simorgh.apps.events.bus import dispatch
    from simorgh.core.audit import record_service_event

    if assignment.status not in ("pending", "in_progress"):
        raise ValueError("Only pending or in-progress assignments can be reassigned.")

    from_user_id = assignment.assigned_to_id

    if assignment.status == "pending":
        assignment.status = "in_progress"
    assignment.assigned_to_id = to_user_id
    assignment.save(update_fields=["status", "assigned_to_id", "updated_at"])

    record_service_event("assignments.assignment.reassigned", resource=assignment, after={
        "from_user_id": from_user_id,
        "to_user_id":   to_user_id,
    })
    dispatch("assignment.reassigned", {
        "assignment_id":  str(assignment.public_id),
        "from_user_id":   from_user_id,
        "to_user_id":     to_user_id,
        "tenant_id":      assignment.tenant_id,
    })
    return assignment


def cancel_assignment(assignment: Assignment, *, actor_id: int | None = None) -> Assignment:
    from simorgh.apps.events.bus import dispatch
    from simorgh.core.audit import record_service_event

    if assignment.status in (AssignmentStatus.COMPLETED, AssignmentStatus.CANCELLED):
        raise ValueError("Cannot cancel an assignment that is already completed or cancelled.")

    assignment.status = AssignmentStatus.CANCELLED
    assignment.save(update_fields=["status", "updated_at"])

    record_service_event("assignments.assignment.cancelled", resource=assignment)
    dispatch("assignment.cancelled", {
        "assignment_id":   str(assignment.public_id),
        "cancelled_by_id": actor_id,
        "tenant_id":       assignment.tenant_id,
    })
    return assignment
