"""Phase 9.0 Sports Center validators."""
from __future__ import annotations

import re
from datetime import date

from shared.exceptions import AppError

from app.models.types import (
    ConnectorCapability,
    DeviceStatus,
    FacilityKind,
    LifecycleStatus,
    LockerStatus,
    MembershipStatus,
)

EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
PHONE_RE = re.compile(r"^[\d\s\+\-\(\)]{5,30}$")
CODE_RE = re.compile(r"^[A-Za-z0-9_\-]{2,50}$")
SETTING_KEY_RE = re.compile(r"^[A-Za-z0-9_.\-]{2,100}$")


def validate_non_empty(value: str | None, *, field: str) -> str:
    if value is None or not str(value).strip():
        raise AppError(
            f"{field} الزامی است",
            status_code=422,
            error_code="validation_error",
            details={"field": field},
        )
    return str(value).strip()


def validate_code(code: str | None, *, field: str = "code") -> str:
    cleaned = validate_non_empty(code, field=field).upper()
    if not CODE_RE.match(cleaned):
        raise AppError(
            f"فرمت {field} نامعتبر است",
            status_code=422,
            error_code="invalid_code",
            details={"field": field},
        )
    return cleaned


def validate_setting_key(key: str | None) -> str:
    cleaned = validate_non_empty(key, field="key")
    if not SETTING_KEY_RE.match(cleaned):
        raise AppError(
            "فرمت key نامعتبر است",
            status_code=422,
            error_code="invalid_setting_key",
        )
    return cleaned


def validate_email(email: str | None, *, required: bool = False) -> str | None:
    if email is None or not email.strip():
        if required:
            raise AppError(
                "ایمیل الزامی است",
                status_code=422,
                error_code="email_required",
            )
        return None
    cleaned = email.strip().lower()
    if not EMAIL_RE.match(cleaned):
        raise AppError(
            "فرمت ایمیل نامعتبر است",
            status_code=422,
            error_code="invalid_email",
        )
    return cleaned


def validate_phone(phone: str | None, *, field: str = "phone") -> str | None:
    if phone is None or not phone.strip():
        return None
    cleaned = phone.strip()
    if not PHONE_RE.match(cleaned):
        raise AppError(
            f"فرمت {field} نامعتبر است",
            status_code=422,
            error_code="invalid_phone",
            details={"field": field},
        )
    return cleaned


def validate_currency_code(code: str) -> str:
    cleaned = code.strip().upper()
    if len(cleaned) != 3:
        raise AppError(
            "کد ارز باید ۳ حرفی باشد",
            status_code=422,
            error_code="invalid_currency_code",
        )
    return cleaned


def validate_non_negative_int(value: int | None, *, field: str) -> int | None:
    if value is None:
        return None
    resolved = int(value)
    if resolved < 0:
        raise AppError(
            f"{field} نمی‌تواند منفی باشد",
            status_code=422,
            error_code="invalid_amount",
            details={"field": field},
        )
    return resolved


def _enum_or_error(enum_cls, value, *, error_code: str, message: str):
    if isinstance(value, enum_cls):
        return value
    try:
        return enum_cls(value)
    except ValueError as exc:
        raise AppError(message, status_code=422, error_code=error_code) from exc


def validate_lifecycle_status(status: LifecycleStatus | str) -> LifecycleStatus:
    return _enum_or_error(
        LifecycleStatus,
        status,
        error_code="invalid_lifecycle_status",
        message="وضعیت نامعتبر است",
    )


def validate_membership_status(status: MembershipStatus | str) -> MembershipStatus:
    return _enum_or_error(
        MembershipStatus,
        status,
        error_code="invalid_membership_status",
        message="وضعیت عضویت نامعتبر است",
    )


def validate_device_status(status: DeviceStatus | str) -> DeviceStatus:
    return _enum_or_error(
        DeviceStatus,
        status,
        error_code="invalid_device_status",
        message="وضعیت دستگاه نامعتبر است",
    )


def validate_locker_status(status: LockerStatus | str) -> LockerStatus:
    return _enum_or_error(
        LockerStatus,
        status,
        error_code="invalid_locker_status",
        message="وضعیت کمد نامعتبر است",
    )


def validate_capability(capability: ConnectorCapability | str) -> ConnectorCapability:
    return _enum_or_error(
        ConnectorCapability,
        capability,
        error_code="invalid_connector_capability",
        message="قابلیت کانکتور نامعتبر است",
    )


def validate_facility_kind(kind: FacilityKind | str) -> FacilityKind:
    return _enum_or_error(
        FacilityKind,
        kind,
        error_code="invalid_facility_kind",
        message="نوع تسهیلات نامعتبر است",
    )


def validate_date_range(
    starts_on: date | None, ends_on: date | None
) -> tuple[date | None, date | None]:
    if starts_on and ends_on and ends_on < starts_on:
        raise AppError(
            "تاریخ پایان نمی‌تواند قبل از تاریخ شروع باشد",
            status_code=422,
            error_code="invalid_date_range",
        )
    return starts_on, ends_on


def ensure_optimistic_version(entity_version: int, expected: int | None) -> None:
    if expected is None:
        return
    if entity_version != expected:
        raise AppError(
            "نسخه رکورد تغییر کرده است؛ دوباره تلاش کنید",
            status_code=409,
            error_code="version_conflict",
            details={"expected": expected, "actual": entity_version},
        )


def forbid_sport_specific_hardcoding(payload: dict) -> None:
    """Reject payloads that try to inject sport-engine rule keys into foundation."""
    forbidden = {"sport_engine", "hardcoded_sport_rules", "vendor_sdk"}
    present = forbidden.intersection(payload.keys())
    if present:
        raise AppError(
            "قوانین اختصاصی ورزش یا SDK فروشنده در لایه foundation مجاز نیست",
            status_code=422,
            error_code="sport_specific_forbidden",
            details={"fields": sorted(present)},
        )
