"""Tasks domain models.

Model inventory
---------------
TaskStatus        — enum: todo / in_progress / in_review / done / cancelled
TaskPriority      — enum: none / low / medium / high / urgent
TaskTag           — per-tenant flat label
Task              — core entity: tenant-scoped task
TaskComment       — comment thread on a task
TaskAttachment    — file attachment reference (DMS FileRecord)
TaskStatusChange  — immutable audit log of status transitions
TaskWatcher       — user watching a task for notifications

Design decisions
----------------
* Task extends UUIDModel + TenantScopedModel (same as Project).
* project / phase FKs are nullable so standalone tasks are supported.
* parent FK on Task enables subtask trees (single-level assumed; UI can show deeper).
* TaskStatusChange is append-only (never edited).
* TaskWatcher uses UniqueConstraint so one user watches a task once.
"""

from __future__ import annotations

from typing import ClassVar

from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _

from simorgh.core.models import (
    TenantScopedModel,
    TimeStampedModel,
    UUIDModel,
)


# ---------------------------------------------------------------------------
# Choices
# ---------------------------------------------------------------------------

class TaskStatus(models.TextChoices):
    TODO       = "todo",        _("To Do")
    IN_PROGRESS = "in_progress", _("In Progress")
    IN_REVIEW  = "in_review",   _("In Review")
    DONE       = "done",        _("Done")
    CANCELLED  = "cancelled",   _("Cancelled")


class TaskPriority(models.TextChoices):
    NONE   = "none",   _("None")
    LOW    = "low",    _("Low")
    MEDIUM = "medium", _("Medium")
    HIGH   = "high",   _("High")
    URGENT = "urgent", _("Urgent")


# ---------------------------------------------------------------------------
# TaskTag
# ---------------------------------------------------------------------------

class TaskTag(UUIDModel, TimeStampedModel):
    """Flat, tenant-scoped label applied to tasks."""

    tenant = models.ForeignKey(
        "tenants.Tenant",
        on_delete=models.CASCADE,
        related_name="task_tags",
    )
    name  = models.CharField(_("name"),  max_length=100)
    color = models.CharField(_("color"), max_length=20, blank=True, default="")

    class Meta:
        verbose_name        = _("task tag")
        verbose_name_plural = _("task tags")
        constraints = (
            models.UniqueConstraint(
                fields=("tenant", "name"),
                name="tasks_tag_unique_tenant_name",
            ),
        )

    def __str__(self) -> str:
        return self.name


# ---------------------------------------------------------------------------
# Task
# ---------------------------------------------------------------------------

class Task(UUIDModel, TenantScopedModel):
    """A work item belonging to a tenant, optionally linked to a project/phase."""

    title       = models.CharField(_("title"), max_length=500)
    description = models.TextField(_("description"), blank=True, default="")

    status = models.CharField(
        _("status"),
        max_length=20,
        choices=TaskStatus.choices,
        default=TaskStatus.TODO,
        db_index=True,
    )
    priority = models.CharField(
        _("priority"),
        max_length=10,
        choices=TaskPriority.choices,
        default=TaskPriority.NONE,
        db_index=True,
    )

    project = models.ForeignKey(
        "projects.Project",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="tasks",
        verbose_name=_("project"),
    )
    phase = models.ForeignKey(
        "projects.ProjectPhase",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="tasks",
        verbose_name=_("phase"),
    )

    assignee = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="assigned_tasks",
        verbose_name=_("assignee"),
    )
    reporter = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.PROTECT,
        related_name="reported_tasks",
        verbose_name=_("reporter"),
    )

    due_date        = models.DateField(_("due date"), null=True, blank=True)
    estimated_hours = models.DecimalField(
        _("estimated hours"), max_digits=7, decimal_places=2, null=True, blank=True
    )

    parent = models.ForeignKey(
        "self",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="subtasks",
        verbose_name=_("parent task"),
    )

    tags = models.ManyToManyField(
        TaskTag,
        blank=True,
        related_name="tasks",
        verbose_name=_("tags"),
    )

    order = models.FloatField(_("order"), default=0.0, db_index=True)

    class Meta(TenantScopedModel.Meta):
        verbose_name        = _("task")
        verbose_name_plural = _("tasks")
        ordering            = ["order", "created_at"]
        indexes: ClassVar = [
            *TenantScopedModel.Meta.indexes,
            models.Index(fields=("tenant", "project", "status"), name="tasks_task_tenant_proj_status"),
            models.Index(fields=("tenant", "assignee", "status"), name="tasks_task_tenant_asgn_status"),
            models.Index(fields=("tenant", "due_date"),            name="tasks_task_tenant_due_date"),
        ]

    def __str__(self) -> str:
        return self.title


# ---------------------------------------------------------------------------
# TaskComment
# ---------------------------------------------------------------------------

class TaskComment(TimeStampedModel):
    """Comment posted on a task."""

    task      = models.ForeignKey(Task, on_delete=models.CASCADE, related_name="comments")
    author    = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        related_name="task_comments",
    )
    body      = models.TextField(_("body"))
    is_edited = models.BooleanField(_("is edited"), default=False)

    class Meta:
        verbose_name        = _("task comment")
        verbose_name_plural = _("task comments")
        ordering            = ["created_at"]

    def __str__(self) -> str:
        return f"Comment on {self.task_id} by {self.author_id}"


# ---------------------------------------------------------------------------
# TaskAttachment
# ---------------------------------------------------------------------------

class TaskAttachment(TimeStampedModel):
    """Reference to a DMS file attached to a task."""

    task        = models.ForeignKey(Task, on_delete=models.CASCADE, related_name="attachments")
    dms_file_id = models.UUIDField(_("DMS file ID"), db_index=True)
    filename    = models.CharField(_("filename"), max_length=255, blank=True, default="")
    uploaded_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        related_name="task_attachments",
    )

    class Meta:
        verbose_name        = _("task attachment")
        verbose_name_plural = _("task attachments")
        ordering            = ["created_at"]

    def __str__(self) -> str:
        return f"Attachment {self.dms_file_id} on task {self.task_id}"


# ---------------------------------------------------------------------------
# TaskStatusChange
# ---------------------------------------------------------------------------

class TaskStatusChange(models.Model):
    """Immutable log of every status transition on a task."""

    task        = models.ForeignKey(Task, on_delete=models.CASCADE, related_name="status_changes")
    from_status = models.CharField(_("from status"), max_length=20)
    to_status   = models.CharField(_("to status"),   max_length=20)
    changed_by  = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        related_name="task_status_changes",
    )
    changed_at  = models.DateTimeField(_("changed at"), auto_now_add=True)

    class Meta:
        verbose_name        = _("task status change")
        verbose_name_plural = _("task status changes")
        ordering            = ["changed_at"]

    def __str__(self) -> str:
        return f"Task {self.task_id}: {self.from_status} → {self.to_status}"


# ---------------------------------------------------------------------------
# TaskWatcher
# ---------------------------------------------------------------------------

class TaskWatcher(models.Model):
    """A user who is watching a task for notifications."""

    task = models.ForeignKey(Task, on_delete=models.CASCADE, related_name="watchers")
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="watched_tasks",
    )

    class Meta:
        verbose_name        = _("task watcher")
        verbose_name_plural = _("task watchers")
        constraints = (
            models.UniqueConstraint(
                fields=("task", "user"),
                name="tasks_watcher_unique_task_user",
            ),
        )

    def __str__(self) -> str:
        return f"Watcher {self.user_id} on task {self.task_id}"
