"""Read-only HTTP routes for the Inventory domain.""" from typing import Annotated from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, Query, status from app.identity import require_permission, user_has_permission from app.inventory.dependencies import get_inventory_service from app.inventory.exceptions import ( DuplicateAssetID, InventoryValidationError, MinerNotFound, ) from app.inventory.models import ( InventoryAssetIDHistoryResponse, InventoryCreateRequest, InventoryListMeta, InventoryLocationOptions, InventoryMetadataUpdateRequest, InventoryMinerDetailResponse, InventoryMinerListResponse, InventorySearchFilters, ) from app.inventory.service import InventoryService router = APIRouter(prefix="/api/inventory/miners", tags=["inventory"]) require_inventory_read = require_permission("miners.view") require_inventory_write = require_permission("miners.update") InventoryServiceDependency = Annotated[ InventoryService, Depends(get_inventory_service), ] InventoryReadUser = Annotated[dict, Depends(require_inventory_read)] InventoryWriteUser = Annotated[dict, Depends(require_inventory_write)] InventoryQuery = Annotated[InventorySearchFilters, Query()] @router.post( "", response_model=InventoryMinerDetailResponse, status_code=status.HTTP_201_CREATED, ) def create_inventory_miner( request: InventoryCreateRequest, service: InventoryServiceDependency, current_user: InventoryWriteUser, ) -> InventoryMinerDetailResponse: del current_user try: miner = service.create_miner(request) except DuplicateAssetID as exc: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail=str(exc), ) from exc except InventoryValidationError as exc: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(exc), ) from exc return InventoryMinerDetailResponse(data=miner) @router.patch("/{miner_id}", response_model=InventoryMinerDetailResponse) def update_inventory_miner_metadata( miner_id: UUID, request: InventoryMetadataUpdateRequest, service: InventoryServiceDependency, current_user: InventoryWriteUser, ) -> InventoryMinerDetailResponse: try: miner = service.update_metadata( miner_id, request, actor_user_id=UUID(str(current_user["id"])), ) except MinerNotFound as exc: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Miner not found", ) from exc except DuplicateAssetID as exc: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail=str(exc), ) from exc except InventoryValidationError as exc: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(exc), ) from exc return InventoryMinerDetailResponse(data=miner) @router.get("", response_model=InventoryMinerListResponse) def list_inventory_miners( filters: InventoryQuery, service: InventoryServiceDependency, current_user: InventoryReadUser, ) -> InventoryMinerListResponse: include_location_details = user_has_permission(current_user, "locations.view") if (filters.site_id or filters.room_id) and not include_location_details: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Location viewing permission is required for location filters", ) try: miners = service.search_miners( filters, include_location_details=include_location_details, ) except InventoryValidationError as exc: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(exc), ) from exc location_options = InventoryLocationOptions() if include_location_details: location_options = InventoryLocationOptions.model_validate( service.list_location_filter_options() ) return InventoryMinerListResponse( data=miners, meta=InventoryListMeta( limit=filters.limit, offset=filters.offset, returned=len(miners), location_options=location_options, ), ) @router.get( "/{miner_id}/asset-id-history", response_model=InventoryAssetIDHistoryResponse, ) def get_inventory_miner_asset_id_history( miner_id: UUID, service: InventoryServiceDependency, current_user: InventoryReadUser, ) -> InventoryAssetIDHistoryResponse: del current_user try: history = service.get_asset_id_history(miner_id) except MinerNotFound as exc: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Miner not found", ) from exc return InventoryAssetIDHistoryResponse(data=history) @router.get("/{miner_id}", response_model=InventoryMinerDetailResponse) def get_inventory_miner( miner_id: UUID, service: InventoryServiceDependency, current_user: InventoryReadUser, ) -> InventoryMinerDetailResponse: del current_user try: miner = service.get_miner(miner_id) except MinerNotFound as exc: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Miner not found", ) from exc return InventoryMinerDetailResponse(data=miner)