import { sql } from 'drizzle-orm';
import { CommandNotFoundError } from './errors.js';
import { mapCommandRow, selectCommandById } from './shared.js';
import type {
  CommandDb,
  CommandStatus,
  ReclaimCommandInput,
  ReclaimCommandResult,
} from './types.js';

interface UpdatedCommandRow {
  id: string;
  command_key: string;
  command_type: string;
  aggregate_type: string;
  aggregate_id: string;
  payload_hash: string;
  payload: unknown;
  status: CommandStatus;
  claimed_by: string;
  claimed_at: Date | string;
  claim_generation: number | string;
  lease_expires_at: Date | string;
  completed_at: Date | string | null;
  failed_at: Date | string | null;
  result_payload: unknown;
  failure_code: string | null;
  failure_message: string | null;
}

type UpdatedCommandRowResult = { rows: UpdatedCommandRow[] };

export async function reclaimCommand(
  db: CommandDb,
  input: ReclaimCommandInput,
): Promise<ReclaimCommandResult> {
  const updated = (await db.execute(sql`
    UPDATE "command_records"
    SET
      "claimed_by" = ${input.claimedBy},
      "claimed_at" = CURRENT_TIMESTAMP,
      "claim_generation" = "claim_generation" + 1,
      "lease_expires_at" = CURRENT_TIMESTAMP + INTERVAL '10 minutes'
    WHERE "id" = ${input.commandId}::uuid
      AND "status" = 'CLAIMED'
      AND "lease_expires_at" <= CURRENT_TIMESTAMP
      AND "command_key" = ${input.expectedCommandKey}
      AND "command_type" = ${input.expectedCommandType}
      AND "aggregate_type" = ${input.expectedAggregateType}
      AND "aggregate_id" = ${input.expectedAggregateId}::uuid
      AND "payload_hash" = ${input.expectedPayloadHash}
    RETURNING
      "id",
      "command_key",
      "command_type",
      "aggregate_type",
      "aggregate_id",
      "payload_hash",
      "payload",
      "status",
      "claimed_by",
      "claimed_at",
      "claim_generation",
      "lease_expires_at",
      "completed_at",
      "failed_at",
      "result_payload",
      "failure_code",
      "failure_message"
  `)) as UpdatedCommandRowResult;

  const updatedRow = updated.rows[0];
  if (updatedRow) return { reclaimed: true, record: mapCommandRow(updatedRow) };

  const existing = await selectCommandById(db, input.commandId);
  if (!existing) throw new CommandNotFoundError(input.commandId);
  return { reclaimed: false, record: existing };
}
