import uuid

from fastapi import UploadFile, HTTPException
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.config import settings
from app.models.ressource import Ressource
from app.models.utilisateur import Utilisateur
from app.schemas.ressource import RessourceCreate, RessourceUpdate
from app.services.fichier_service import FichierService


class RessourceService:
    def __init__(self, db: AsyncSession) -> None:
        self.db = db
        self._fichier_service = FichierService()

    async def upload_and_create(
        self,
        file: UploadFile,
        current_user: Utilisateur,
        titre: str | None = None,
        description: str | None = None,
        categorie: str | None = None,
    ) -> dict:
        content = await file.read()
        if len(content) > settings.resource_max_file_size_mb * 1024 * 1024:
            raise HTTPException(
                status_code=413,
                detail=f"Fichier trop volumineux (max {settings.resource_max_file_size_mb} Mo)",
            )

        mime_type = file.content_type or "application/octet-stream"
        if mime_type not in settings.resource_allowed_mime_types:
            raise HTTPException(status_code=415, detail="Type de fichier non autorise")

        filename = file.filename or "resource"
        upload_payload = await self._fichier_service.upload(
            content=content,
            filename=filename,
            content_type=mime_type,
        )

        normalized_title = titre.strip() if titre else filename
        resource = Ressource(
            titre=normalized_title,
            description=description,
            categorie=categorie,
            bucket_id="local",
            file_id=upload_payload["fileId"],
            file_name=upload_payload["nom"],
            mime_type=upload_payload["type"],
            size_bytes=upload_payload["taille"],
            public_url=upload_payload["url"],
            download_url=upload_payload["url"],
            auteur_id=current_user.id,
        )
        self.db.add(resource)
        await self.db.commit()
        await self.db.refresh(resource)

        return {
            "resourceId": str(resource.id),
            "fileId": resource.file_id,
            "bucketId": resource.bucket_id,
            "fileName": resource.file_name,
            "mimeType": resource.mime_type,
            "sizeBytes": resource.size_bytes,
            "publicUrl": resource.public_url,
            "downloadUrl": resource.download_url or resource.public_url,
            "createdAt": resource.date_creation,
        }

    async def create_metadata(self, data: RessourceCreate, current_user: Utilisateur) -> dict:
        existing = (
            await self.db.execute(
                select(Ressource).where(Ressource.file_id == data.fileId, Ressource.est_supprime == False)
            )
        ).scalar_one_or_none()
        if existing is not None:
            raise HTTPException(status_code=409, detail="Cette ressource existe deja")

        resource = Ressource(
            titre=data.titre,
            description=data.description,
            categorie=data.categorie,
            bucket_id="local",
            file_id=data.fileId,
            file_name=data.fileName or data.fileId,
            mime_type=data.mimeType or "application/octet-stream",
            size_bytes=data.sizeBytes or 0,
            public_url=data.publicUrl,
            download_url=data.downloadUrl,
            auteur_id=current_user.id,
        )
        self.db.add(resource)
        await self.db.commit()
        await self.db.refresh(resource)
        return self._serialize(resource)

    async def list_ressources(
        self,
        page: int,
        size: int,
        categorie: str | None = None,
        search: str | None = None,
        sort: str | None = None,
        order: str | None = None,
    ) -> tuple[list[dict], int]:
        filters = [Ressource.est_supprime == False]

        if categorie:
            filters.append(Ressource.categorie == categorie)
        if search:
            like = f"%{search}%"
            filters.append(Ressource.titre.ilike(like))

        query = select(Ressource).where(*filters)

        sort_key = (sort or "dateCreation").lower()
        sort_column = Ressource.date_creation
        if sort_key == "titre":
            sort_column = Ressource.titre
        elif sort_key == "categorie":
            sort_column = Ressource.categorie

        if (order or "desc").lower() == "asc":
            query = query.order_by(sort_column.asc())
        else:
            query = query.order_by(sort_column.desc())

        total = (await self.db.execute(select(func.count(Ressource.id)).where(*filters))).scalar_one()
        rows = (await self.db.execute(query.offset(page * size).limit(size))).scalars().all()
        return [self._serialize(r) for r in rows], total

    async def get_ressource(self, resource_id: str | uuid.UUID) -> dict:
        resource = await self._get_or_404(resource_id)
        return self._serialize(resource)

    async def delete_ressource(self, resource_id: str | uuid.UUID, current_user: Utilisateur) -> dict:
        resource = await self._get_or_404(resource_id)
        if str(resource.auteur_id) != str(current_user.id) and current_user.type_utilisateur not in (
            "GESTIONNAIRE",
            "ADMIN_SYSTEME",
        ):
            raise HTTPException(status_code=403, detail="Permission insuffisante")

        await self._fichier_service.delete(resource.file_id)

        resource.est_supprime = True
        await self.db.commit()
        return {"id": str(resource.id), "deleted": True}

    async def update_ressource(
        self,
        resource_id: str | uuid.UUID,
        data: RessourceUpdate,
        current_user: Utilisateur,
    ) -> dict:
        resource = await self._get_or_404(resource_id)
        if str(resource.auteur_id) != str(current_user.id) and current_user.type_utilisateur not in (
            "GESTIONNAIRE",
            "ADMIN_SYSTEME",
        ):
            raise HTTPException(status_code=403, detail="Permission insuffisante")

        if data.titre is not None:
            resource.titre = data.titre
        if data.description is not None:
            resource.description = data.description
        if data.categorie is not None:
            resource.categorie = data.categorie

        if data.publicUrl is not None:
            resource.public_url = data.publicUrl
        if data.downloadUrl is not None:
            resource.download_url = data.downloadUrl

        await self.db.commit()
        await self.db.refresh(resource)
        return self._serialize(resource)

    async def _get_or_404(self, resource_id: str | uuid.UUID) -> Ressource:
        resource_uuid = resource_id if isinstance(resource_id, uuid.UUID) else uuid.UUID(resource_id)
        resource = (
            await self.db.execute(
                select(Ressource).where(Ressource.id == resource_uuid, Ressource.est_supprime == False)
            )
        ).scalar_one_or_none()
        if resource is None:
            raise HTTPException(status_code=404, detail="Ressource non trouvee")
        return resource

    @staticmethod
    def _serialize(resource: Ressource) -> dict:
        return {
            "id": str(resource.id),
            "titre": resource.titre,
            "description": resource.description,
            "categorie": resource.categorie,
            "bucketId": resource.bucket_id,
            "fileId": resource.file_id,
            "fileName": resource.file_name,
            "mimeType": resource.mime_type,
            "sizeBytes": resource.size_bytes,
            "publicUrl": resource.public_url,
            "downloadUrl": resource.download_url,
            "auteurId": str(resource.auteur_id) if resource.auteur_id else None,
            "dateCreation": resource.date_creation,
        }
