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

export interface ReorderPipelineItemInput {
  itemId: string;
  position: number;
}

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

  const item = await db
    .selectFrom("PipelineItem")
    .selectAll()
    .where("id", "=", input.itemId)
    .forUpdate()
    .executeTakeFirst();

  if (!item) {
    return err(new ItemNotFoundError(input.itemId));
  }

  if (item.lifecycle !== "OPEN") {
    return err(new ItemNotOpenError(input.itemId));
  }

  const siblings = await db
    .selectFrom("PipelineItem")
    .selectAll()
    .where("stageId", "=", item.stageId)
    .orderBy("position", "asc")
    .execute();

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

  const now = new Date();
  for (const [index, current] of reordered.entries()) {
    const nextPosition = index + 1;
    if (current.position !== nextPosition) {
      await db
        .updateTable("PipelineItem")
        .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 = item.position !== nextPosition;
  return ok({
    item: { ...item, position: nextPosition, updatedAt: moved ? now : item.updatedAt },
  });
}
