"""Audio capture template and engine routes."""

import asyncio
from fastapi import APIRouter, HTTPException, Depends, Query
from starlette.websockets import WebSocket, WebSocketDisconnect

from wled_controller.api.auth import AuthRequired
from wled_controller.api.dependencies import fire_entity_event, get_audio_template_store, get_audio_source_store, get_processor_manager
from wled_controller.api.schemas.audio_templates import (
    AudioEngineInfo,
    AudioEngineListResponse,
    AudioTemplateCreate,
    AudioTemplateListResponse,
    AudioTemplateResponse,
    AudioTemplateUpdate,
)
from wled_controller.core.audio.factory import AudioEngineRegistry
from wled_controller.storage.audio_template_store import AudioTemplateStore
from wled_controller.storage.audio_source_store import AudioSourceStore
from wled_controller.utils import get_logger
from wled_controller.storage.base_store import EntityNotFoundError

logger = get_logger(__name__)

router = APIRouter()


# ===== AUDIO TEMPLATE ENDPOINTS =====

@router.get("/api/v1/audio-templates", response_model=AudioTemplateListResponse, tags=["Audio Templates"])
async def list_audio_templates(
    _auth: AuthRequired,
    store: AudioTemplateStore = Depends(get_audio_template_store),
):
    """List all audio capture templates."""
    try:
        templates = store.get_all_templates()
        responses = [
            AudioTemplateResponse(
                id=t.id, name=t.name, engine_type=t.engine_type,
                engine_config=t.engine_config, tags=t.tags,
                created_at=t.created_at,
                updated_at=t.updated_at, description=t.description,
            )
            for t in templates
        ]
        return AudioTemplateListResponse(templates=responses, count=len(responses))
    except Exception as e:
        logger.error("Failed to list audio templates: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Internal server error")


@router.post("/api/v1/audio-templates", response_model=AudioTemplateResponse, tags=["Audio Templates"], status_code=201)
async def create_audio_template(
    data: AudioTemplateCreate,
    _auth: AuthRequired,
    store: AudioTemplateStore = Depends(get_audio_template_store),
):
    """Create a new audio capture template."""
    try:
        template = store.create_template(
            name=data.name, engine_type=data.engine_type,
            engine_config=data.engine_config, description=data.description,
            tags=data.tags,
        )
        fire_entity_event("audio_template", "created", template.id)
        return AudioTemplateResponse(
            id=template.id, name=template.name, engine_type=template.engine_type,
            engine_config=template.engine_config, tags=template.tags,
            created_at=template.created_at,
            updated_at=template.updated_at, description=template.description,
        )
    except EntityNotFoundError as e:
        raise HTTPException(status_code=404, detail=str(e))

    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error("Failed to create audio template: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Internal server error")


@router.get("/api/v1/audio-templates/{template_id}", response_model=AudioTemplateResponse, tags=["Audio Templates"])
async def get_audio_template(
    template_id: str,
    _auth: AuthRequired,
    store: AudioTemplateStore = Depends(get_audio_template_store),
):
    """Get audio template by ID."""
    try:
        t = store.get_template(template_id)
    except ValueError:
        raise HTTPException(status_code=404, detail=f"Audio template {template_id} not found")
    return AudioTemplateResponse(
        id=t.id, name=t.name, engine_type=t.engine_type,
        engine_config=t.engine_config, tags=t.tags,
        created_at=t.created_at,
        updated_at=t.updated_at, description=t.description,
    )


@router.put("/api/v1/audio-templates/{template_id}", response_model=AudioTemplateResponse, tags=["Audio Templates"])
async def update_audio_template(
    template_id: str,
    data: AudioTemplateUpdate,
    _auth: AuthRequired,
    store: AudioTemplateStore = Depends(get_audio_template_store),
):
    """Update an audio template."""
    try:
        t = store.update_template(
            template_id=template_id, name=data.name,
            engine_type=data.engine_type, engine_config=data.engine_config,
            description=data.description, tags=data.tags,
        )
        fire_entity_event("audio_template", "updated", template_id)
        return AudioTemplateResponse(
            id=t.id, name=t.name, engine_type=t.engine_type,
            engine_config=t.engine_config, tags=t.tags,
            created_at=t.created_at,
            updated_at=t.updated_at, description=t.description,
        )
    except EntityNotFoundError as e:
        raise HTTPException(status_code=404, detail=str(e))

    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error("Failed to update audio template: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Internal server error")


@router.delete("/api/v1/audio-templates/{template_id}", status_code=204, tags=["Audio Templates"])
async def delete_audio_template(
    template_id: str,
    _auth: AuthRequired,
    store: AudioTemplateStore = Depends(get_audio_template_store),
    audio_source_store: AudioSourceStore = Depends(get_audio_source_store),
):
    """Delete an audio template."""
    try:
        store.delete_template(template_id, audio_source_store=audio_source_store)
        fire_entity_event("audio_template", "deleted", template_id)
    except HTTPException:
        raise
    except EntityNotFoundError as e:
        raise HTTPException(status_code=404, detail=str(e))

    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error("Failed to delete audio template: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Internal server error")


# ===== AUDIO ENGINE ENDPOINTS =====

@router.get("/api/v1/audio-engines", response_model=AudioEngineListResponse, tags=["Audio Templates"])
async def list_audio_engines(_auth: AuthRequired):
    """List all registered audio capture engines."""
    try:
        available_set = set(AudioEngineRegistry.get_available_engines())
        all_engines = AudioEngineRegistry.get_all_engines()

        engines = []
        for engine_type, engine_class in all_engines.items():
            engines.append(
                AudioEngineInfo(
                    type=engine_type,
                    name=engine_type.upper(),
                    default_config=engine_class.get_default_config(),
                    available=(engine_type in available_set),
                )
            )

        return AudioEngineListResponse(engines=engines, count=len(engines))
    except Exception as e:
        logger.error("Failed to list audio engines: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Internal server error")


# ===== REAL-TIME AUDIO TEMPLATE TEST WEBSOCKET =====


@router.websocket("/api/v1/audio-templates/{template_id}/test/ws")
async def test_audio_template_ws(
    websocket: WebSocket,
    template_id: str,
    token: str = Query(""),
    device_index: int = Query(-1),
    is_loopback: int = Query(1),
):
    """WebSocket for real-time audio spectrum test of a template with a chosen device.

    Auth via ?token=<api_key>. Device specified via ?device_index=N&is_loopback=0|1.
    Streams AudioAnalysis snapshots as JSON at ~20 Hz.
    """
    from wled_controller.api.auth import verify_ws_token
    if not verify_ws_token(token):
        await websocket.close(code=4001, reason="Unauthorized")
        return

    # Resolve template
    store = get_audio_template_store()
    try:
        template = store.get_template(template_id)
    except ValueError:
        await websocket.close(code=4004, reason="Template not found")
        return

    # Acquire shared audio stream
    manager = get_processor_manager()
    audio_mgr = manager.audio_capture_manager
    loopback = is_loopback != 0

    try:
        stream = audio_mgr.acquire(device_index, loopback, template.engine_type, template.engine_config)
    except RuntimeError as e:
        await websocket.close(code=4003, reason=str(e))
        return

    await websocket.accept()
    logger.info(f"Audio template test WS connected: template={template_id} device={device_index} loopback={loopback}")

    last_ts = 0.0
    try:
        while True:
            analysis = stream.get_latest_analysis()
            if analysis is not None and analysis.timestamp != last_ts:
                last_ts = analysis.timestamp
                await websocket.send_json({
                    "spectrum": analysis.spectrum.tolist(),
                    "rms": round(analysis.rms, 4),
                    "peak": round(analysis.peak, 4),
                    "beat": analysis.beat,
                    "beat_intensity": round(analysis.beat_intensity, 4),
                })
            await asyncio.sleep(0.05)
    except WebSocketDisconnect:
        logger.debug("Audio template test WebSocket disconnected")
        pass
    except Exception as e:
        logger.error(f"Audio template test WS error: {e}")
    finally:
        audio_mgr.release(device_index, loopback, template.engine_type)
        logger.info(f"Audio template test WS disconnected: template={template_id}")
