"""Forms / surveys / appointments validators — Phase 11.6."""
from __future__ import annotations

from typing import Any

from app.models.types import (
    AppointmentBookingStatus,
    AppointmentPageStatus,
    FormStatus,
    FormSubmissionStatus,
    SurveyResponseStatus,
    SurveyStatus,
)
from app.validators.foundation import ValidationError, validate_non_empty
from shared.exceptions import AppError

_FORBIDDEN_OWNERSHIP_KEYS = frozenset(
    {
        "crm_contact_id",
        "crm_id",
        "schedule_engine",
        "calendar_engine",
        "booking_engine",
        "forms_engine",
        "survey_engine",
    }
)

FORM_STATUS_TRANSITIONS: dict[FormStatus, frozenset[FormStatus]] = {
    FormStatus.DRAFT: frozenset({FormStatus.ACTIVE, FormStatus.ARCHIVED}),
    FormStatus.ACTIVE: frozenset({FormStatus.DRAFT, FormStatus.ARCHIVED}),
    FormStatus.ARCHIVED: frozenset(),
}

SURVEY_STATUS_TRANSITIONS: dict[SurveyStatus, frozenset[SurveyStatus]] = {
    SurveyStatus.DRAFT: frozenset({SurveyStatus.ACTIVE, SurveyStatus.ARCHIVED}),
    SurveyStatus.ACTIVE: frozenset({SurveyStatus.DRAFT, SurveyStatus.ARCHIVED}),
    SurveyStatus.ARCHIVED: frozenset(),
}

APPOINTMENT_PAGE_STATUS_TRANSITIONS: dict[
    AppointmentPageStatus, frozenset[AppointmentPageStatus]
] = {
    AppointmentPageStatus.DRAFT: frozenset(
        {AppointmentPageStatus.ACTIVE, AppointmentPageStatus.ARCHIVED}
    ),
    AppointmentPageStatus.ACTIVE: frozenset(
        {AppointmentPageStatus.DRAFT, AppointmentPageStatus.ARCHIVED}
    ),
    AppointmentPageStatus.ARCHIVED: frozenset(),
}

SUBMISSION_STATUS_TRANSITIONS: dict[
    FormSubmissionStatus, frozenset[FormSubmissionStatus]
] = {
    FormSubmissionStatus.RECEIVED: frozenset(
        {
            FormSubmissionStatus.PROCESSED,
            FormSubmissionStatus.FAILED,
            FormSubmissionStatus.ARCHIVED,
        }
    ),
    FormSubmissionStatus.PROCESSED: frozenset({FormSubmissionStatus.ARCHIVED}),
    FormSubmissionStatus.FAILED: frozenset(
        {FormSubmissionStatus.RECEIVED, FormSubmissionStatus.ARCHIVED}
    ),
    FormSubmissionStatus.ARCHIVED: frozenset(),
}

RESPONSE_STATUS_TRANSITIONS: dict[
    SurveyResponseStatus, frozenset[SurveyResponseStatus]
] = {
    SurveyResponseStatus.RECEIVED: frozenset(
        {
            SurveyResponseStatus.PROCESSED,
            SurveyResponseStatus.FAILED,
            SurveyResponseStatus.ARCHIVED,
        }
    ),
    SurveyResponseStatus.PROCESSED: frozenset({SurveyResponseStatus.ARCHIVED}),
    SurveyResponseStatus.FAILED: frozenset(
        {SurveyResponseStatus.RECEIVED, SurveyResponseStatus.ARCHIVED}
    ),
    SurveyResponseStatus.ARCHIVED: frozenset(),
}

BOOKING_STATUS_TRANSITIONS: dict[
    AppointmentBookingStatus, frozenset[AppointmentBookingStatus]
] = {
    AppointmentBookingStatus.PENDING: frozenset(
        {
            AppointmentBookingStatus.CONFIRMED,
            AppointmentBookingStatus.CANCELLED,
            AppointmentBookingStatus.ARCHIVED,
        }
    ),
    AppointmentBookingStatus.CONFIRMED: frozenset(
        {AppointmentBookingStatus.CANCELLED, AppointmentBookingStatus.ARCHIVED}
    ),
    AppointmentBookingStatus.CANCELLED: frozenset({AppointmentBookingStatus.ARCHIVED}),
    AppointmentBookingStatus.ARCHIVED: frozenset(),
}


def ensure_json_object(value: Any, field: str) -> dict | None:
    if value is None:
        return None
    if not isinstance(value, dict):
        raise ValidationError(f"{field} باید object باشد", {"field": field})
    banned = _FORBIDDEN_OWNERSHIP_KEYS.intersection(value.keys())
    if banned:
        raise ValidationError(
            "کلیدهای مالکیت CRM/scheduling در payload مجاز نیست",
            {"field": field, "forbidden_keys": sorted(banned)},
        )
    return value


def validate_external_ref(value: str | None, field: str) -> str | None:
    if value is None:
        return None
    cleaned = validate_non_empty(value, field)
    if len(cleaned) > 255:
        raise ValidationError(
            f"{field} بیش از حد طولانی است", {"field": field, "max_length": 255}
        )
    return cleaned


def ensure_form_status_transition(current: FormStatus, target: FormStatus) -> None:
    if current == target:
        return
    if target not in FORM_STATUS_TRANSITIONS.get(current, frozenset()):
        raise AppError(
            "گذار وضعیت فرم مجاز نیست",
            error_code="invalid_form_status_transition",
            status_code=409,
            details={"current": current.value, "target": target.value},
        )


def ensure_survey_status_transition(
    current: SurveyStatus, target: SurveyStatus
) -> None:
    if current == target:
        return
    if target not in SURVEY_STATUS_TRANSITIONS.get(current, frozenset()):
        raise AppError(
            "گذار وضعیت نظرسنجی مجاز نیست",
            error_code="invalid_survey_status_transition",
            status_code=409,
            details={"current": current.value, "target": target.value},
        )


def ensure_appointment_page_status_transition(
    current: AppointmentPageStatus, target: AppointmentPageStatus
) -> None:
    if current == target:
        return
    if target not in APPOINTMENT_PAGE_STATUS_TRANSITIONS.get(current, frozenset()):
        raise AppError(
            "گذار وضعیت صفحه نوبت مجاز نیست",
            error_code="invalid_appointment_page_status_transition",
            status_code=409,
            details={"current": current.value, "target": target.value},
        )


def ensure_submission_status_transition(
    current: FormSubmissionStatus, target: FormSubmissionStatus
) -> None:
    if current == target:
        return
    if target not in SUBMISSION_STATUS_TRANSITIONS.get(current, frozenset()):
        raise AppError(
            "گذار وضعیت ارسال فرم مجاز نیست",
            error_code="invalid_form_submission_status_transition",
            status_code=409,
            details={"current": current.value, "target": target.value},
        )


def ensure_response_status_transition(
    current: SurveyResponseStatus, target: SurveyResponseStatus
) -> None:
    if current == target:
        return
    if target not in RESPONSE_STATUS_TRANSITIONS.get(current, frozenset()):
        raise AppError(
            "گذار وضعیت پاسخ نظرسنجی مجاز نیست",
            error_code="invalid_survey_response_status_transition",
            status_code=409,
            details={"current": current.value, "target": target.value},
        )


def ensure_booking_status_transition(
    current: AppointmentBookingStatus, target: AppointmentBookingStatus
) -> None:
    if current == target:
        return
    if target not in BOOKING_STATUS_TRANSITIONS.get(current, frozenset()):
        raise AppError(
            "گذار وضعیت رزرو مجاز نیست",
            error_code="invalid_booking_status_transition",
            status_code=409,
            details={"current": current.value, "target": target.value},
        )
