"""Typed contracts for the Repairs domain.""" from datetime import datetime from typing import Any, Literal from uuid import UUID from pydantic import AwareDatetime, BaseModel, ConfigDict, Field RepairReason = Literal[ "HARDWARE_FAILURE", "LOW_HASHRATE", "CONTROL_BOARD", "HASHBOARD", "PSU", "FAN", "OVERHEATING", "NETWORKING", "PREVENTIVE_MAINTENANCE", "INSPECTION", "OTHER", ] RepairStatus = Literal[ "OPEN", "IN_PROGRESS", "WAITING_FOR_PARTS", "READY_FOR_TEST", "COMPLETED", "CANCELLED", ] RepairEventType = Literal[ "REPAIR_STARTED", "INITIAL_NOTE", "REPAIR_COMPLETED", "CONTROL_BOARD_REPLACED", ] RepairEligibleLifecycle = Literal["active", "spare"] RepairPostLifecycle = Literal["active", "spare"] RepairMinerLifecycle = Literal["active", "spare", "retired", "in_repair"] class BeginRepairRequest(BaseModel): model_config = ConfigDict(extra="forbid") miner_id: UUID reason: RepairReason notes: str | None = Field(default=None, max_length=4000) class RepairCompleteRequest(BaseModel): model_config = ConfigDict(extra="forbid") target_lifecycle: RepairPostLifecycle completion_summary: str | None = Field(default=None, max_length=4000) ControlBoardReplacementReason = Literal[ "failed", "upgrade", "diagnostic_swap", "preventive_maintenance", "temporary_donor", "unknown", ] RemovedBoardDisposition = Literal[ "repair_inventory", "sent_for_repair", "scrapped", "retained_for_diagnosis", "returned_to_vendor", "unknown", ] class ControlBoardReplacementRequest(BaseModel): model_config = ConfigDict(extra="forbid") new_mac_address: str = Field(min_length=1, max_length=64) new_control_board_serial: str | None = Field(..., max_length=255) board_model: str | None = Field(default=None, max_length=255) board_revision: str | None = Field(default=None, max_length=255) firmware_version: str | None = Field(default=None, max_length=255) removed_board_disposition: RemovedBoardDisposition replacement_reason: ControlBoardReplacementReason notes: str | None = Field(default=None, max_length=4000) class RepairStartedPayload(BaseModel): reason: RepairReason from_lifecycle: RepairEligibleLifecycle to_lifecycle: Literal["in_repair"] = "in_repair" class InitialRepairNotePayload(BaseModel): note: str = Field(min_length=1, max_length=4000) class RepairCompletedPayload(BaseModel): previous_miner_lifecycle: Literal["in_repair"] = "in_repair" resulting_miner_lifecycle: RepairPostLifecycle pre_repair_lifecycle: RepairEligibleLifecycle | None = None completion_summary: str | None = Field(default=None, max_length=4000) class ControlBoardReplacedPayload(BaseModel): old_mac_address: str | None new_mac_address: str old_control_board_serial: str | None new_control_board_serial: str | None old_board_model: str | None = None new_board_model: str | None = None old_board_revision: str | None = None new_board_revision: str | None = None new_firmware_version: str | None = None removed_board_disposition: RemovedBoardDisposition replacement_reason: ControlBoardReplacementReason notes: str | None = Field(default=None, max_length=4000) class ControlBoardReplacementResult(BaseModel): miner_id: UUID asset_id: str | None = None repair_id: UUID installation_id: UUID event_id: UUID event_sequence: int = Field(ge=1) current_mac: str control_board_serial: str | None replaced_at: AwareDatetime replaced_by: UUID class ControlBoardReplacementResponse(BaseModel): data: ControlBoardReplacementResult meta: dict[str, object] = Field(default_factory=dict) error: None = None class BeginRepairResult(BaseModel): repair_id: UUID miner_id: UUID status: Literal["OPEN"] reason: RepairReason opened_at: datetime opened_by: UUID class BeginRepairResponse(BaseModel): data: BeginRepairResult meta: dict[str, object] = Field(default_factory=dict) error: None = None class RepairActor(BaseModel): user_id: UUID display_name: str class RepairDetail(BaseModel): repair_id: UUID miner_id: UUID miner_asset_id: str | None = None status: RepairStatus reason: RepairReason opened_at: AwareDatetime opened_by: RepairActor closed_at: AwareDatetime | None = None closed_by: RepairActor | None = None miner_lifecycle: RepairMinerLifecycle latest_event_sequence: int | None = Field(default=None, ge=1) class RepairTimelineEvent(BaseModel): event_id: UUID sequence: int = Field(ge=1) event_type: RepairEventType actor: RepairActor occurred_at: AwareDatetime payload: dict[str, Any] class RepairTimeline(BaseModel): repair_id: UUID events: list[RepairTimelineEvent] = Field(default_factory=list) class RepairDetailResponse(BaseModel): data: RepairDetail meta: dict[str, object] = Field(default_factory=dict) error: None = None class RepairTimelineResponse(BaseModel): data: RepairTimeline meta: dict[str, object] = Field(default_factory=dict) error: None = None