"""API routes for workflow recording operations."""

from typing import Annotated, Optional

from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, UploadFile
from loguru import logger

from api.constants import DEPLOYMENT_MODE
from api.db import db_client
from api.db.workflow_recording_client import generate_short_id
from api.enums import StorageBackend
from api.schemas.workflow_recording import (
    BatchRecordingCreateRequestSchema,
    BatchRecordingCreateResponseSchema,
    BatchRecordingUploadRequestSchema,
    BatchRecordingUploadResponseSchema,
    RecordingListResponseSchema,
    RecordingResponseSchema,
    RecordingUpdateRequestSchema,
    RecordingUploadResponseSchema,
)
from api.sdk_expose import sdk_expose
from api.services.auth.depends import get_user
from api.services.mps_service_key_client import mps_service_key_client
from api.services.storage import storage_fs

router = APIRouter(prefix="/workflow-recordings", tags=["workflow-recordings"])


async def _generate_unique_recording_id(organization_id: int) -> str:
    """Generate a unique short recording ID within an organization."""
    for _ in range(10):
        rid = generate_short_id(8)
        exists = await db_client.check_recording_id_exists(rid, organization_id)
        if not exists:
            return rid
    raise HTTPException(
        status_code=500, detail="Failed to generate unique recording ID"
    )


def _build_response(rec) -> RecordingResponseSchema:
    return RecordingResponseSchema(
        id=rec.id,
        recording_id=rec.recording_id,
        workflow_id=rec.workflow_id,
        organization_id=rec.organization_id,
        tts_provider=rec.tts_provider,
        tts_model=rec.tts_model,
        tts_voice_id=rec.tts_voice_id,
        transcript=rec.transcript,
        storage_key=rec.storage_key,
        storage_backend=rec.storage_backend,
        metadata=rec.recording_metadata or {},
        created_by=rec.created_by,
        created_at=rec.created_at,
        is_active=rec.is_active,
    )


@router.post(
    "/upload-url",
    response_model=BatchRecordingUploadResponseSchema,
    summary="Get presigned URLs for recording uploads",
)
async def get_upload_urls(
    request: BatchRecordingUploadRequestSchema,
    user=Depends(get_user),
):
    """Generate presigned PUT URLs for uploading one or more audio recordings."""
    try:
        items = []
        for fd in request.files:
            recording_id = await _generate_unique_recording_id(
                user.selected_organization_id
            )

            storage_key = (
                f"recordings/{user.selected_organization_id}"
                f"/{recording_id}"
                f"/{fd.filename}"
            )

            upload_url = await storage_fs.aget_presigned_put_url(
                file_path=storage_key,
                expiration=1800,
                content_type=fd.mime_type,
                max_size=5_242_880,
            )

            if not upload_url:
                raise HTTPException(
                    status_code=500,
                    detail=f"Failed to generate presigned upload URL for {fd.filename}",
                )

            items.append(
                RecordingUploadResponseSchema(
                    upload_url=upload_url,
                    recording_id=recording_id,
                    storage_key=storage_key,
                )
            )

        logger.info(
            f"Generated {len(items)} recording upload URL(s), "
            f"org {user.selected_organization_id}"
        )

        return BatchRecordingUploadResponseSchema(items=items)

    except HTTPException:
        raise
    except Exception as exc:
        logger.error(f"Error generating recording upload URLs: {exc}")
        raise HTTPException(
            status_code=500, detail="Failed to generate upload URLs"
        ) from exc


@router.post(
    "/",
    response_model=BatchRecordingCreateResponseSchema,
    summary="Create recording records after upload",
)
async def create_recordings(
    request: BatchRecordingCreateRequestSchema,
    user=Depends(get_user),
):
    """Create one or more recording records after audio files have been uploaded."""
    try:
        backend = StorageBackend.get_current_backend()
        results = []

        for rec_req in request.recordings:
            recording = await db_client.create_recording(
                recording_id=rec_req.recording_id,
                organization_id=user.selected_organization_id,
                transcript=rec_req.transcript,
                storage_key=rec_req.storage_key,
                storage_backend=backend.value,
                created_by=user.id,
                tts_provider=rec_req.tts_provider,
                tts_model=rec_req.tts_model,
                tts_voice_id=rec_req.tts_voice_id,
                metadata=rec_req.metadata,
            )
            results.append(_build_response(recording))

        logger.info(
            f"Created {len(results)} recording(s) for org {user.selected_organization_id}"
        )

        return BatchRecordingCreateResponseSchema(recordings=results)

    except HTTPException:
        raise
    except Exception as exc:
        logger.error(f"Error creating recordings: {exc}")
        raise HTTPException(
            status_code=500, detail="Failed to create recordings"
        ) from exc


@router.get(
    "/",
    response_model=RecordingListResponseSchema,
    summary="List recordings",
    **sdk_expose(
        method="list_recordings",
        description="List workflow recordings available to the authenticated organization.",
    ),
)
async def list_recordings(
    workflow_id: Annotated[
        Optional[int], Query(description="Filter by workflow ID")
    ] = None,
    tts_provider: Annotated[
        Optional[str], Query(description="Filter by TTS provider")
    ] = None,
    tts_model: Annotated[
        Optional[str], Query(description="Filter by TTS model")
    ] = None,
    tts_voice_id: Annotated[
        Optional[str], Query(description="Filter by TTS voice ID")
    ] = None,
    user=Depends(get_user),
):
    """List recordings for the organization, optionally filtered."""
    try:
        recordings = await db_client.get_recordings(
            organization_id=user.selected_organization_id,
            workflow_id=workflow_id,
            tts_provider=tts_provider,
            tts_model=tts_model,
            tts_voice_id=tts_voice_id,
        )

        return RecordingListResponseSchema(
            recordings=[_build_response(r) for r in recordings],
            total=len(recordings),
        )

    except Exception as exc:
        logger.error(f"Error listing recordings: {exc}")
        raise HTTPException(
            status_code=500, detail="Failed to list recordings"
        ) from exc


@router.delete(
    "/{recording_id}",
    summary="Delete a recording",
)
async def delete_recording(
    recording_id: str,
    user=Depends(get_user),
):
    """Soft delete a recording."""
    try:
        success = await db_client.delete_recording(
            recording_id=recording_id,
            organization_id=user.selected_organization_id,
        )

        if not success:
            raise HTTPException(status_code=404, detail="Recording not found")

        logger.info(
            f"Deleted recording {recording_id}, org {user.selected_organization_id}"
        )

        return {"success": True, "message": "Recording deleted successfully"}

    except HTTPException:
        raise
    except Exception as exc:
        logger.error(f"Error deleting recording: {exc}")
        raise HTTPException(
            status_code=500, detail="Failed to delete recording"
        ) from exc


@router.patch(
    "/{id}",
    response_model=RecordingResponseSchema,
    summary="Update a recording's Recording ID",
)
async def update_recording(
    id: int,
    request: RecordingUpdateRequestSchema,
    user=Depends(get_user),
):
    """Update the recording_id (descriptive name) of a recording."""
    try:
        new_id = request.recording_id.strip()
        if not new_id:
            raise HTTPException(status_code=400, detail="Recording ID cannot be empty")

        existing = await db_client.get_recording_by_id(
            id, user.selected_organization_id
        )
        if not existing:
            raise HTTPException(status_code=404, detail="Recording not found")

        if new_id == existing.recording_id:
            return _build_response(existing)

        exists = await db_client.check_recording_id_exists(
            new_id, user.selected_organization_id
        )
        if exists:
            raise HTTPException(
                status_code=409,
                detail=f"Recording ID '{new_id}' is already in use",
            )

        old_id = existing.recording_id

        recording = await db_client.update_recording_id(
            id=id,
            new_recording_id=new_id,
            organization_id=user.selected_organization_id,
        )

        if not recording:
            raise HTTPException(status_code=404, detail="Recording not found")

        # Replace old recording ID in all non-legacy workflow definitions
        updated = await db_client.replace_recording_id_in_workflows(
            old_id=old_id,
            new_id=new_id,
            organization_id=user.selected_organization_id,
        )
        if updated:
            logger.info(
                f"Updated {updated} workflow definition(s) with new recording ID "
                f"'{old_id}' -> '{new_id}'"
            )

        return _build_response(recording)

    except HTTPException:
        raise
    except Exception as exc:
        logger.error(f"Error updating recording: {exc}")
        raise HTTPException(
            status_code=500, detail="Failed to update recording"
        ) from exc


@router.post(
    "/transcribe",
    summary="Transcribe an audio file",
)
async def transcribe_audio(
    file: UploadFile = File(...),
    language: str = Form("en"),
    user=Depends(get_user),
):
    """Transcribe an uploaded audio file using MPS STT."""
    try:
        audio_data = await file.read()

        if DEPLOYMENT_MODE == "oss":
            result = await mps_service_key_client.transcribe_audio(
                audio_data=audio_data,
                filename=file.filename or "audio.wav",
                content_type=file.content_type or "audio/wav",
                language=language,
                created_by=str(user.provider_id),
            )
        else:
            result = await mps_service_key_client.transcribe_audio(
                audio_data=audio_data,
                filename=file.filename or "audio.wav",
                content_type=file.content_type or "audio/wav",
                language=language,
                organization_id=user.selected_organization_id,
            )

        return result

    except Exception as exc:
        logger.error(f"Error transcribing audio: {exc}")
        raise HTTPException(
            status_code=500, detail="Failed to transcribe audio"
        ) from exc
