import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core";
import type { defineModule as defineOrganizationModule } from "../../organization";
import type { defineModule as definePrimitivesModule } from "../../primitives";
import type { BusinessPartnerType } from "../generated/enums";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  CompanyNotFoundError,
  InvalidNameError,
  CurrencyNotFoundError,
} from "../lib/errors.generated";

type OrganizationModule = ReturnType<typeof defineOrganizationModule>;
type OrganizationQueries = OrganizationModule["queries"];
type PrimitivesModule = ReturnType<typeof definePrimitivesModule>;
type PrimitivesQueries = PrimitivesModule["queries"];

export interface CreatePartnerInput {
  name: string;
  type: BusinessPartnerType;
  companyId: string;
  preferredCurrencyId?: string;
}

/**
 * Function: createPartner
 *
 * Creates a new business partner in DRAFT status.
 * Validates company existence, name, type, and optional currency.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: CreatePartnerInput & CF,
  ctx: CommandContext,
  organizationQueries: Pick<OrganizationQueries, "getCompany">,
  primitivesQueries: Pick<PrimitivesQueries, "getCurrency">,
) {
  const { name, type, companyId, preferredCurrencyId, ...customFields } = input;

  // 1. Validate name is non-empty
  if (!name || name.trim() === "") {
    return err(new InvalidNameError(String(name)));
  }

  // 2. Validate company exists
  const { company } = (await organizationQueries.getCompany(db, { id: companyId }, ctx)).value;
  if (!company) {
    return err(new CompanyNotFoundError(companyId));
  }

  // 3. Validate currency exists if provided
  if (preferredCurrencyId) {
    const { currency } = (await primitivesQueries.getCurrency(db, { id: preferredCurrencyId }, ctx))
      .value;
    if (!currency) {
      return err(new CurrencyNotFoundError(preferredCurrencyId));
    }
  }

  // 4. Create partner in DRAFT status
  const partner = await db
    .insertInto("BusinessPartner")
    .values({
      ...(customFields as Record<string, unknown>),
      name,
      type,
      companyId,
      preferredCurrencyId: preferredCurrencyId ?? null,
      status: "DRAFT",
    })
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ partner });
}
