"""Domain exceptions with stable machine-readable codes.

Handlers translate these into short Persian messages. Internal details are
logged, never shown verbatim to Telegram users.
"""

from __future__ import annotations


class AppError(Exception):
    code = "app_error"
    default_user_message = "یک خطای غیرمنتظره رخ داد."

    def __init__(self, message: str | None = None, *, user_message: str | None = None) -> None:
        super().__init__(message or self.default_user_message)
        self.user_message = user_message or self.default_user_message


class InvalidUrlError(AppError):
    code = "invalid_url"
    default_user_message = "آدرس نامعتبر است. فقط آدرس کامل با پروتکل http یا https پذیرفته می‌شود."


class BlockedUrlError(AppError):
    code = "blocked_url"
    default_user_message = "دسترسی به این آدرس به دلایل امنیتی مجاز نیست."


class DomainNotAllowedError(AppError):
    code = "domain_not_allowed"
    default_user_message = "دامنه این آدرس در فهرست دامنه‌های مجاز نیست."


class SsrfBlockedError(BlockedUrlError):
    code = "ssrf_blocked"
    default_user_message = "این آدرس به شبکه داخلی یا محدوده غیرمجاز اشاره می‌کند و مسدود شد."


class RobotsDeniedError(AppError):
    code = "robots_denied"
    default_user_message = "سیاست robots.txt این سایت اجازه بررسی آن را نمی‌دهد."


class AccessDeniedError(AppError):
    code = "access_denied"
    default_user_message = "سایت دسترسی را رد کرد. برای دور زدن محدودیت تلاش نمی‌شود."

    def __init__(self, status: int = 403, message: str | None = None) -> None:
        super().__init__(message or f"HTTP {status}")
        self.status = status


class NotFoundError(AppError):
    code = "not_found"
    default_user_message = "صفحه یا فایل مورد نظر پیدا نشد (HTTP 404)."


class RateLimitedError(AppError):
    code = "rate_limited"
    default_user_message = "سایت محدودیت نرخ درخواست اعمال کرده است. بعداً دوباره تلاش کنید."

    def __init__(self, retry_after: float | None = None, message: str | None = None) -> None:
        super().__init__(message or "rate limited")
        self.retry_after = retry_after


class NetworkError(AppError):
    code = "network_error"
    default_user_message = "خطای شبکه یا DNS رخ داد."


class ContentTooLargeError(AppError):
    code = "content_too_large"
    default_user_message = "حجم محتوا از محدودیت مجاز بیشتر است."


class InsufficientDiskSpaceError(AppError):
    code = "insufficient_disk"
    default_user_message = "فضای دیسک برای دانلود کافی نیست."


class InvalidMediaError(AppError):
    code = "invalid_media"
    default_user_message = "فایل دریافتی یک ویدئوی معتبر نیست و پذیرفته نشد."


class MediaNotFoundError(AppError):
    code = "media_not_found"
    default_user_message = "هیچ ویدئوی قابل دسترسی در این آدرس پیدا نشد."


class BrowserUnavailableError(AppError):
    code = "browser_unavailable"
    default_user_message = "مرورگر Playwright نصب یا قابل اجرا نیست."


class BrowserBlockedRequestError(BlockedUrlError):
    code = "browser_blocked_request"


class DownloadCancelledError(AppError):
    code = "download_cancelled"
    default_user_message = "دانلود لغو شد."


class JobCancelledError(AppError):
    code = "job_cancelled"
    default_user_message = "عملیات لغو شد."


class DuplicateJobError(AppError):
    code = "duplicate_job"
    default_user_message = "برای این آدرس از قبل یک عملیات فعال وجود دارد."


class JobStateError(AppError):
    code = "job_state"
    default_user_message = "این عملیات در وضعیت مناسبی برای این تغییر نیست."


class UploadUncertainError(AppError):
    code = "upload_uncertain"
    default_user_message = (
        "نتیجه ارسال به تلگرام قطعی نیست. برای جلوگیری از ارسال تکراری، تلاش خودکار متوقف شد."
    )


class UploadRejectedError(AppError):
    code = "upload_rejected"
    default_user_message = "تلگرام ارسال فایل را نپذیرفت."


class DestinationError(AppError):
    code = "destination_error"
    default_user_message = "مقصد انتشار تنظیم نشده یا ربات به آن دسترسی ندارد."


class PermanentSiteError(AppError):
    code = "permanent_site_error"
    default_user_message = "سایت خطای دائمی برگرداند و پردازش متوقف شد."


def sanitize_error(exc: BaseException, limit: int = 500) -> str:
    """Return a compact, non-traceback error description for persistence.

    The logging redaction filter is still responsible for token-like strings.
    URL query strings are intentionally not copied into the database.
    """

    name = type(exc).__name__
    text = " ".join(str(exc).split())
    if len(text) > limit:
        text = text[: limit - 1] + "…"
    return f"{name}: {text}" if text else name
