import type { Transaction } from "../generated/kysely-tailordb";
import {
  InvalidInvoiceLineError,
  InvoiceNotEditableError,
  SalesInvoiceNotFoundError,
} from "../lib/errors.generated";
import { encodeJson, type SalesInvoiceLineInput } from "../lib/domain";
import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/module";

export type UpdateSalesInvoiceInput = {
  salesInvoiceId: string;
  invoiceDate?: Date;
  notes?: string | null;
  lines?: SalesInvoiceLineInput[];
};
export async function run(db: Transaction, input: UpdateSalesInvoiceInput, ctx: CommandContext) {
  void ctx;
  const salesInvoice = await db
    .selectFrom("SalesInvoice")
    .selectAll()
    .where("id", "=", input.salesInvoiceId)
    .forUpdate()
    .executeTakeFirst();
  if (!salesInvoice) {
    return err(new SalesInvoiceNotFoundError(input.salesInvoiceId));
  }
  if (salesInvoice.status !== "DRAFT") {
    return err(new InvoiceNotEditableError(input.salesInvoiceId));
  }
  if (input.lines) {
    for (const line of input.lines) {
      if (!line.salesOrderLineId || line.quantity <= 0) {
        return err(new InvalidInvoiceLineError(input.salesInvoiceId));
      }
    }
  }
  const updated = await db
    .updateTable("SalesInvoice")
    .set({
      invoiceDate: input.invoiceDate ?? salesInvoice.invoiceDate,
      notes: input.notes === undefined ? salesInvoice.notes : input.notes,
      updatedAt: new Date(),
    })
    .where("id", "=", input.salesInvoiceId)
    .returningAll()
    .executeTakeFirst();
  if (input.lines) {
    await db
      .deleteFrom("SalesInvoiceLine")
      .where("salesInvoiceId", "=", input.salesInvoiceId)
      .execute();
    for (const line of input.lines) {
      const orderLine = await db
        .selectFrom("SalesOrderLine")
        .selectAll()
        .where("id", "=", line.salesOrderLineId)
        .executeTakeFirst();
      await db
        .insertInto("SalesInvoiceLine")
        .values({
          salesInvoiceId: input.salesInvoiceId,
          salesOrderLineId: line.salesOrderLineId,
          shipmentLineId: line.shipmentLineId ?? null,
          quantity: line.quantity,
          unitPrice: line.unitPrice ?? orderLine!.unitPrice,
          taxCode: line.taxCode ?? null,
          taxableAmount: line.taxableAmount ?? null,
          taxAmount: line.taxAmount ?? null,
          billingPolicy:
            line.billingPolicy ?? (line.shipmentLineId ? "SHIPMENT" : orderLine!.billingPolicy),
          orderPriceSnapshot: encodeJson({ unitPrice: orderLine!.unitPrice }),
          createdAt: new Date(),
          updatedAt: null,
        })
        .executeTakeFirst();
    }
  }
  return ok({ salesInvoice: updated! });
}
