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

export interface UpdatePipelineInput {
  pipelineId: string;
  name?: string;
  description?: string | null;
}

const PIPELINE_OWN_KEYS = new Set(["pipelineId", "name", "description"]);

// Module-managed Pipeline columns that must never be writable through the
// extension-field pass-through — status/locked changes go through their
// dedicated commands, itemNumberSeq is an internal counter, and
// createdByUserId is immutable creation-time audit metadata.
const PIPELINE_RESERVED_KEYS = new Set([
  "id",
  "pipelineType",
  "createdByUserId",
  "status",
  "locked",
  "itemNumberSeq",
  "createdAt",
  "updatedAt",
]);

export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: UpdatePipelineInput & Partial<CF>,
  ctx: CommandContext,
) {
  void ctx;

  // Consumer extension fields (declared via defineModule's pipeline.fields)
  // pass through to the update untouched — except reserved model columns.
  const customFields: Record<string, unknown> = {};
  for (const [key, value] of Object.entries(input as Record<string, unknown>)) {
    if (!PIPELINE_OWN_KEYS.has(key) && !PIPELINE_RESERVED_KEYS.has(key)) {
      customFields[key] = value;
    }
  }

  const pipeline = await db
    .selectFrom("Pipeline")
    .selectAll()
    .where("id", "=", input.pipelineId)
    .forUpdate()
    .executeTakeFirst();

  if (!pipeline) {
    return err(new PipelineNotFoundError(input.pipelineId));
  }

  if (input.name !== undefined && !input.name.trim()) {
    return err(new InvalidNameError(input.name));
  }

  if (input.name !== undefined && input.name !== pipeline.name && !pipeline.locked) {
    // See createPipeline for why locked (managed) pipelines skip the uniqueness check.
    const duplicate = await db
      .selectFrom("Pipeline")
      .selectAll()
      .where("pipelineType", "=", pipeline.pipelineType)
      .where("name", "=", input.name)
      .executeTakeFirst();

    if (duplicate && duplicate.id !== pipeline.id) {
      return err(new DuplicatePipelineNameError(input.name));
    }
  }

  const update: Record<string, unknown> = {
    ...customFields,
    updatedAt: new Date(),
  };

  if (input.name !== undefined) update.name = input.name;
  if (input.description !== undefined) update.description = input.description;

  const updatedPipeline = await db
    .updateTable("Pipeline")
    .set(update)
    .where("id", "=", input.pipelineId)
    .returningAll()
    .executeTakeFirst();

  return ok({ pipeline: updatedPipeline ?? { ...pipeline, ...update } });
}
