"""Phase 7.1 membership lifecycle validators."""
from __future__ import annotations

from datetime import datetime, timedelta, timezone

from shared.exceptions import AppError

from app.models.types import MemberStatus, MembershipLifecycleAction

DEFAULT_TERM_DAYS = 365

# Canonical transitions for the Membership Engine.
ALLOWED_TRANSITIONS: dict[MembershipLifecycleAction, set[MemberStatus]] = {
    MembershipLifecycleAction.ENROLL: {MemberStatus.PENDING},
    MembershipLifecycleAction.ACTIVATE: {MemberStatus.PENDING, MemberStatus.EXPIRED},
    MembershipLifecycleAction.RENEW: {
        MemberStatus.ACTIVE,
        MemberStatus.EXPIRED,
        MemberStatus.FROZEN,
        MemberStatus.SUSPENDED,
    },
    MembershipLifecycleAction.FREEZE: {MemberStatus.ACTIVE},
    MembershipLifecycleAction.RESUME: {
        MemberStatus.FROZEN,
        MemberStatus.SUSPENDED,
    },
    MembershipLifecycleAction.CANCEL: {
        MemberStatus.PENDING,
        MemberStatus.ACTIVE,
        MemberStatus.FROZEN,
        MemberStatus.SUSPENDED,
        MemberStatus.EXPIRED,
    },
    MembershipLifecycleAction.EXPIRE: {
        MemberStatus.ACTIVE,
        MemberStatus.FROZEN,
        MemberStatus.SUSPENDED,
    },
    MembershipLifecycleAction.TRANSFER: {
        MemberStatus.ACTIVE,
        MemberStatus.FROZEN,
        MemberStatus.SUSPENDED,
    },
}

TERMINAL_STATUSES = frozenset(
    {
        MemberStatus.CANCELLED,
        MemberStatus.TRANSFERRED,
        MemberStatus.CLOSED,
    }
)

ACTIVE_MEMBER_STATUSES = frozenset(
    {
        MemberStatus.PENDING,
        MemberStatus.ACTIVE,
        MemberStatus.FROZEN,
        MemberStatus.SUSPENDED,
        MemberStatus.EXPIRED,
    }
)


def ensure_lifecycle_transition(
    *,
    action: MembershipLifecycleAction,
    current: MemberStatus,
) -> None:
    if current in TERMINAL_STATUSES:
        raise AppError(
            "عضویت در وضعیت پایانی است و قابل تغییر نیست",
            status_code=409,
            error_code="membership_terminal",
            details={"status": current.value, "action": action.value},
        )
    allowed = ALLOWED_TRANSITIONS.get(action, set())
    if current not in allowed:
        raise AppError(
            "انتقال وضعیت عضویت مجاز نیست",
            status_code=409,
            error_code="invalid_lifecycle_transition",
            details={
                "from_status": current.value,
                "action": action.value,
                "allowed_from": sorted(s.value for s in allowed),
            },
        )


def target_status_for(action: MembershipLifecycleAction) -> MemberStatus:
    mapping = {
        MembershipLifecycleAction.ENROLL: MemberStatus.ACTIVE,
        MembershipLifecycleAction.ACTIVATE: MemberStatus.ACTIVE,
        MembershipLifecycleAction.RENEW: MemberStatus.ACTIVE,
        MembershipLifecycleAction.FREEZE: MemberStatus.FROZEN,
        MembershipLifecycleAction.RESUME: MemberStatus.ACTIVE,
        MembershipLifecycleAction.CANCEL: MemberStatus.CANCELLED,
        MembershipLifecycleAction.EXPIRE: MemberStatus.EXPIRED,
        MembershipLifecycleAction.TRANSFER: MemberStatus.TRANSFERRED,
    }
    return mapping[action]


def compute_expiry(
    *,
    from_when: datetime | None = None,
    term_days: int = DEFAULT_TERM_DAYS,
    explicit: datetime | None = None,
) -> datetime:
    if explicit is not None:
        if explicit.tzinfo is None:
            explicit = explicit.replace(tzinfo=timezone.utc)
        return explicit
    if term_days < 1:
        raise AppError(
            "مدت عضویت باید حداقل ۱ روز باشد",
            status_code=422,
            error_code="invalid_term_days",
        )
    base = from_when or datetime.now(timezone.utc)
    if base.tzinfo is None:
        base = base.replace(tzinfo=timezone.utc)
    return base + timedelta(days=term_days)


def validate_reason(reason: str | None, *, required: bool = False, field: str = "reason") -> str | None:
    if reason is None or not str(reason).strip():
        if required:
            raise AppError(
                f"{field} الزامی است",
                status_code=422,
                error_code="validation_error",
                details={"field": field},
            )
        return None
    cleaned = str(reason).strip()
    if len(cleaned) > 500:
        raise AppError(
            f"{field} بیش از حد طولانی است",
            status_code=422,
            error_code="validation_error",
            details={"field": field},
        )
    return cleaned
