import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  JournalEntryNotFoundError,
  InvalidStatusForUpdateError,
  DuplicateReferenceNumberError,
} from "../lib/errors.generated";

export type UpdateJournalEntryInput = {
  journalEntryId: string;
  description?: string;
  sourceDocumentReference?: string;
  entryDate?: Date;
  referenceNumber?: string;
};
/**
 * Function: updateJournalEntry
 *
 * Modifies the header fields of an existing journal entry that is in DRAFT status.
 * Posted and reversed entries are immutable and cannot be updated.
 */
export async function run(db: Transaction, input: UpdateJournalEntryInput, _ctx: CommandContext) {
  const { journalEntryId, description, sourceDocumentReference, entryDate, referenceNumber } =
    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
  if (existing.status !== "DRAFT") {
    return err(new InvalidStatusForUpdateError(journalEntryId));
  }

  // 3. If referenceNumber is being updated, check uniqueness within company
  if (referenceNumber !== undefined) {
    const duplicate = await db
      .selectFrom("JournalEntry")
      .selectAll()
      .where("companyId", "=", existing.companyId)
      .where("referenceNumber", "=", referenceNumber)
      .where("id", "!=", journalEntryId)
      .executeTakeFirst();

    if (duplicate) {
      return err(new DuplicateReferenceNumberError(referenceNumber));
    }
  }

  // 4. Build update object from provided fields
  const updateData: Record<string, unknown> = {
    updatedAt: new Date(),
  };

  if (description !== undefined) updateData.description = description;
  if (sourceDocumentReference !== undefined)
    updateData.sourceDocumentReference = sourceDocumentReference;
  if (entryDate !== undefined) updateData.entryDate = entryDate;
  if (referenceNumber !== undefined) updateData.referenceNumber = referenceNumber;

  // 5. Update journal entry
  const journalEntry = await db
    .updateTable("JournalEntry")
    .set(updateData)
    .where("id", "=", journalEntryId)
    .returningAll()
    .executeTakeFirst();

  return ok({ journalEntry: journalEntry! });
}
