import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core";
import { channelOrderLifecycle } from "../db/channelOrder.lifecycle.generated";
import type { Transaction } from "../generated/kysely-tailordb";
import { encodeJson } from "../lib/domain";
import {
  ChannelNotFoundError,
  CrossCompanyReferenceError,
  ExternalOrderIdRequiredError,
} from "../lib/errors.generated";

export interface RecordChannelOrderInput {
  companyId: string;
  channelId: string;
  externalOrderId: string;
  externalCustomerId?: string;
  customerId?: string | null;
  currencyId?: string | null;
  rawPayload?: Record<string, unknown> | null;
  normalizedPayload?: Record<string, unknown> | null;
  syncJobId?: string | null;
}

export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: RecordChannelOrderInput & CF,
  ctx: CommandContext,
) {
  void ctx;
  const {
    companyId,
    channelId,
    externalOrderId,
    externalCustomerId,
    customerId,
    currencyId,
    rawPayload,
    normalizedPayload,
    syncJobId,
    ...customFields
  } = input;
  if (!externalOrderId) {
    return err(new ExternalOrderIdRequiredError(channelId));
  }
  const channel = await db
    .selectFrom("Channel")
    .selectAll()
    .where("id", "=", channelId)
    .executeTakeFirst();
  if (!channel) {
    return err(new ChannelNotFoundError(channelId));
  }
  if (channel.companyId !== companyId) {
    return err(new CrossCompanyReferenceError(channelId));
  }
  const existing = await db
    .selectFrom("ChannelOrder")
    .selectAll()
    .where("companyId", "=", companyId)
    .where("channelId", "=", channelId)
    .where("externalOrderId", "=", externalOrderId)
    .executeTakeFirst();

  if (existing) {
    const updated = await db
      .updateTable("ChannelOrder")
      .set({
        ...(customFields as Record<string, unknown>),
        externalCustomerId: externalCustomerId ?? existing.externalCustomerId,
        customerId: customerId ?? existing.customerId,
        currencyId: currencyId ?? existing.currencyId,
        rawPayload: rawPayload ? encodeJson(rawPayload) : existing.rawPayload,
        normalizedPayload: normalizedPayload
          ? encodeJson(normalizedPayload)
          : existing.normalizedPayload,
        syncJobId: syncJobId ?? existing.syncJobId,
        status:
          existing.status === "CANCELLED_BY_CHANNEL" || existing.salesOrderId
            ? existing.status
            : channelOrderLifecycle.transitions.record.to,
      })
      .where("id", "=", existing.id)
      .returningAll()
      .executeTakeFirstOrThrow();
    return ok({ channelOrder: updated });
  }

  const channelOrder = await db
    .insertInto("ChannelOrder")
    .values({
      ...(customFields as Record<string, unknown>),
      companyId,
      channelId,
      externalOrderId,
      externalCustomerId: externalCustomerId ?? null,
      customerId: customerId ?? null,
      salesOrderId: null,
      status: channelOrderLifecycle.transitions.record.to,
      orderDate: new Date(),
      currencyId: currencyId ?? null,
      rawPayload: rawPayload ? encodeJson(rawPayload) : null,
      normalizedPayload: normalizedPayload ? encodeJson(normalizedPayload) : null,
      errorSummary: null,
      retryContext: null,
      syncJobId: syncJobId ?? null,
    })
    .returningAll()
    .executeTakeFirstOrThrow();
  return ok({ channelOrder });
}
