import os
from dataclasses import dataclass
from typing import Callable, Optional

import magic
from django.core.files.storage import default_storage
from django.core.files.uploadedfile import UploadedFile

from .paths import upload_generic
from .registry import FILE_TYPE_REGISTRY, FileTypeConfig
from .validators import FileValidator


@dataclass
class UploadResult:
    file: UploadedFile
    original_name: str
    extension: str
    category: str
    mime_types: list[str]
    size_bytes: int
    size_mb: float
    config: FileTypeConfig
    preview_supported: bool
    thumbnail_required: bool
    extract_text: bool


@dataclass
class StoredUploadResult:
    path: str
    url: str
    filename: str
    mime_type: str
    size_bytes: int
    category: str


class UploadHandler:
    def __init__(
        self,
        module: str,
        allowed_categories: list[str] | None = None,
        extra_validators: list[Callable] | None = None,
    ):
        self.module = module
        self.allowed_categories = allowed_categories
        self.core_validator = FileValidator(module, allowed_categories)
        self.extra_validators = extra_validators or []

    def handle(self, file: UploadedFile) -> UploadResult:
        self.core_validator(file)

        for validator in self.extra_validators:
            validator(file)

        _, ext = os.path.splitext(file.name.lower())
        if file.name.lower().endswith(".tar.gz"):
            ext = ".tar.gz"

        config = FILE_TYPE_REGISTRY[ext]
        size_bytes = file.size
        return UploadResult(
            file=file,
            original_name=file.name,
            extension=ext,
            category=config.category,
            mime_types=config.mime_types,
            size_bytes=size_bytes,
            size_mb=round(size_bytes / (1024 * 1024), 2),
            config=config,
            preview_supported=config.preview_supported,
            thumbnail_required=config.thumbnail_required,
            extract_text=config.extract_text,
        )

    @classmethod
    def as_validator(cls, module: str, allowed_categories: list[str] | None = None):
        return cls(module, allowed_categories).core_validator

    def get_type_info(self, filename: str) -> Optional[FileTypeConfig]:
        _, ext = os.path.splitext(filename.lower())
        return FILE_TYPE_REGISTRY.get(ext)

    def save_stored(self, file: UploadedFile) -> StoredUploadResult:
        """Validate, then persist under MEDIA_ROOT using a dated generic path."""
        meta = self.handle(file)
        file.seek(0)
        relative = upload_generic(self.module, file.name)
        stored_name = default_storage.save(relative, file)
        url = default_storage.url(stored_name)
        file.seek(0)
        mime_type = magic.from_buffer(file.read(2048), mime=True)
        file.seek(0)
        return StoredUploadResult(
            path=stored_name,
            url=url,
            filename=file.name,
            mime_type=mime_type,
            size_bytes=meta.size_bytes,
            category=meta.category,
        )
