"""File-upload validators for SME-Hub forms.

Centralises MIME-type and size checks so pitch decks, deal-room documents,
signed MoUs and similar artefacts cannot be used as an upload-abuse vector.

Usage:
    from apps.smehub._shared.validators import build_file_validator

    pitch_deck = forms.FileField(validators=[PITCH_DECK_VALIDATOR])
"""
from __future__ import annotations

import os
from typing import Iterable

from django.core.exceptions import ValidationError
from django.utils.deconstruct import deconstructible


@deconstructible
class FileSafetyValidator:
    """Reject uploads outside an allowed extension/size envelope.

    The check is intentionally extension-based (cheap, deterministic) plus a
    coarse content-type sniff when the browser supplies one. Anti-malware
    scanning is out of scope and lives at the storage tier.
    """

    def __init__(
        self,
        allowed_extensions: Iterable[str],
        max_size_mb: int,
        allowed_content_types: Iterable[str] | None = None,
    ):
        self.allowed_extensions = tuple(ext.lower().lstrip(".") for ext in allowed_extensions)
        self.max_size_mb = int(max_size_mb)
        self.allowed_content_types = tuple(allowed_content_types or ())

    def __call__(self, file_obj):
        if file_obj is None:
            return
        name = getattr(file_obj, "name", "") or ""
        ext = os.path.splitext(name)[1].lower().lstrip(".")
        if ext not in self.allowed_extensions:
            raise ValidationError(
                f"Unsupported file type \".{ext}\". Allowed: "
                f"{', '.join('.' + e for e in self.allowed_extensions)}.",
                code="unsupported_extension",
            )
        size = getattr(file_obj, "size", None)
        if size is not None and size > self.max_size_mb * 1024 * 1024:
            raise ValidationError(
                f"File too large ({size / (1024 * 1024):.1f} MB). "
                f"Maximum allowed: {self.max_size_mb} MB.",
                code="file_too_large",
            )
        content_type = getattr(file_obj, "content_type", "") or ""
        if self.allowed_content_types and content_type and content_type not in self.allowed_content_types:
            raise ValidationError(
                f"Unexpected content type \"{content_type}\".",
                code="unsupported_content_type",
            )

    def __eq__(self, other):
        return (
            isinstance(other, FileSafetyValidator)
            and self.allowed_extensions == other.allowed_extensions
            and self.max_size_mb == other.max_size_mb
            and self.allowed_content_types == other.allowed_content_types
        )


# Preset envelopes used by the modules.

PITCH_DECK_VALIDATOR = FileSafetyValidator(
    allowed_extensions=("pdf", "ppt", "pptx", "key"),
    max_size_mb=25,
    allowed_content_types=(
        "application/pdf",
        "application/vnd.ms-powerpoint",
        "application/vnd.openxmlformats-officedocument.presentationml.presentation",
        "application/octet-stream",
    ),
)

DEAL_ROOM_DOCUMENT_VALIDATOR = FileSafetyValidator(
    allowed_extensions=(
        "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx",
        "png", "jpg", "jpeg", "txt", "csv",
    ),
    max_size_mb=50,
)

SIGNED_AGREEMENT_VALIDATOR = FileSafetyValidator(
    allowed_extensions=("pdf",),
    max_size_mb=25,
    allowed_content_types=("application/pdf",),
)

DEAL_ROOM_ATTACHMENT_VALIDATOR = FileSafetyValidator(
    allowed_extensions=(
        "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx",
        "png", "jpg", "jpeg", "txt", "csv",
    ),
    max_size_mb=25,
)
