"""Output target routes: processing control, state, metrics, events, overlay.

Extracted from output_targets.py to keep files under 800 lines.
"""


from fastapi import APIRouter, HTTPException, Depends, Query, WebSocket, WebSocketDisconnect

from wled_controller.api.auth import AuthRequired
from wled_controller.api.dependencies import (
    get_color_strip_store,
    get_output_target_store,
    get_picture_source_store,
    get_processor_manager,
)
from wled_controller.api.schemas.output_targets import (
    BulkTargetRequest,
    BulkTargetResponse,
    TargetMetricsResponse,
    TargetProcessingState,
)
from wled_controller.core.processing.processor_manager import ProcessorManager
from wled_controller.core.capture.screen_capture import get_available_displays
from wled_controller.storage.color_strip_store import ColorStripStore
from wled_controller.storage.color_strip_source import AdvancedPictureColorStripSource, PictureColorStripSource
from wled_controller.storage.picture_source_store import PictureSourceStore
from wled_controller.storage.wled_output_target import WledOutputTarget
from wled_controller.storage.output_target_store import OutputTargetStore
from wled_controller.utils import get_logger

logger = get_logger(__name__)

router = APIRouter()


# ===== BULK PROCESSING CONTROL ENDPOINTS =====

@router.post("/api/v1/output-targets/bulk/start", response_model=BulkTargetResponse, tags=["Processing"])
async def bulk_start_processing(
    body: BulkTargetRequest,
    _auth: AuthRequired,
    target_store: OutputTargetStore = Depends(get_output_target_store),
    manager: ProcessorManager = Depends(get_processor_manager),
):
    """Start processing for multiple output targets. Returns lists of started IDs and per-ID errors."""
    started: list[str] = []
    errors: dict[str, str] = {}

    for target_id in body.ids:
        try:
            target_store.get_target(target_id)
            await manager.start_processing(target_id)
            started.append(target_id)
            logger.info(f"Bulk start: started processing for target {target_id}")
        except ValueError as e:
            errors[target_id] = str(e)
        except RuntimeError as e:
            msg = str(e)
            for t in target_store.get_all_targets():
                if t.id in msg:
                    msg = msg.replace(t.id, f"'{t.name}'")
            errors[target_id] = msg
        except Exception as e:
            logger.error(f"Bulk start: failed to start target {target_id}: {e}")
            errors[target_id] = str(e)

    return BulkTargetResponse(started=started, errors=errors)


@router.post("/api/v1/output-targets/bulk/stop", response_model=BulkTargetResponse, tags=["Processing"])
async def bulk_stop_processing(
    body: BulkTargetRequest,
    _auth: AuthRequired,
    manager: ProcessorManager = Depends(get_processor_manager),
):
    """Stop processing for multiple output targets. Returns lists of stopped IDs and per-ID errors."""
    stopped: list[str] = []
    errors: dict[str, str] = {}

    for target_id in body.ids:
        try:
            await manager.stop_processing(target_id)
            stopped.append(target_id)
            logger.info(f"Bulk stop: stopped processing for target {target_id}")
        except ValueError as e:
            errors[target_id] = str(e)
        except Exception as e:
            logger.error(f"Bulk stop: failed to stop target {target_id}: {e}")
            errors[target_id] = str(e)

    return BulkTargetResponse(stopped=stopped, errors=errors)


# ===== PROCESSING CONTROL ENDPOINTS =====

@router.post("/api/v1/output-targets/{target_id}/start", tags=["Processing"])
async def start_processing(
    target_id: str,
    _auth: AuthRequired,
    target_store: OutputTargetStore = Depends(get_output_target_store),
    manager: ProcessorManager = Depends(get_processor_manager),
):
    """Start processing for a output target."""
    try:
        # Verify target exists in store
        target_store.get_target(target_id)

        await manager.start_processing(target_id)

        logger.info(f"Started processing for target {target_id}")
        return {"status": "started", "target_id": target_id}

    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    except RuntimeError as e:
        # Resolve target IDs to human-readable names in error messages
        msg = str(e)
        for t in target_store.get_all_targets():
            if t.id in msg:
                msg = msg.replace(t.id, f"'{t.name}'")
        raise HTTPException(status_code=409, detail=msg)
    except Exception as e:
        logger.error("Failed to start processing: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Internal server error")


@router.post("/api/v1/output-targets/{target_id}/stop", tags=["Processing"])
async def stop_processing(
    target_id: str,
    _auth: AuthRequired,
    manager: ProcessorManager = Depends(get_processor_manager),
):
    """Stop processing for a output target."""
    try:
        await manager.stop_processing(target_id)

        logger.info(f"Stopped processing for target {target_id}")
        return {"status": "stopped", "target_id": target_id}

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


# ===== STATE & METRICS ENDPOINTS =====

@router.get("/api/v1/output-targets/{target_id}/state", response_model=TargetProcessingState, tags=["Processing"])
async def get_target_state(
    target_id: str,
    _auth: AuthRequired,
    manager: ProcessorManager = Depends(get_processor_manager),
):
    """Get current processing state for a target."""
    try:
        state = manager.get_target_state(target_id)
        return TargetProcessingState(**state)

    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    except Exception as e:
        logger.error("Failed to get target state: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Internal server error")


@router.get("/api/v1/output-targets/{target_id}/metrics", response_model=TargetMetricsResponse, tags=["Metrics"])
async def get_target_metrics(
    target_id: str,
    _auth: AuthRequired,
    manager: ProcessorManager = Depends(get_processor_manager),
):
    """Get processing metrics for a target."""
    try:
        metrics = manager.get_target_metrics(target_id)
        return TargetMetricsResponse(**metrics)

    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    except Exception as e:
        logger.error("Failed to get target metrics: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Internal server error")


# ===== STATE CHANGE EVENT STREAM =====


@router.websocket("/api/v1/events/ws")
async def events_ws(
    websocket: WebSocket,
    token: str = Query(""),
):
    """WebSocket for real-time state change events. Auth via ?token=<api_key>."""
    from wled_controller.api.auth import verify_ws_token
    if not verify_ws_token(token):
        await websocket.close(code=4001, reason="Unauthorized")
        return

    await websocket.accept()

    manager = get_processor_manager()
    queue = manager.subscribe_events()

    try:
        while True:
            event = await queue.get()
            await websocket.send_json(event)
    except WebSocketDisconnect:
        pass
    except Exception:
        pass
    finally:
        manager.unsubscribe_events(queue)


# ===== OVERLAY VISUALIZATION =====

@router.post("/api/v1/output-targets/{target_id}/overlay/start", tags=["Visualization"])
async def start_target_overlay(
    target_id: str,
    _auth: AuthRequired,
    manager: ProcessorManager = Depends(get_processor_manager),
    target_store: OutputTargetStore = Depends(get_output_target_store),
    color_strip_store: ColorStripStore = Depends(get_color_strip_store),
    picture_source_store: PictureSourceStore = Depends(get_picture_source_store),
):
    """Start screen overlay visualization for a target.

    Displays a transparent overlay on the target display showing:
    - Border sampling zones (colored rectangles)
    - LED position markers (numbered dots)
    - Pixel-to-LED mapping ranges (colored segments)
    - Calibration info text
    """
    try:
        # Get target name from store
        target = target_store.get_target(target_id)
        if not target:
            raise ValueError(f"Target {target_id} not found")

        # Pre-load calibration and display info from the CSS store so the overlay
        # can start even when processing is not currently running.
        calibration = None
        display_info = None
        if isinstance(target, WledOutputTarget) and target.color_strip_source_id:
            first_css_id = target.color_strip_source_id
            if first_css_id:
                try:
                    css = color_strip_store.get_source(first_css_id)
                    if isinstance(css, (PictureColorStripSource, AdvancedPictureColorStripSource)) and css.calibration:
                        calibration = css.calibration
                        # Resolve the display this CSS is capturing
                        from wled_controller.api.routes.color_strip_sources import _resolve_display_index
                        ps_id = getattr(css, "picture_source_id", "") or ""
                        display_index = _resolve_display_index(ps_id, picture_source_store)
                        displays = get_available_displays()
                        if displays:
                            display_index = min(display_index, len(displays) - 1)
                            display_info = displays[display_index]
                except Exception as e:
                    logger.warning(f"Could not pre-load CSS calibration for overlay on {target_id}: {e}")

        await manager.start_overlay(target_id, target.name, calibration=calibration, display_info=display_info)
        return {"status": "started", "target_id": target_id}

    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    except RuntimeError as e:
        raise HTTPException(status_code=409, detail=str(e))
    except Exception as e:
        logger.error("Failed to start overlay: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Internal server error")


@router.post("/api/v1/output-targets/{target_id}/overlay/stop", tags=["Visualization"])
async def stop_target_overlay(
    target_id: str,
    _auth: AuthRequired,
    manager: ProcessorManager = Depends(get_processor_manager),
):
    """Stop screen overlay visualization for a target."""
    try:
        await manager.stop_overlay(target_id)
        return {"status": "stopped", "target_id": target_id}

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


@router.get("/api/v1/output-targets/{target_id}/overlay/status", tags=["Visualization"])
async def get_overlay_status(
    target_id: str,
    _auth: AuthRequired,
    manager: ProcessorManager = Depends(get_processor_manager),
):
    """Check if overlay is active for a target."""
    try:
        active = manager.is_overlay_active(target_id)
        return {"target_id": target_id, "active": active}

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


# ===== LED PREVIEW WEBSOCKET =====

@router.websocket("/api/v1/output-targets/{target_id}/led-preview/ws")
async def led_preview_ws(
    websocket: WebSocket,
    target_id: str,
    token: str = Query(""),
):
    """WebSocket for real-time LED strip preview. Sends binary RGB frames. Auth via ?token=<api_key>."""
    from wled_controller.api.auth import verify_ws_token
    if not verify_ws_token(token):
        await websocket.close(code=4001, reason="Unauthorized")
        return

    await websocket.accept()

    manager = get_processor_manager()

    try:
        manager.add_led_preview_client(target_id, websocket)
    except ValueError:
        await websocket.close(code=4004, reason="Target not found")
        return

    try:
        while True:
            await websocket.receive_text()
    except WebSocketDisconnect:
        pass
    finally:
        manager.remove_led_preview_client(target_id, websocket)
