"""Phase 6.1 CRM validators."""
from __future__ import annotations

import re
from decimal import Decimal
from typing import Any

from shared.exceptions import AppError

from app.models.types import CustomFieldType, LeadStatus

EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
PHONE_RE = re.compile(r"^[\d\s\+\-\(\)]{5,30}$")


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_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_owner(owner_user_id: str | None) -> str:
    return validate_non_empty(owner_user_id, field="owner_user_id")


def validate_lead_status(status: LeadStatus | str) -> LeadStatus:
    if isinstance(status, LeadStatus):
        return status
    try:
        return LeadStatus(status)
    except ValueError as exc:
        raise AppError(
            "وضعیت سرنخ نامعتبر است",
            status_code=422,
            error_code="invalid_lead_status",
        ) from exc


def validate_amount(amount: Decimal | None) -> Decimal:
    value = amount if amount is not None else Decimal("0")
    if value < 0:
        raise AppError(
            "مبلغ نمی‌تواند منفی باشد",
            status_code=422,
            error_code="invalid_amount",
        )
    return value


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_win_probability(value: int) -> int:
    if value < 0 or value > 100:
        raise AppError(
            "احتمال برد باید بین ۰ و ۱۰۰ باشد",
            status_code=422,
            error_code="invalid_win_probability",
        )
    return value


def validate_address_line(line1: str | None) -> str:
    return validate_non_empty(line1, field="line1")


def validate_tag_name(name: str | None) -> str:
    cleaned = validate_non_empty(name, field="name")
    if len(cleaned) > 100:
        raise AppError(
            "نام تگ بیش از حد طولانی است",
            status_code=422,
            error_code="tag_name_too_long",
        )
    return cleaned


def validate_custom_field_value(
    *,
    field_type: CustomFieldType,
    is_required: bool,
    options: list | None,
    value_text: str | None = None,
    value_number: Decimal | None = None,
    value_boolean: bool | None = None,
    value_date: Any = None,
    value_json: list | dict | None = None,
) -> None:
    has_value = any(
        v is not None
        for v in (value_text, value_number, value_boolean, value_date, value_json)
    )
    if is_required and not has_value:
        raise AppError(
            "مقدار فیلد سفارشی الزامی است",
            status_code=422,
            error_code="custom_field_required",
        )
    if field_type == CustomFieldType.TEXT and value_text is None and has_value:
        raise AppError(
            "مقدار متنی مورد انتظار است",
            status_code=422,
            error_code="custom_field_type_mismatch",
        )
    if field_type == CustomFieldType.NUMBER and value_number is None and has_value:
        raise AppError(
            "مقدار عددی مورد انتظار است",
            status_code=422,
            error_code="custom_field_type_mismatch",
        )
    if field_type == CustomFieldType.BOOLEAN and value_boolean is None and has_value:
        raise AppError(
            "مقدار بولین مورد انتظار است",
            status_code=422,
            error_code="custom_field_type_mismatch",
        )
    if field_type == CustomFieldType.DATE and value_date is None and has_value:
        raise AppError(
            "مقدار تاریخ مورد انتظار است",
            status_code=422,
            error_code="custom_field_type_mismatch",
        )
    if field_type == CustomFieldType.SELECT and value_text is not None and options:
        if value_text not in options:
            raise AppError(
                "گزینه انتخابی نامعتبر است",
                status_code=422,
                error_code="custom_field_invalid_option",
            )
    if field_type == CustomFieldType.MULTI_SELECT and value_json is not None and options:
        if not isinstance(value_json, list):
            raise AppError(
                "مقدار چندگزینه‌ای باید لیست باشد",
                status_code=422,
                error_code="custom_field_type_mismatch",
            )
        for item in value_json:
            if item not in options:
                raise AppError(
                    "گزینه انتخابی نامعتبر است",
                    status_code=422,
                    error_code="custom_field_invalid_option",
                )


def validate_file_storage_id(file_storage_id: str | None) -> str:
    return validate_non_empty(file_storage_id, field="file_storage_id")


def validate_attachment_file_name(file_name: str | None) -> str:
    return validate_non_empty(file_name, field="file_name")


# Compatibility aliases used by Phase 6.0 opportunity/quote services
ensure_quote_number = lambda n: validate_non_empty(n, field="quote_number")  # noqa: E731


def ensure_opportunity_open(status) -> None:
    from app.models.types import OpportunityStatus

    if status != OpportunityStatus.OPEN:
        raise AppError(
            "فقط فرصت‌های باز قابل این عملیات هستند",
            status_code=409,
            error_code="opportunity_not_open",
        )


def ensure_activity_completable(status) -> None:
    from app.models.types import ActivityStatus

    if status == ActivityStatus.COMPLETED:
        raise AppError(
            "فعالیت قبلاً تکمیل شده است",
            status_code=409,
            error_code="activity_already_completed",
        )
    if status == ActivityStatus.CANCELLED:
        raise AppError(
            "فعالیت لغوشده قابل تکمیل نیست",
            status_code=409,
            error_code="activity_cancelled",
        )


def ensure_quote_mutable(status) -> None:
    from app.models.types import QuoteStatus

    if status in {QuoteStatus.ACCEPTED, QuoteStatus.CANCELLED}:
        raise AppError(
            "این پیش‌فاکتور قابل ویرایش نیست",
            status_code=409,
            error_code="quote_not_mutable",
        )
