"""DMS workflow — state transitions and validators.

States
------
  ""           → initial (no workflow started yet; document just created)
  "draft"      → in workflow, not yet submitted
  "under_review" → submitted for peer / manager review
  "approved"   → reviewer approved; ready to publish
  "rejected"   → reviewer rejected; author must revise and resubmit
  "published"  → live document (Document.status = PUBLISHED)
  "archived"   → retired document (Document.status = ARCHIVED)

Transitions
-----------
  submit_for_review : "" | "draft"      → "under_review"  (manage)
  approve           : "under_review"    → "approved"       (approve)
  reject            : "under_review"    → "rejected"       (approve)
  publish           : "approved"        → "published"      (publish)
  archive           : "published"       → "archived"       (manage)
  resubmit          : "rejected"        → "under_review"   (manage)
  retract           : "under_review"    → "draft"          (manage)
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Callable

from simorgh.apps.dms.documents.permissions import (
    PERM_DOCUMENT_MANAGE,
    PERM_DOCUMENT_PUBLISH,
)

# New permission defined in this phase.
PERM_DOCUMENT_APPROVE = "dms.document.approve"

# ---------------------------------------------------------------------------
# State constants
# ---------------------------------------------------------------------------

class WorkflowState:
    INITIAL = ""
    DRAFT = "draft"
    UNDER_REVIEW = "under_review"
    APPROVED = "approved"
    REJECTED = "rejected"
    PUBLISHED = "published"
    ARCHIVED = "archived"


ALL_STATES = {
    WorkflowState.INITIAL,
    WorkflowState.DRAFT,
    WorkflowState.UNDER_REVIEW,
    WorkflowState.APPROVED,
    WorkflowState.REJECTED,
    WorkflowState.PUBLISHED,
    WorkflowState.ARCHIVED,
}

# ---------------------------------------------------------------------------
# Validator type
# ---------------------------------------------------------------------------

Validator = Callable[["Document"], None]  # type: ignore[name-defined]


def _require_version(doc) -> None:
    """Document must have at least one non-deleted version."""
    from simorgh.apps.dms.documents.models import DocumentVersion

    if not DocumentVersion.objects.filter(document=doc, is_deleted=False).exists():
        raise WorkflowError("Document must have at least one version before entering review.")


def _require_not_archived(doc) -> None:
    from simorgh.apps.dms.documents.models import DocumentStatus

    if doc.status == DocumentStatus.ARCHIVED:
        raise WorkflowError("Cannot transition an archived document.")


# ---------------------------------------------------------------------------
# Transition spec
# ---------------------------------------------------------------------------

@dataclass(frozen=True)
class TransitionSpec:
    action: str
    from_states: frozenset[str]
    to_state: str
    required_permission: str
    validators: tuple[Validator, ...] = ()
    # Human-readable label used in audit log
    label: str = ""


TRANSITIONS: dict[str, TransitionSpec] = {
    "submit_for_review": TransitionSpec(
        action="submit_for_review",
        from_states=frozenset({WorkflowState.INITIAL, WorkflowState.DRAFT}),
        to_state=WorkflowState.UNDER_REVIEW,
        required_permission=PERM_DOCUMENT_MANAGE,
        validators=(_require_version, _require_not_archived),
        label="Submitted for review",
    ),
    "approve": TransitionSpec(
        action="approve",
        from_states=frozenset({WorkflowState.UNDER_REVIEW}),
        to_state=WorkflowState.APPROVED,
        required_permission=PERM_DOCUMENT_APPROVE,
        validators=(_require_not_archived,),
        label="Approved",
    ),
    "reject": TransitionSpec(
        action="reject",
        from_states=frozenset({WorkflowState.UNDER_REVIEW}),
        to_state=WorkflowState.REJECTED,
        required_permission=PERM_DOCUMENT_APPROVE,
        validators=(_require_not_archived,),
        label="Rejected",
    ),
    "publish": TransitionSpec(
        action="publish",
        from_states=frozenset({WorkflowState.APPROVED}),
        to_state=WorkflowState.PUBLISHED,
        required_permission=PERM_DOCUMENT_PUBLISH,
        validators=(_require_not_archived,),
        label="Published",
    ),
    "archive": TransitionSpec(
        action="archive",
        from_states=frozenset({WorkflowState.PUBLISHED}),
        to_state=WorkflowState.ARCHIVED,
        required_permission=PERM_DOCUMENT_MANAGE,
        validators=(),
        label="Archived",
    ),
    "resubmit": TransitionSpec(
        action="resubmit",
        from_states=frozenset({WorkflowState.REJECTED}),
        to_state=WorkflowState.UNDER_REVIEW,
        required_permission=PERM_DOCUMENT_MANAGE,
        validators=(_require_version, _require_not_archived),
        label="Resubmitted for review",
    ),
    "retract": TransitionSpec(
        action="retract",
        from_states=frozenset({WorkflowState.UNDER_REVIEW}),
        to_state=WorkflowState.DRAFT,
        required_permission=PERM_DOCUMENT_MANAGE,
        validators=(_require_not_archived,),
        label="Retracted from review",
    ),
}


class WorkflowError(Exception):
    """Raised for invalid workflow operations."""
