import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core";
import type { Transaction } from "../generated/kysely-tailordb";
import { StageNotFoundError } from "../lib/errors.generated";
import { loadPipelineLockError } from "../lib/pipelineLock";

export interface ReorderPipelineStageInput {
  stageId: string;
  position: number;
}

export async function run(db: Transaction, input: ReorderPipelineStageInput, ctx: CommandContext) {
  void ctx;

  const stage = await db
    .selectFrom("PipelineStage")
    .selectAll()
    .where("id", "=", input.stageId)
    .forUpdate()
    .executeTakeFirst();

  if (!stage) {
    return err(new StageNotFoundError(input.stageId));
  }

  const lockError = await loadPipelineLockError(db, stage.pipelineId);
  if (lockError) {
    return err(lockError);
  }

  const siblings = await db
    .selectFrom("PipelineStage")
    .selectAll()
    .where("pipelineId", "=", stage.pipelineId)
    .orderBy("position", "asc")
    .execute();

  const otherStages = siblings.filter((item) => item.id !== stage.id);
  const insertIndex = Math.max(0, Math.min(input.position - 1, otherStages.length));
  const reordered = [...otherStages];
  reordered.splice(insertIndex, 0, stage);

  const now = new Date();
  for (const [index, current] of reordered.entries()) {
    const nextPosition = index + 1;
    if (current.position !== nextPosition) {
      await db
        .updateTable("PipelineStage")
        .set({
          position: nextPosition,
          updatedAt: now,
        })
        .where("id", "=", current.id)
        .execute();
    }
  }

  // Mirror exactly what was persisted: updatedAt moves only if the row was
  // actually rewritten (a no-op reorder keeps the stored timestamp).
  const nextPosition = insertIndex + 1;
  const moved = stage.position !== nextPosition;
  return ok({
    stage: { ...stage, position: nextPosition, updatedAt: moved ? now : stage.updatedAt },
  });
}
