import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core";
import { journalEntryLifecycle } from "../db/journalEntry.lifecycle.generated";
import type { Transaction } from "../generated/kysely-tailordb";
import { JournalEntryNotFoundError, InvalidStatusForCancelError } from "../lib/errors.generated";

export interface CancelJournalEntryInput {
  journalEntryId: string;
}

/**
 * Function: cancelJournalEntry
 *
 * Cancels a draft journal entry without deleting its header or prepared lines.
 * Posted entries must be reversed instead because they have ledger effect.
 */
export async function run(db: Transaction, input: CancelJournalEntryInput, _ctx: CommandContext) {
  const { journalEntryId } = input;

  // 1. Find journal entry with forUpdate
  const existing = await db
    .selectFrom("JournalEntry")
    .selectAll()
    .where("id", "=", journalEntryId)
    .forUpdate()
    .executeTakeFirst();

  if (!existing) {
    return err(new JournalEntryNotFoundError(journalEntryId));
  }

  // 2. Must be DRAFT
  const nextStatus = journalEntryLifecycle.tryTransition(existing.status, "cancel");
  if (!nextStatus) {
    return err(new InvalidStatusForCancelError(journalEntryId));
  }

  const journalEntry = await db
    .updateTable("JournalEntry")
    .set({ status: nextStatus })
    .where("id", "=", journalEntryId)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ journalEntry });
}
