"""Upload and download prompt media (images, voice, files) for the builder."""
from __future__ import annotations
import os
from typing import Optional
from uuid import UUID, uuid4

import structlog
from fastapi import APIRouter, Depends, File, Form, Path, UploadFile
from fastapi.responses import FileResponse
from sqlalchemy.ext.asyncio import AsyncSession

from src.config import get_settings
from src.core.database import get_db
from src.core.dependencies import require_active_billing_for_project_id
from src.core.exceptions import NotFoundError, ValidationError
from src.domain.auth.models import User
from src.domain.auth.router import get_current_user
from src.domain.instructions.repository import PromptSessionRepository
from src.domain.projects.repository import ProjectRepository
from src.domain.prompt_media.paths import build_storage_relative_path
from src.domain.prompt_media.repository import PromptMediaRepository
from src.domain.prompt_media.schemas import PromptMediaUploadResponse
from src.domain.prompt_media.validate import (
    assert_allowed_mime,
    assert_file_size,
    kind_from_mime,
)

logger = structlog.get_logger()

router = APIRouter(
    prefix="/api/projects/{project_id}/prompt-media",
    tags=["prompt-media"],
)


def _safe_filename(name: str) -> str:
    import re

    base = os.path.basename(name or "") or "upload.bin"
    base = re.sub(r"[^a-zA-Z0-9._-]+", "_", base).strip("._")[:180]
    return base or "upload.bin"


@router.post("/upload", response_model=PromptMediaUploadResponse)
async def upload_prompt_media(
    project_id: UUID = Depends(require_active_billing_for_project_id),
    file: UploadFile = File(...),
    transcript: Optional[str] = Form(None),
    session_id: Optional[str] = Form(
        None,
        description="Optional prompt session (chat) id — adds a third path segment under tenant/project.",
    ),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Store a file under ``<root>/<workspace_id>/<project_id>/[session_id]/`` and return metadata."""
    settings = get_settings()
    proj = await ProjectRepository(db).get_by_id(project_id)
    if not proj:
        raise NotFoundError("Project not found")
    workspace_id = proj.workspace_id

    chat_id: Optional[UUID] = None
    if session_id and str(session_id).strip():
        try:
            chat_id = UUID(str(session_id).strip())
        except ValueError as e:
            raise ValidationError("Invalid session_id") from e
        sess = await PromptSessionRepository(db).get_by_id(chat_id)
        if not sess or sess.project_id != project_id:
            raise ValidationError("Session not found for this project")

    content = await file.read()
    assert_file_size(len(content))
    mime = file.content_type or "application/octet-stream"
    assert_allowed_mime(mime, file.filename)
    kind = kind_from_mime(mime)

    asset_id = uuid4()
    storage_fn = f"{asset_id}_{_safe_filename(file.filename or 'upload.bin')}"
    rel = build_storage_relative_path(
        workspace_id,
        project_id,
        storage_fn,
        session_id=chat_id,
    )
    abs_path = os.path.join(
        os.path.normpath(settings.prompt_media_upload_root),
        rel.replace("/", os.sep),
    )
    abs_dir = os.path.dirname(abs_path)
    os.makedirs(abs_dir, exist_ok=True)

    def _write() -> None:
        with open(abs_path, "wb") as out:
            out.write(content)

    import asyncio

    await asyncio.to_thread(_write)

    repo = PromptMediaRepository(db)
    row = await repo.create(
        project_id=project_id,
        uploaded_by=user.id,
        kind=kind,
        storage_relative_path=rel.replace("\\", "/"),
        original_filename=file.filename or "upload.bin",
        mime_type=mime,
        byte_size=len(content),
        transcript=transcript.strip() if transcript and transcript.strip() else None,
        asset_id=asset_id,
    )
    await db.commit()
    logger.info(
        "prompt_media_uploaded",
        asset_id=str(row.id),
        project_id=str(project_id),
        workspace_id=str(workspace_id),
        session_id=str(chat_id) if chat_id else None,
        kind=kind,
        bytes=len(content),
    )
    return PromptMediaUploadResponse.model_validate(row)


@router.get("/{asset_id}/file")
async def download_prompt_media_file(
    project_id: UUID = Depends(require_active_billing_for_project_id),
    asset_id: UUID = Path(...),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Return the raw file (for dashboard preview)."""
    settings = get_settings()
    row = await PromptMediaRepository(db).get_by_id(asset_id)
    if not row or row.project_id != project_id:
        raise NotFoundError("Attachment not found")
    abs_path = os.path.join(
        os.path.normpath(settings.prompt_media_upload_root),
        row.storage_relative_path.replace("/", os.sep),
    )
    if not os.path.isfile(abs_path):
        raise NotFoundError("File missing on server")
    return FileResponse(
        abs_path,
        media_type=row.mime_type,
        filename=row.original_filename,
    )
