import uuid
from pathlib import Path

from fastapi import UploadFile

from app.core.config import settings
from app.core.exceptions import ValidationError

ALLOWED_EXTENSIONS = {".jpg", ".jpeg", ".png", ".webp"}
ALLOWED_CONTENT_TYPES = {"image/jpeg", "image/png", "image/webp"}
FOLDERS = {"products", "receipts", "cards", "temp"}
CHUNK = 1024 * 1024


class UploadService:
    """Validated image uploads stored under uploads/<folder>/."""

    def __init__(self, base_path: Path | None = None) -> None:
        self.base_path = base_path or settings.upload_dir

    async def save_image(self, file: UploadFile, folder: str = "temp") -> dict[str, str]:
        if folder not in FOLDERS:
            raise ValidationError("invalid_upload_folder")

        extension = Path(file.filename or "").suffix.lower()
        if extension not in ALLOWED_EXTENSIONS:
            raise ValidationError("invalid_file_type")
        if file.content_type not in ALLOWED_CONTENT_TYPES:
            raise ValidationError("invalid_file_type")

        target_dir = self.base_path / folder
        target_dir.mkdir(parents=True, exist_ok=True)
        name = f"{uuid.uuid4().hex}{extension}"
        path = target_dir / name

        limit = settings.max_upload_mb * 1024 * 1024
        size = 0
        with path.open("wb") as handle:
            while chunk := await file.read(CHUNK):
                size += len(chunk)
                if size > limit:
                    handle.close()
                    path.unlink(missing_ok=True)
                    raise ValidationError("file_too_large")
                handle.write(chunk)

        if size == 0:
            path.unlink(missing_ok=True)
            raise ValidationError("empty_file")

        relative = f"{folder}/{name}"
        return {
            "path": relative,
            "url": f"{settings.upload_url_prefix}/{relative}",
            "size": str(size),
        }

    def delete(self, relative_path: str) -> bool:
        target = (self.base_path / relative_path).resolve()
        if not str(target).startswith(str(self.base_path.resolve())):
            raise ValidationError("invalid_path")
        if target.is_file():
            target.unlink()
            return True
        return False

    def list_files(self, folder: str = "products") -> list[dict[str, str]]:
        if folder not in FOLDERS:
            raise ValidationError("invalid_upload_folder")
        target_dir = self.base_path / folder
        if not target_dir.is_dir():
            return []
        return [
            {
                "path": f"{folder}/{item.name}",
                "url": f"{settings.upload_url_prefix}/{folder}/{item.name}",
                "size": str(item.stat().st_size),
            }
            for item in sorted(target_dir.iterdir(), key=lambda p: p.stat().st_mtime, reverse=True)
            if item.is_file()
        ]
