"""Update management API endpoints.

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

Supports both legacy whole-system operations and per-component operations.
"""
from typing import Optional, List
from fastapi import APIRouter, HTTPException, Path
from pydantic import BaseModel

from ..services.container import container
from ..managers.ble_manager import get_ble_manager, NotificationType


router = APIRouter()


# ---------------------------------------------------------------------------
# Response / request models
# ---------------------------------------------------------------------------

class ComponentUpdateInfo(BaseModel):
    """Per-component update information."""
    component_id: str
    current_version: Optional[str] = None
    available_version: str
    changelog: str = ""
    download_size: Optional[int] = None
    compatible: bool = True
    incompatible_reason: Optional[str] = None


class PluginUpdateInfo(BaseModel):
    """Per-plugin update information."""
    plugin_id: str
    current_version: str
    available_version: str
    changelog: str = ""
    download_size: Optional[int] = None
    source_repo: str = ""


class UpdateCheckResponse(BaseModel):
    """Response model for update check."""
    update_available: bool
    current_version: str
    latest_version: Optional[str] = None
    release_date: Optional[str] = None
    changelog: Optional[str] = None
    is_prerelease: bool = False
    download_size: Optional[int] = None
    message: Optional[str] = None
    components: List[ComponentUpdateInfo] = []
    plugins: List[PluginUpdateInfo] = []


class UpdateProgressResponse(BaseModel):
    """Response model for update progress."""
    status: str
    current_version: str
    available_version: Optional[str] = None
    download_progress: float = 0.0
    error_message: Optional[str] = None
    last_check: Optional[str] = None
    active_component: Optional[str] = None
    components: List[dict] = []


class ReleaseNotesRequest(BaseModel):
    """Request model for release notes."""
    version: Optional[str] = None


class ComponentDownloadRequest(BaseModel):
    """Optional request body for selective download."""
    components: Optional[List[str]] = None


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def _service_error_to_http_status(error_code: str) -> int:
    """Map service error codes to HTTP status codes."""
    codes = {
        "update_check_error": 500,
        "no_update_available": 400,
        "download_failed": 500,
        "update_download_error": 500,
        "no_update_downloaded": 400,
        "installation_failed": 500,
        "update_install_error": 500,
        "progress_error": 500,
        "release_notes_error": 500,
        "check_download_error": 500,
        "full_update_error": 500,
        "manifest_error": 500,
        "component_not_available": 400,
        "component_download_error": 500,
        "component_install_error": 500,
        "rollback_error": 500,
    }
    return codes.get(error_code, 500)


def _raise_on_error(result):
    """Raise HTTPException if result indicates failure."""
    if not result.success:
        raise HTTPException(
            status_code=_service_error_to_http_status(result.error.code),
            detail=result.error.message,
        )


# ---------------------------------------------------------------------------
# Legacy endpoints (backward-compatible)
# ---------------------------------------------------------------------------

@router.get("/check", response_model=UpdateCheckResponse)
async def check_for_updates():
    """Check for available updates (components + plugins)."""
    result = await container.update_service.check_for_updates()
    _raise_on_error(result)

    # BLE notification
    if result.data.get("update_available"):
        try:
            ble_manager = get_ble_manager()
            await ble_manager.send_notification(
                NotificationType.UPDATE_AVAILABLE,
                f"Update available: v{result.data['latest_version']}",
                {"version": result.data["latest_version"]},
            )
        except Exception:
            pass

    return UpdateCheckResponse(**result.data)


@router.get("/progress", response_model=UpdateProgressResponse)
async def get_update_progress():
    """Get current update progress."""
    result = await container.update_service.get_progress()
    _raise_on_error(result)
    return UpdateProgressResponse(**result.data)


@router.post("/download")
async def download_update(body: Optional[ComponentDownloadRequest] = None):
    """Download available updates.

    Without a body, downloads all available components (legacy behavior).
    With ``{"components": ["frontend"]}``, downloads only the specified ones.
    """
    if body and body.components:
        results = {}
        for comp_id in body.components:
            r = await container.update_service.download_component(comp_id)
            _raise_on_error(r)
            results[comp_id] = r.data
        return {"message": "Components downloaded", "components": results}

    result = await container.update_service.download_update()
    _raise_on_error(result)
    return {"message": result.data["message"]}


@router.post("/install")
async def install_update(body: Optional[ComponentDownloadRequest] = None):
    """Install downloaded updates.

    Without a body, installs all downloaded components (legacy behavior).
    With ``{"components": ["frontend"]}``, installs only the specified ones.
    """
    if body and body.components:
        results = {}
        for comp_id in body.components:
            r = await container.update_service.install_component(comp_id)
            _raise_on_error(r)
            results[comp_id] = r.data
        return {"message": "Components installed", "components": results}

    result = await container.update_service.install_update()
    _raise_on_error(result)

    # BLE notification
    try:
        ble_manager = get_ble_manager()
        await ble_manager.send_notification(
            NotificationType.UPDATE_COMPLETE,
            "Update installed successfully",
            {"restart_required": True},
        )
    except Exception:
        pass

    return {
        "message": result.data["message"],
        "restart_required": result.data.get("requires_restart", True),
    }


@router.post("/release-notes", response_model=dict)
async def get_release_notes(request: ReleaseNotesRequest):
    """Get release notes for a specific version."""
    result = await container.update_service.get_release_notes(request.version)
    _raise_on_error(result)
    return {
        "version": result.data["version"],
        "notes": result.data["release_notes"],
    }


@router.get("/current-version")
async def get_current_version():
    """Get current system version."""
    result = await container.update_service.get_progress()
    _raise_on_error(result)
    return {
        "version": result.data["current_version"],
        "last_check": result.data["last_check"],
    }


# ---------------------------------------------------------------------------
# Component-level endpoints
# ---------------------------------------------------------------------------

@router.get("/components")
async def get_installed_components():
    """Get installed component manifest."""
    result = await container.update_service.get_installed_components()
    _raise_on_error(result)
    return result.data


@router.post("/components/{component_id}/download")
async def download_component(
    component_id: str = Path(description="Component ID (pm3, frontend, backend, theme)"),
):
    """Download a single component update."""
    result = await container.update_service.download_component(component_id)
    _raise_on_error(result)
    return result.data


@router.post("/components/{component_id}/install")
async def install_component(
    component_id: str = Path(description="Component ID"),
):
    """Install a single downloaded component."""
    result = await container.update_service.install_component(component_id)
    _raise_on_error(result)
    return result.data


@router.post("/components/{component_id}/rollback")
async def rollback_component(
    component_id: str = Path(description="Component ID"),
):
    """Rollback a component to its backup."""
    result = await container.update_service.rollback_component(component_id)
    _raise_on_error(result)
    return result.data
