"""Proxmark3 API endpoints.

Refactored to use PM3Service for all business logic.
Endpoints are now thin adapters that convert HTTP requests/responses.

Multi-device support:
- GET /devices - List all devices
- GET /devices/available - List available devices
- POST /devices/{device_id}/identify - Blink device LEDs
- GET /devices/{device_id}/status - Get device-specific status
- GET /status?device_id=xxx - Get status (all devices or specific)
- POST /command - Execute command (with optional device_id)
"""
from fastapi import APIRouter, HTTPException, Query
from pydantic import BaseModel, Field
from typing import Optional, List, Dict, Any

from ..services.container import container

router = APIRouter()


class CommandRequest(BaseModel):
    command: str
    session_id: Optional[str] = None
    device_id: Optional[str] = None  # Multi-device support


class CommandResponse(BaseModel):
    success: bool
    output: str
    error: Optional[str] = None


class StatusResponse(BaseModel):
    connected: bool
    device: str
    version: Optional[str] = None
    session_active: bool


class FirmwareInfo(BaseModel):
    """Firmware information for a device."""
    bootrom_version: Optional[str] = None
    os_version: Optional[str] = None
    compatible: bool = False


class DeviceInfo(BaseModel):
    """Device information response."""
    device_id: str
    device_path: str
    friendly_name: Optional[str] = None
    serial_number: Optional[str] = None
    status: str
    connected: bool
    in_use: bool
    firmware_info: FirmwareInfo
    usb_vid: Optional[str] = None
    usb_pid: Optional[str] = None
    last_seen: str


class DeviceListResponse(BaseModel):
    """Response for device list endpoints."""
    devices: List[DeviceInfo]


class DeviceStatusResponse(BaseModel):
    """Response for single device status."""
    device_id: str
    device_path: str
    friendly_name: Optional[str] = None
    serial_number: Optional[str] = None
    connected: bool
    in_use: bool
    status: str
    firmware_info: FirmwareInfo
    last_seen: str


class IdentifyRequest(BaseModel):
    """Request to identify a device (blink LEDs)."""
    duration_ms: int = Field(default=2000, ge=500, le=10000, description="LED blink duration in milliseconds")


def _service_error_to_http_status(error_code: str) -> int:
    """Map service error codes to HTTP status codes.

    Args:
        error_code: Service error code

    Returns:
        HTTP status code
    """
    codes = {
        # Session errors
        "session_locked": 423,
        "session_not_found": 404,
        # Connection errors
        "pm3_not_connected": 503,
        "connection_error": 503,
        "disconnection_error": 500,
        # Command execution errors
        "command_failed": 500,
        "execution_error": 500,
        "status_error": 500,
        # Multi-device errors
        "device_not_found": 404,
        "device_manager_not_available": 503,
        "list_devices_error": 500,
        "get_available_devices_error": 500,
        "identify_device_error": 500,
    }
    return codes.get(error_code, 500)


@router.get("/status")
async def get_status(device_id: Optional[str] = Query(None, description="Optional device ID for multi-device support")):
    """Get Proxmark3 status.

    Multi-device support:
    - If device_id is None and device_manager exists: returns all devices
    - If device_id is provided: returns specific device status
    - If device_id is None and no device_manager: returns legacy single device status

    Args:
        device_id: Optional device ID for multi-device mode

    Returns:
        StatusResponse (legacy single device) or dict with devices list (multi-device)
    """
    result = await container.pm3_service.get_status(device_id=device_id)

    if not result.success:
        raise HTTPException(
            status_code=_service_error_to_http_status(result.error.code),
            detail=result.error.message
        )

    # Multi-device mode: return all devices
    if "devices" in result.data:
        return {"devices": result.data["devices"]}

    # Single device mode (specific device or legacy)
    return StatusResponse(
        connected=result.data["connected"],
        device=result.data["device_path"],
        version=result.data.get("version"),
        session_active=result.data["session_active"]
    )


@router.post("/connect")
async def connect():
    """Connect to Proxmark3 device.

    Uses PM3Service for business logic.
    """
    result = await container.pm3_service.connect()

    if not result.success:
        raise HTTPException(
            status_code=_service_error_to_http_status(result.error.code),
            detail=result.error.message
        )

    return {"success": True, "message": result.data["message"]}


@router.post("/disconnect")
async def disconnect():
    """Disconnect from Proxmark3 device.

    Uses PM3Service for business logic.
    """
    result = await container.pm3_service.disconnect()

    if not result.success:
        raise HTTPException(
            status_code=_service_error_to_http_status(result.error.code),
            detail=result.error.message
        )

    return {"success": True, "message": result.data["message"]}


@router.post("/command", response_model=CommandResponse)
async def execute_command(request: CommandRequest):
    """Execute a Proxmark3 command.

    Uses PM3Service for all business logic including:
    - Session validation
    - Command execution
    - Session activity updates
    - Multi-device support (via device_id in request)

    Multi-device support:
    - If device_id is provided: command executes on specified device
    - If device_id is None: uses legacy single-device mode

    Args:
        request: CommandRequest with command, optional session_id, and optional device_id
    """
    result = await container.pm3_service.execute_command(
        command=request.command,
        session_id=request.session_id,
        device_id=request.device_id
    )

    if result.success:
        return CommandResponse(
            success=True,
            output=result.data["output"],
            error=None
        )
    else:
        # For session_locked, return 423 via HTTPException
        # For other errors, return in response body
        if result.error.code == "session_locked":
            raise HTTPException(
                status_code=423,
                detail=result.error.message
            )

        # Include details in error message for better diagnostics
        error_msg = result.error.message
        if result.error.details:
            error_msg = f"{error_msg}: {result.error.details}"

        return CommandResponse(
            success=False,
            output="",
            error=error_msg
        )


@router.get("/commands/history")
async def get_command_history(limit: int = 50):
    """Get recent command history.

    TODO: Implement command history in PM3Service or separate HistoryService
    """
    # TODO: Implement database query
    return {"history": []}


# ============================================================================
# Multi-Device Endpoints
# ============================================================================


@router.get("/devices", response_model=DeviceListResponse)
async def list_devices():
    """List all discovered PM3 devices.

    Returns all devices regardless of their status (connected, in use, etc.).
    Uses PM3DeviceManager via PM3Service.

    Returns:
        DeviceListResponse: List of all devices with their status and firmware info
    """
    result = await container.pm3_service.list_devices()

    if not result.success:
        raise HTTPException(
            status_code=_service_error_to_http_status(result.error.code),
            detail=result.error.message
        )

    # Convert device dictionaries to DeviceInfo models
    devices = []
    for device_dict in result.data["devices"]:
        devices.append(DeviceInfo(
            device_id=device_dict["device_id"],
            device_path=device_dict["device_path"],
            friendly_name=device_dict.get("friendly_name"),
            serial_number=device_dict.get("serial_number"),
            status=device_dict["status"],
            connected=device_dict["status"] in ["CONNECTED", "IN_USE"],
            in_use=device_dict["status"] == "IN_USE",
            firmware_info=FirmwareInfo(
                bootrom_version=device_dict["firmware_info"].get("bootrom_version"),
                os_version=device_dict["firmware_info"].get("os_version"),
                compatible=device_dict["firmware_info"].get("compatible", False)
            ),
            usb_vid=device_dict.get("usb_vid"),
            usb_pid=device_dict.get("usb_pid"),
            last_seen=device_dict["last_seen"]
        ))

    return DeviceListResponse(devices=devices)


@router.get("/devices/available", response_model=DeviceListResponse)
async def list_available_devices():
    """List available PM3 devices (not currently in use).

    Returns only devices that are connected but do not have active sessions.
    These devices can be selected for new operations.

    Returns:
        DeviceListResponse: List of available devices
    """
    result = await container.pm3_service.get_available_devices()

    if not result.success:
        raise HTTPException(
            status_code=_service_error_to_http_status(result.error.code),
            detail=result.error.message
        )

    # Convert device dictionaries to DeviceInfo models
    devices = []
    for device_dict in result.data["devices"]:
        devices.append(DeviceInfo(
            device_id=device_dict["device_id"],
            device_path=device_dict["device_path"],
            friendly_name=device_dict.get("friendly_name"),
            serial_number=device_dict.get("serial_number"),
            status=device_dict["status"],
            connected=device_dict["status"] in ["CONNECTED", "IN_USE"],
            in_use=device_dict["status"] == "IN_USE",
            firmware_info=FirmwareInfo(
                bootrom_version=device_dict["firmware_info"].get("bootrom_version"),
                os_version=device_dict["firmware_info"].get("os_version"),
                compatible=device_dict["firmware_info"].get("compatible", False)
            ),
            usb_vid=device_dict.get("usb_vid"),
            usb_pid=device_dict.get("usb_pid"),
            last_seen=device_dict["last_seen"]
        ))

    return DeviceListResponse(devices=devices)


@router.post("/devices/{device_id}/identify")
async def identify_device(device_id: str, request: IdentifyRequest = IdentifyRequest()):
    """Identify a PM3 device by blinking its LEDs.

    Useful for physically identifying which device corresponds to a device_id
    when multiple devices are connected.

    Args:
        device_id: Device ID to identify
        request: Request with optional duration_ms (default: 2000ms)

    Returns:
        Success message
    """
    result = await container.pm3_service.identify_device(
        device_id=device_id,
        duration_ms=request.duration_ms
    )

    if not result.success:
        raise HTTPException(
            status_code=_service_error_to_http_status(result.error.code),
            detail=result.error.message
        )

    return {"success": True, "message": result.data["message"]}


@router.get("/devices/{device_id}/status", response_model=DeviceStatusResponse)
async def get_device_status(device_id: str):
    """Get status for a specific PM3 device.

    Args:
        device_id: Device ID to query

    Returns:
        DeviceStatusResponse: Device status and firmware info
    """
    result = await container.pm3_service.get_status(device_id=device_id)

    if not result.success:
        raise HTTPException(
            status_code=_service_error_to_http_status(result.error.code),
            detail=result.error.message
        )

    data = result.data
    return DeviceStatusResponse(
        device_id=data["device_id"],
        device_path=data["device_path"],
        friendly_name=data.get("friendly_name"),
        serial_number=data.get("serial_number"),
        connected=data["connected"],
        in_use=data["in_use"],
        status=data["status"],
        firmware_info=FirmwareInfo(
            bootrom_version=data["firmware_info"]["bootrom_version"],
            os_version=data["firmware_info"]["os_version"],
            compatible=data["firmware_info"]["compatible"]
        ),
        last_seen=data["last_seen"]
    )
