/* eslint-disable @typescript-eslint/array-type, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return */
export type PurchaseOrderLifecycleStatus =
  | "DRAFT"
  | "SUBMITTED"
  | "ORDERED"
  | "PARTIALLY_RECEIVED"
  | "RECEIVED"
  | "PARTIALLY_BILLED"
  | "BILLED"
  | "CANCELLED"
  | "CLOSED";

export interface DbLike {
  selectFrom(table: any): any;
  updateTable(table: any): any;
  insertInto(table: any): any;
  deleteFrom(table: any): any;
}

export interface PaymentTermScheduleLineInput {
  kind?: "NET_DAYS" | "END_OF_MONTH" | "FIXED_DAY" | "INSTALLMENT";
  days?: number;
  fixedDayOfMonth?: number;
  percentage?: number;
  fixedAmount?: number;
  discountPercentage?: number;
  discountDays?: number;
  discountBasis?: "GROSS_AMOUNT" | "NET_AMOUNT";
  note?: string;
}

export interface PaymentTermSnapshot {
  id?: string;
  name: string;
  lines: PaymentTermScheduleLineInput[];
}

export interface PurchaseOrderLineInput {
  id?: string;
  itemId: string;
  description?: string;
  quantity: number;
  unitPrice: number;
  unitId?: string | null;
  receivingSiteId?: string | null;
  requiresPhysicalReceipt?: boolean;
  matchedPriceRuleId?: string | null;
  priceSnapshot?: Record<string, unknown> | null;
  receivedQuantity?: number;
  billedQuantity?: number;
}

export interface GoodsReceiptLineInput {
  id?: string;
  purchaseOrderLineId: string;
  quantity: number;
  itemId?: string;
  unitId?: string | null;
  requiresInventoryHandoff?: boolean;
  destinationSite?: string | null;
  destinationStorageLocation?: string | null;
}

export interface InboundReceiptHandoff {
  purchaseOrderId: string;
  purchaseOrderLineId: string;
  goodsReceiptId: string;
  quantity: number;
  unitId?: string | null;
  receiptDate: Date;
  destinationSite?: string | null;
  destinationStorageLocation?: string | null;
}

export interface PurchaseBillLineInput {
  id?: string;
  purchaseOrderLineId: string;
  quantity: number;
  billedUnitPrice?: number;
  taxCode?: string;
  taxableAmount?: number;
  taxAmount?: number;
}

export interface DueScheduleLine {
  dueDate: Date;
  amount: number;
  discountDueDate?: Date | null;
  discountAmount?: number | null;
}

export interface PriceCalculationInput {
  companyId: string;
  supplierId: string;
  itemId: string;
  unitId: string;
  currencyId: string;
  quantity: number;
  pricingDate?: Date;
}

export interface PriceSuggestion {
  ruleId: string;
  priceListId: string;
  unitPrice: number;
}

function asDate(value: Date | string | null | undefined): Date | null {
  if (!value) return null;
  return value instanceof Date ? value : new Date(value);
}

export function decodeJson<T>(value: unknown, fallback: T): T {
  if (value === null || value === undefined) {
    return fallback;
  }
  if (typeof value === "string") {
    return JSON.parse(value) as T;
  }
  return value as T;
}

export function encodeJson(value: unknown): string {
  return JSON.stringify(value);
}

export function nextEntityId(prefix: string, index = 1) {
  return `${prefix}-${index}`;
}

export function normalizeOrderLines(lines: PurchaseOrderLineInput[]) {
  return lines.map((line, index) => ({
    id: line.id ?? nextEntityId("po-line", index + 1),
    description: line.description ?? null,
    itemId: line.itemId,
    quantity: line.quantity,
    unitPrice: line.unitPrice,
    unitId: line.unitId ?? null,
    receivingSiteId: line.receivingSiteId ?? null,
    requiresPhysicalReceipt: line.requiresPhysicalReceipt ?? true,
    matchedPriceRuleId: line.matchedPriceRuleId ?? null,
    priceSnapshot: line.priceSnapshot ?? null,
    receivedQuantity: line.receivedQuantity ?? 0,
    billedQuantity: line.billedQuantity ?? 0,
  }));
}

export function normalizeReceiptLines(lines: GoodsReceiptLineInput[]) {
  return lines.map((line, index) => ({
    id: line.id ?? nextEntityId("gr-line", index + 1),
    purchaseOrderLineId: line.purchaseOrderLineId,
    quantity: line.quantity,
    itemId: line.itemId ?? null,
    unitId: line.unitId ?? null,
    requiresInventoryHandoff: line.requiresInventoryHandoff ?? true,
    destinationSite: line.destinationSite ?? null,
    destinationStorageLocation: line.destinationStorageLocation ?? null,
  }));
}

export function normalizeBillLines(lines: PurchaseBillLineInput[]) {
  return lines.map((line, index) => ({
    id: line.id ?? nextEntityId("bill-line", index + 1),
    purchaseOrderLineId: line.purchaseOrderLineId,
    quantity: line.quantity,
    billedUnitPrice: line.billedUnitPrice ?? null,
    taxCode: line.taxCode ?? null,
    taxableAmount: line.taxableAmount ?? null,
    taxAmount: line.taxAmount ?? null,
  }));
}

export function findOrderLine(orders: Array<Record<string, unknown>>, purchaseOrderLineId: string) {
  for (const order of orders) {
    const lines = decodeJson<PurchaseOrderLineInput[]>(order.lines, []);
    const line = lines.find((candidate) => candidate.id === purchaseOrderLineId);
    if (line) {
      return { order, line, lines };
    }
  }
  return null;
}

export function summarizeOrderProgress(lines: PurchaseOrderLineInput[]) {
  let totalOrdered = 0;
  let totalReceived = 0;
  let totalBilled = 0;

  for (const line of lines) {
    totalOrdered += line.quantity;
    totalReceived += line.receivedQuantity ?? 0;
    totalBilled += line.billedQuantity ?? 0;
  }

  return { totalOrdered, totalReceived, totalBilled };
}

export function derivePurchaseOrderStatus(
  currentStatus: string,
  lines: PurchaseOrderLineInput[],
): PurchaseOrderLifecycleStatus {
  if (currentStatus === "CANCELLED" || currentStatus === "CLOSED") {
    return currentStatus as PurchaseOrderLifecycleStatus;
  }

  const { totalOrdered, totalReceived, totalBilled } = summarizeOrderProgress(lines);
  if (totalOrdered === 0) return currentStatus as PurchaseOrderLifecycleStatus;

  if (totalBilled >= totalOrdered) {
    return "BILLED";
  }
  if (totalBilled > 0) {
    return "PARTIALLY_BILLED";
  }
  if (totalReceived >= totalOrdered) {
    return "RECEIVED";
  }
  if (totalReceived > 0) {
    return "PARTIALLY_RECEIVED";
  }

  return currentStatus as PurchaseOrderLifecycleStatus;
}

export function validateScheduleLines(lines: PaymentTermScheduleLineInput[]) {
  if (lines.length === 0) return "EMPTY_SCHEDULE";

  let percentageTotal = 0;
  let maxDueDays = 0;

  for (const line of lines) {
    if ((line.percentage ?? 0) < 0 || (line.fixedAmount ?? 0) < 0) {
      return "INVALID_SCHEDULE_LINE";
    }
    if (line.percentage !== undefined) {
      percentageTotal += line.percentage;
    }
    maxDueDays = Math.max(maxDueDays, line.days ?? 0);
    if (line.discountPercentage !== undefined) {
      if (!line.discountBasis || line.discountDays === undefined) {
        return "INVALID_DISCOUNT_CONFIGURATION";
      }
      if (line.discountDays > (line.days ?? maxDueDays)) {
        return "INVALID_DISCOUNT_CONFIGURATION";
      }
    }
  }

  const hasInstallments = lines.some((line) => line.percentage !== undefined);
  if (hasInstallments && Math.abs(percentageTotal - 100) > 0.0001) {
    return "INVALID_INSTALLMENT_TOTAL";
  }

  return null;
}

export function calculateDueSchedule(
  scheduleLines: PaymentTermScheduleLineInput[],
  billDate: Date,
  billAmount: number,
) {
  if (billAmount < 0) {
    return { error: "INVALID_BILL_AMOUNT" as const };
  }

  const scheduleError = validateScheduleLines(scheduleLines);
  if (scheduleError) {
    return { error: "INVALID_PAYMENT_TERM_CONFIGURATION" as const };
  }

  const normalized = scheduleLines.map((line) => ({
    ...line,
    amount:
      line.fixedAmount ??
      (line.percentage !== undefined
        ? Number(((billAmount * line.percentage) / 100).toFixed(2))
        : 0),
  }));

  const assigned = normalized.reduce((sum, line) => sum + line.amount, 0);
  const residual = Number((billAmount - assigned).toFixed(2));
  if (normalized.length > 0) {
    normalized[normalized.length - 1]!.amount = Number(
      (normalized[normalized.length - 1]!.amount + residual).toFixed(2),
    );
  }

  return {
    schedule: normalized.map((line) => {
      const dueDate = new Date(billDate);
      if (line.kind === "END_OF_MONTH") {
        dueDate.setMonth(dueDate.getMonth() + 1, 0);
      } else if (line.kind === "FIXED_DAY" && line.fixedDayOfMonth) {
        dueDate.setDate(line.fixedDayOfMonth);
      } else {
        dueDate.setDate(dueDate.getDate() + (line.days ?? 0));
      }

      const discountDueDate =
        line.discountDays !== undefined
          ? new Date(billDate.getTime() + line.discountDays * 24 * 60 * 60 * 1000)
          : null;

      return {
        dueDate,
        amount: Number(line.amount.toFixed(2)),
        discountDueDate,
        discountAmount:
          line.discountPercentage !== undefined
            ? Number(((line.amount * line.discountPercentage) / 100).toFixed(2))
            : null,
      } satisfies DueScheduleLine;
    }),
  };
}

export function buildPaymentTermSnapshot(term: { id?: string; name: string; scheduleLines: any }) {
  return {
    id: term.id,
    name: term.name,
    lines: decodeJson<PaymentTermScheduleLineInput[]>(term.scheduleLines, []),
  } satisfies PaymentTermSnapshot;
}

export function isActiveStatus(value: string | null | undefined) {
  return value === "ACTIVE";
}

export function isSupplierRole(value: string | null | undefined) {
  return value === "SUPPLIER";
}

export async function selectOne(
  db: DbLike,
  table: string,
  column: string,
  value: string,
  lock = false,
) {
  const builder = db.selectFrom(table).selectAll().where(column, "=", value);
  const query = lock ? builder.forUpdate() : builder;
  return (await query.executeTakeFirst()) as any;
}

export async function selectMany(db: DbLike, table: string, column: string, value: string) {
  return (await db.selectFrom(table).selectAll().where(column, "=", value).execute()) as any[];
}

export async function getSupplierRole(db: DbLike, supplierId: string) {
  return (await db
    .selectFrom("PartnerRole")
    .selectAll()
    .where("partnerId", "=", supplierId)
    .where("role", "=", "SUPPLIER")
    .executeTakeFirst()) as any;
}

export async function getDefaultPartnerIdentification(db: DbLike, supplierId: string) {
  return (await db
    .selectFrom("PartnerIdentification")
    .selectAll()
    .where("partnerId", "=", supplierId)
    .executeTakeFirst()) as any;
}

export async function getSupplierProfileByCompanyAndSupplier(
  db: DbLike,
  companyId: string,
  supplierId: string,
  lock = false,
) {
  const builder = db
    .selectFrom("SupplierProfile")
    .selectAll()
    .where("companyId", "=", companyId)
    .where("supplierId", "=", supplierId);
  const query = lock ? builder.forUpdate() : builder;
  return (await query.executeTakeFirst()) as any;
}

export async function findPurchaseBillDuplicate(
  db: DbLike,
  companyId: string,
  supplierId: string,
  invoiceNumber: string,
) {
  return (await db
    .selectFrom("PurchaseBill")
    .selectAll()
    .where("companyId", "=", companyId)
    .where("supplierId", "=", supplierId)
    .where("invoiceNumber", "=", invoiceNumber)
    .executeTakeFirst()) as any;
}

export async function findPurchaseOrderByExternalReference(
  db: DbLike,
  companyId: string,
  externalSupplierOrderReference: string,
) {
  return (await db
    .selectFrom("PurchaseOrder")
    .selectAll()
    .where("companyId", "=", companyId)
    .where("externalSupplierOrderReference", "=", externalSupplierOrderReference)
    .executeTakeFirst()) as any;
}

export async function findRuleConflict(
  db: DbLike,
  params: {
    itemId: string;
    currencyId: string;
    unitId: string;
    minimumQuantity: number;
    effectiveFrom: Date;
  },
) {
  return (await db
    .selectFrom("PurchasePriceRule")
    .selectAll()
    .where("itemId", "=", params.itemId)
    .where("currencyId", "=", params.currencyId)
    .where("unitId", "=", params.unitId)
    .where("minimumQuantity", "=", params.minimumQuantity)
    .where("effectiveFrom", "=", params.effectiveFrom)
    .executeTakeFirst()) as any;
}

export function updateOrderLineQuantities(
  lines: PurchaseOrderLineInput[],
  quantities: Map<string, number>,
  field: "receivedQuantity" | "billedQuantity",
) {
  return lines.map((line) => ({
    ...line,
    [field]: (line[field] ?? 0) + (quantities.get(line.id ?? "") ?? 0),
  }));
}

export function totalReceiptQuantityForOrderLine(
  receipts: Array<Record<string, unknown>>,
  purchaseOrderLineId: string,
) {
  return receipts
    .flatMap((receipt) => decodeJson<GoodsReceiptLineInput[]>(receipt.lines, []))
    .filter((line) => line.purchaseOrderLineId === purchaseOrderLineId)
    .reduce((sum, line) => sum + line.quantity, 0);
}

export function priceVarianceExceeded(
  billedUnitPrice: number,
  orderedUnitPrice: number,
  toleranceRatio = 0.05,
) {
  if (orderedUnitPrice <= 0) return billedUnitPrice > 0;
  return billedUnitPrice > orderedUnitPrice * (1 + toleranceRatio);
}

export function effectiveDateInRange(date: Date, from: Date | string, to?: Date | string | null) {
  const start = asDate(from);
  const end = asDate(to);
  if (!start) return false;
  if (date < start) return false;
  if (end && date > end) return false;
  return true;
}
