"""Attachment service — upload, list, download, delete with file storage."""

from __future__ import annotations

import os
import uuid
from datetime import UTC, datetime
from typing import Any

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

from app.core.audit import log_audit
from app.core.storage import get_storage_backend
from app.models.attachment import Attachment


def _attachment_to_dict(a: Attachment) -> dict[str, Any]:
    """Serialize an Attachment ORM object to dict."""
    return {
        "id": str(a.id),
        "entity_type": a.entity_type,
        "entity_id": str(a.entity_id),
        "filename": a.filename,
        "file_path": a.file_path,
        "mime_type": a.mime_type,
        "file_size": a.file_size,
        "uploaded_by": str(a.uploaded_by) if a.uploaded_by else None,
        "created_at": a.created_at.isoformat() if a.created_at else None,
        "updated_at": a.updated_at.isoformat() if a.updated_at else None,
    }


def _generate_unique_filename(original_filename: str) -> str:
    """Generate a unique filename using UUID + original extension."""
    ext = os.path.splitext(original_filename)[1]
    return f"{uuid.uuid4().hex}{ext}"


async def save_attachment(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    user_id: uuid.UUID,
    entity_type: str,
    entity_id: uuid.UUID,
    filename: str,
    file_content: bytes,
    mime_type: str,
) -> dict[str, Any]:
    """Save a file to storage and create an Attachment record."""
    # Generate unique filename and relative storage path
    unique_filename = _generate_unique_filename(filename)
    file_path = f"{entity_type}/{entity_id}/{unique_filename}"

    # Save file via storage backend
    storage = get_storage_backend()
    await storage.save(file_path, file_content)

    file_size = len(file_content)

    # Create DB record
    attachment = Attachment(
        tenant_id=tenant_id,
        entity_type=entity_type,
        entity_id=entity_id,
        filename=filename,
        file_path=file_path,
        mime_type=mime_type,
        file_size=file_size,
        uploaded_by=user_id,
    )
    db.add(attachment)
    await db.flush()
    await db.refresh(attachment)
    await log_audit(
        db, tenant_id, user_id, "upload", "attachment", attachment.id,
        changes={"filename": filename, "entity_type": entity_type, "entity_id": str(entity_id)},
    )
    return _attachment_to_dict(attachment)


async def list_attachments(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    entity_type: str,
    entity_id: uuid.UUID,
) -> dict[str, Any]:
    """List attachments for a specific entity."""
    q = select(Attachment).where(
        Attachment.tenant_id == tenant_id,
        Attachment.entity_type == entity_type,
        Attachment.entity_id == entity_id,
        Attachment.deleted_at.is_(None),
    ).order_by(Attachment.created_at.desc())
    result = await db.execute(q)
    attachments = result.scalars().all()
    return {
        "items": [_attachment_to_dict(a) for a in attachments],
        "total": len(attachments),
    }


async def get_attachment(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    attachment_id: uuid.UUID,
) -> dict[str, Any] | None:
    """Get a single attachment by ID."""
    q = select(Attachment).where(
        Attachment.id == attachment_id,
        Attachment.tenant_id == tenant_id,
        Attachment.deleted_at.is_(None),
    )
    result = await db.execute(q)
    attachment = result.scalar_one_or_none()
    if attachment is None:
        return None
    return _attachment_to_dict(attachment)


async def delete_attachment(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    user_id: uuid.UUID,
    attachment_id: uuid.UUID,
) -> bool:
    """Soft-delete an attachment (keeps file on disk for audit trail)."""
    q = select(Attachment).where(
        Attachment.id == attachment_id,
        Attachment.tenant_id == tenant_id,
        Attachment.deleted_at.is_(None),
    )
    result = await db.execute(q)
    attachment = result.scalar_one_or_none()
    if attachment is None:
        return False

    attachment.deleted_at = datetime.now(UTC)
    await db.flush()
    await log_audit(
        db, tenant_id, user_id, "delete", "attachment", attachment_id,
        changes={"filename": attachment.filename},
    )
    return True
