"""DMS Bridge — integrates with the DMS plugin for file storage."""

from __future__ import annotations

import logging
import os
import uuid
from typing import Any

from fastapi import UploadFile
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.plugins.builtins.dms.models import File as DmsFile, Folder

logger = logging.getLogger(__name__)

DMS_STORAGE_BASE = os.environ.get("DMS_STORAGE_BASE", "/tmp/dms")
COMM_FOLDER_NAME = "_kommunikation"
MAX_DIRECT_UPLOAD = 100 * 1024 * 1024  # 100 MB — larger files must be DMS references


class DmsBridge:
    """Bridge to the DMS plugin for attachment storage."""

    @staticmethod
    async def ensure_comm_folder(
        db: AsyncSession,
        tenant_id: uuid.UUID,
        user_id: uuid.UUID,
    ) -> Folder:
        """Ensure the _kommunikation root folder exists in DMS."""
        result = await db.execute(
            select(Folder).where(
                Folder.name == COMM_FOLDER_NAME,
                Folder.parent_id.is_(None),
                Folder.tenant_id == tenant_id,
                Folder.deleted_at.is_(None),
            )
        )
        folder = result.scalar_one_or_none()
        if folder is None:
            folder = Folder(
                tenant_id=tenant_id,
                name=COMM_FOLDER_NAME,
                parent_id=None,
                created_by=user_id,
            )
            db.add(folder)
            await db.flush()
        return folder

    @staticmethod
    async def ensure_conversation_folder(
        db: AsyncSession,
        tenant_id: uuid.UUID,
        user_id: uuid.UUID,
        conversation_id: uuid.UUID,
    ) -> Folder:
        """Ensure a sub-folder for a specific conversation exists."""
        root_folder = await DmsBridge.ensure_comm_folder(db, tenant_id, user_id)
        conv_name = str(conversation_id)
        result = await db.execute(
            select(Folder).where(
                Folder.name == conv_name,
                Folder.parent_id == root_folder.id,
                Folder.tenant_id == tenant_id,
                Folder.deleted_at.is_(None),
            )
        )
        folder = result.scalar_one_or_none()
        if folder is None:
            folder = Folder(
                tenant_id=tenant_id,
                name=conv_name,
                parent_id=root_folder.id,
                created_by=user_id,
            )
            db.add(folder)
            await db.flush()
        return folder

    @staticmethod
    async def store_attachment(
        db: AsyncSession,
        tenant_id: uuid.UUID,
        conversation_id: uuid.UUID,
        user_id: uuid.UUID,
        file: UploadFile,
    ) -> dict[str, Any]:
        """Store an uploaded file in the DMS under _kommunikation/{conversation_id}/.

        Returns dict with file_id, file_name, file_type, file_size.
        """
        # Check size limit
        content = await file.read()
        file_size = len(content)

        if file_size > MAX_DIRECT_UPLOAD:
            raise ValueError(
                f"File size {file_size} exceeds direct upload limit ({MAX_DIRECT_UPLOAD} bytes). "
                f"Use DMS reference instead."
            )

        # Ensure conversation folder
        folder = await DmsBridge.ensure_conversation_folder(
            db, tenant_id, user_id, conversation_id
        )

        # Save file to disk
        file_id = uuid.uuid4()
        file_ext = os.path.splitext(file.filename or "")[1] or ""
        storage_path = os.path.join(
            DMS_STORAGE_BASE,
            str(tenant_id),
            str(folder.id),
            f"{file_id}{file_ext}",
        )
        os.makedirs(os.path.dirname(storage_path), exist_ok=True)
        with open(storage_path, "wb") as f:
            f.write(content)

        # Create DMS file record
        dms_file = DmsFile(
            tenant_id=tenant_id,
            name=file.filename or f"{file_id}",
            folder_id=folder.id,
            uploaded_by=user_id,
            mime_type=file.content_type or "application/octet-stream",
            size_bytes=file_size,
            storage_path=storage_path,
        )
        db.add(dms_file)
        await db.flush()

        return {
            "file_id": str(dms_file.id),
            "file_name": dms_file.name,
            "file_type": dms_file.mime_type,
            "file_size": dms_file.size_bytes,
            "file_source": "comm",
        }

    @staticmethod
    async def reference_external_file(
        db: AsyncSession,
        tenant_id: uuid.UUID,
        file_id: uuid.UUID,
    ) -> dict[str, Any] | None:
        """Reference an existing DMS file without copying it.

        Returns dict with file metadata or None if file not found.
        """
        result = await db.execute(
            select(DmsFile).where(
                DmsFile.id == file_id,
                DmsFile.tenant_id == tenant_id,
                DmsFile.deleted_at.is_(None),
            )
        )
        dms_file = result.scalar_one_or_none()
        if dms_file is None:
            return None

        return {
            "file_id": str(dms_file.id),
            "file_name": dms_file.name,
            "file_type": dms_file.mime_type,
            "file_size": dms_file.size_bytes,
            "file_source": "dms",
        }

    @staticmethod
    async def get_file(
        db: AsyncSession,
        tenant_id: uuid.UUID,
        file_id: uuid.UUID,
    ) -> DmsFile | None:
        """Get a DMS file by ID."""
        result = await db.execute(
            select(DmsFile).where(
                DmsFile.id == file_id,
                DmsFile.tenant_id == tenant_id,
                DmsFile.deleted_at.is_(None),
            )
        )
        return result.scalar_one_or_none()
