"""Scene preset API routes — CRUD, capture, activate, recapture."""

import uuid
from datetime import datetime, timezone

from fastapi import APIRouter, Depends, HTTPException

from wled_controller.api.auth import AuthRequired
from wled_controller.api.dependencies import (
    fire_entity_event,
    get_output_target_store,
    get_processor_manager,
    get_scene_preset_store,
)
from wled_controller.api.schemas.scene_presets import (
    ActivateResponse,
    ScenePresetCreate,
    ScenePresetListResponse,
    ScenePresetResponse,
    ScenePresetUpdate,
)
from wled_controller.core.processing.processor_manager import ProcessorManager
from wled_controller.core.scenes.scene_activator import (
    apply_scene_state,
    capture_current_snapshot,
)
from wled_controller.storage.output_target_store import OutputTargetStore
from wled_controller.storage.scene_preset import ScenePreset
from wled_controller.storage.scene_preset_store import ScenePresetStore
from wled_controller.utils import get_logger
from wled_controller.storage.base_store import EntityNotFoundError

logger = get_logger(__name__)
router = APIRouter()


def _preset_to_response(preset: ScenePreset) -> ScenePresetResponse:
    return ScenePresetResponse(
        id=preset.id,
        name=preset.name,
        description=preset.description,
        targets=[{
            "target_id": t.target_id,
            "running": t.running,
            "color_strip_source_id": t.color_strip_source_id,
            "brightness_value_source_id": t.brightness_value_source_id,
            "fps": t.fps,
        } for t in preset.targets],
        order=preset.order,
        tags=preset.tags,
        created_at=preset.created_at,
        updated_at=preset.updated_at,
    )


# ===== CRUD =====

@router.post(
    "/api/v1/scene-presets",
    response_model=ScenePresetResponse,
    tags=["Scene Presets"],
    status_code=201,
)
async def create_scene_preset(
    data: ScenePresetCreate,
    _auth: AuthRequired,
    store: ScenePresetStore = Depends(get_scene_preset_store),
    target_store: OutputTargetStore = Depends(get_output_target_store),
    manager: ProcessorManager = Depends(get_processor_manager),
):
    """Capture current state as a new scene preset."""
    target_ids = set(data.target_ids) if data.target_ids is not None else None
    targets = capture_current_snapshot(target_store, manager, target_ids)

    now = datetime.now(timezone.utc)
    preset = ScenePreset(
        id=f"scene_{uuid.uuid4().hex[:8]}",
        name=data.name,
        description=data.description,
        targets=targets,
        order=store.count(),
        tags=data.tags if data.tags is not None else [],
        created_at=now,
        updated_at=now,
    )

    try:
        preset = store.create_preset(preset)
    except EntityNotFoundError as e:
        raise HTTPException(status_code=404, detail=str(e))

    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))

    fire_entity_event("scene_preset", "created", preset.id)
    return _preset_to_response(preset)


@router.get(
    "/api/v1/scene-presets",
    response_model=ScenePresetListResponse,
    tags=["Scene Presets"],
)
async def list_scene_presets(
    _auth: AuthRequired,
    store: ScenePresetStore = Depends(get_scene_preset_store),
):
    """List all scene presets."""
    presets = store.get_all_presets()
    return ScenePresetListResponse(
        presets=[_preset_to_response(p) for p in presets],
        count=len(presets),
    )


@router.get(
    "/api/v1/scene-presets/{preset_id}",
    response_model=ScenePresetResponse,
    tags=["Scene Presets"],
)
async def get_scene_preset(
    preset_id: str,
    _auth: AuthRequired,
    store: ScenePresetStore = Depends(get_scene_preset_store),
):
    """Get a single scene preset."""
    try:
        preset = store.get_preset(preset_id)
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    return _preset_to_response(preset)


@router.put(
    "/api/v1/scene-presets/{preset_id}",
    response_model=ScenePresetResponse,
    tags=["Scene Presets"],
)
async def update_scene_preset(
    preset_id: str,
    data: ScenePresetUpdate,
    _auth: AuthRequired,
    store: ScenePresetStore = Depends(get_scene_preset_store),
    target_store: OutputTargetStore = Depends(get_output_target_store),
    manager: ProcessorManager = Depends(get_processor_manager),
):
    """Update scene preset metadata and optionally change targets."""
    # If target_ids changed, update the snapshot: keep state for existing targets,
    # capture fresh state for newly added targets, drop removed ones.
    new_targets = None
    if data.target_ids is not None:
        try:
            existing = store.get_preset(preset_id)
        except ValueError as e:
            raise HTTPException(status_code=404, detail=str(e))

        existing_map = {t.target_id: t for t in existing.targets}
        new_target_ids = set(data.target_ids)

        # Capture fresh state for newly added targets
        added_ids = new_target_ids - set(existing_map.keys())
        fresh = capture_current_snapshot(target_store, manager, added_ids) if added_ids else []
        fresh_map = {t.target_id: t for t in fresh}

        # Build new target list preserving order from target_ids
        new_targets = []
        for tid in data.target_ids:
            if tid in existing_map:
                new_targets.append(existing_map[tid])
            elif tid in fresh_map:
                new_targets.append(fresh_map[tid])

    try:
        preset = store.update_preset(
            preset_id,
            name=data.name,
            description=data.description,
            order=data.order,
            targets=new_targets,
            tags=data.tags,
        )
    except ValueError as e:
        raise HTTPException(status_code=404 if "not found" in str(e).lower() else 400, detail=str(e))
    fire_entity_event("scene_preset", "updated", preset_id)
    return _preset_to_response(preset)


@router.delete(
    "/api/v1/scene-presets/{preset_id}",
    status_code=204,
    tags=["Scene Presets"],
)
async def delete_scene_preset(
    preset_id: str,
    _auth: AuthRequired,
    store: ScenePresetStore = Depends(get_scene_preset_store),
):
    """Delete a scene preset."""
    try:
        store.delete_preset(preset_id)
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))

    fire_entity_event("scene_preset", "deleted", preset_id)


# ===== Recapture =====

@router.post(
    "/api/v1/scene-presets/{preset_id}/recapture",
    response_model=ScenePresetResponse,
    tags=["Scene Presets"],
)
async def recapture_scene_preset(
    preset_id: str,
    _auth: AuthRequired,
    store: ScenePresetStore = Depends(get_scene_preset_store),
    target_store: OutputTargetStore = Depends(get_output_target_store),
    manager: ProcessorManager = Depends(get_processor_manager),
):
    """Re-capture current state into an existing preset (updates snapshot)."""
    try:
        existing = store.get_preset(preset_id)
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))

    # Only recapture targets that are already in the preset
    existing_ids = {t.target_id for t in existing.targets}
    targets = capture_current_snapshot(target_store, manager, existing_ids)

    new_snapshot = ScenePreset(
        id=preset_id,
        name="",
        targets=targets,
    )

    try:
        preset = store.recapture_preset(preset_id, new_snapshot)
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))

    return _preset_to_response(preset)


# ===== Activate =====

@router.post(
    "/api/v1/scene-presets/{preset_id}/activate",
    response_model=ActivateResponse,
    tags=["Scene Presets"],
)
async def activate_scene_preset(
    preset_id: str,
    _auth: AuthRequired,
    store: ScenePresetStore = Depends(get_scene_preset_store),
    target_store: OutputTargetStore = Depends(get_output_target_store),
    manager: ProcessorManager = Depends(get_processor_manager),
):
    """Activate a scene preset — restore the captured state."""
    try:
        preset = store.get_preset(preset_id)
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))

    status, errors = await apply_scene_state(preset, target_store, manager)

    if not errors:
        logger.info(f"Scene preset '{preset.name}' activated successfully")

    fire_entity_event("scene_preset", "updated", preset_id)
    return ActivateResponse(status=status, errors=errors)
