// One typed client method per production-run endpoint (docs/30-BACKEND-PHASE2.md §D.2–D.3,
// FR-MFG-08..19).
//
// Every stage action takes an `idempotencyKey`. The server accepts the header but does not
// store it (matching GRN confirm): replay safety comes from the status guards, so a
// double-fire returns a 409 rather than acting twice. Pass a per-action
// `useRef(crypto.randomUUID())` anyway — it is part of the contract and costs nothing.
//
// Treat a 409 carrying a stage-status code as "someone else moved first": refetch the run
// and re-render silently instead of showing an error (docs/21-FRONTEND-PHASE2.md §6). Use
// `isStaleStageError` for that check.
import { apiRequest, apiRequestWithETag, buildQuery } from "@/lib/api-client"
import { ApiResult, PagedResponse } from "@/types/common"
import {
  ApproveStageRequest,
  ApproveStageResult,
  CancelRunRequest,
  CancelRunResult,
  CompleteStageRequest,
  CreateRunRequest,
  ProductionRunGraph,
  ProductionRunStatus,
  ProductionRunSummary,
  RejectIntakeResult,
  RejectRequest,
  ReturnLeftoverRequest,
  ReturnLeftoverResult,
  RunStage,
  StartStageResult,
  TerminalRejectResult,
  TransferRemainderRequest,
  TransferResult,
  UpdateStageQuantitiesRequest,
} from "@/types/production"

export interface ListRunsParams {
  page?: number
  pageSize?: number
  q?: string
  status?: ProductionRunStatus
  templateId?: number
  warehouseId?: number
  sort?: string
}

export const productionRunsApi = {
  list(params: ListRunsParams = {}): Promise<PagedResponse<ProductionRunSummary>> {
    return apiRequest<PagedResponse<ProductionRunSummary>>(`/production-runs${buildQuery(params)}`)
  },

  get(runId: number): Promise<ApiResult<ProductionRunGraph>> {
    return apiRequestWithETag<ProductionRunGraph>(`/production-runs/${runId}`)
  },

  create(request: CreateRunRequest): Promise<ApiResult<ProductionRunGraph>> {
    return apiRequestWithETag<ProductionRunGraph>("/production-runs", { method: "POST", body: request })
  },

  /** `409 STAGE_NOT_EDITABLE` once the stage has started (FR-MFG-08). */
  updateStageQuantities(
    runId: number,
    runStageId: number,
    request: UpdateStageQuantitiesRequest,
  ): Promise<RunStage> {
    return apiRequest<RunStage>(`/production-runs/${runId}/stages/${runStageId}/quantities`, {
      method: "PUT",
      body: request,
    })
  },

  // --- stage actions ---------------------------------------------------------

  start(runId: number, runStageId: number, idempotencyKey?: string): Promise<StartStageResult> {
    return apiRequest<StartStageResult>(`/production-runs/${runId}/stages/${runStageId}/start`, {
      method: "POST",
      idempotencyKey,
    })
  },

  complete(
    runId: number,
    runStageId: number,
    request: CompleteStageRequest,
    idempotencyKey?: string,
  ): Promise<RunStage> {
    return apiRequest<RunStage>(`/production-runs/${runId}/stages/${runStageId}/complete`, {
      method: "POST",
      body: request,
      idempotencyKey,
    })
  },

  /** Non-terminal: transfers WIP to children. Terminal: posts the receipt and completes the run. */
  approve(
    runId: number,
    runStageId: number,
    request: ApproveStageRequest = {},
    idempotencyKey?: string,
  ): Promise<ApproveStageResult> {
    return apiRequest<ApproveStageResult>(`/production-runs/${runId}/stages/${runStageId}/approve`, {
      method: "POST",
      body: request,
      idempotencyKey,
    })
  },

  transfer(
    runId: number,
    runStageId: number,
    request: TransferRemainderRequest,
    idempotencyKey?: string,
  ): Promise<TransferResult> {
    return apiRequest<TransferResult>(`/production-runs/${runId}/stages/${runStageId}/transfer`, {
      method: "POST",
      body: request,
      idempotencyKey,
    })
  },

  rejectIntake(
    runId: number,
    runStageId: number,
    request: RejectRequest = {},
    idempotencyKey?: string,
  ): Promise<RejectIntakeResult> {
    return apiRequest<RejectIntakeResult>(`/production-runs/${runId}/stages/${runStageId}/reject-intake`, {
      method: "POST",
      body: request,
      idempotencyKey,
    })
  },

  /** Terminal stage only — resets the entire run for a rework pass (FR-MFG-16). */
  rejectTerminal(
    runId: number,
    runStageId: number,
    request: RejectRequest = {},
    idempotencyKey?: string,
  ): Promise<TerminalRejectResult> {
    return apiRequest<TerminalRejectResult>(`/production-runs/${runId}/stages/${runStageId}/reject`, {
      method: "POST",
      body: request,
      idempotencyKey,
    })
  },

  // --- run-level actions -----------------------------------------------------

  returnLeftover(
    runId: number,
    runInputId: number,
    request: ReturnLeftoverRequest,
    idempotencyKey?: string,
  ): Promise<ReturnLeftoverResult> {
    return apiRequest<ReturnLeftoverResult>(`/production-runs/${runId}/inputs/${runInputId}/return-leftover`, {
      method: "POST",
      body: request,
      idempotencyKey,
    })
  },

  cancel(runId: number, request: CancelRunRequest, idempotencyKey?: string): Promise<CancelRunResult> {
    return apiRequest<CancelRunResult>(`/production-runs/${runId}/cancel`, {
      method: "POST",
      body: request,
      idempotencyKey,
    })
  },
}

/**
 * Stage-status conflicts, i.e. "the stage already moved on — probably another user".
 *
 * docs/21-FRONTEND-PHASE2.md §6 says to refetch the run silently and re-render for these
 * rather than surfacing an error, which is also what makes the server's accept-and-ignore
 * idempotency posture feel right: a double-click just refreshes.
 */
const STALE_STAGE_CODES = new Set([
  "STAGE_NOT_READY",
  "STAGE_NOT_IN_PROGRESS",
  "STAGE_NOT_DONE",
  "STAGE_NOT_EDITABLE",
  "STAGE_REJECT_INVALID",
])

export function isStaleStageError(error: unknown): boolean {
  const code = (error as { code?: string } | null)?.code
  return code !== undefined && STALE_STAGE_CODES.has(code)
}
