/**
 * failOrderAndRestoreStock — transition an order to 'failed' and, for
 * cart-created orders, give back the SKU stock the checkout transaction took.
 *
 * Idempotent: stock restores ONLY on an actual pending|charging → failed
 * transition (order row locked FOR UPDATE), so the inline cart failure path
 * and the later payment_intent.canceled webhook can both call this safely.
 *
 * restoreStock MUST be true only for cart-created orders — the cart checkout
 * transaction is the only path that increments deal_skus.quantity_sold at
 * order-create time (single-item/ITEM paths count stock on success instead).
 */

import { eq, sql } from 'drizzle-orm';
import type { DrizzleClient, TxDrizzleClient } from '@/server/db/client.js';
import { order, orderLine, dealSkus } from '@/server/db/schema.js';
import { failOrder, type OrdersSchema } from '@platform-modules/commerce-orders';
import type { Transaction } from '@platform-modules/db';
import { refreshDealCache } from '@/server/domain/variants/cache.js';
import { recomputeOrderRefundStatus } from '@/server/db/queries/purchases.js';
import { asOrderId, toModuleRef } from '@/server/platform-seams/ids.js';

async function restoreCartOrderStock(tx: TxDrizzleClient, orderRef: string): Promise<void> {
  const lines = await tx
    .select({
      variantId: orderLine.variantId,
      qty: orderLine.qty,
      dealId: dealSkus.dealId,
    })
    .from(orderLine)
    .innerJoin(dealSkus, eq(dealSkus.id, orderLine.variantId))
    .where(eq(orderLine.orderId, orderRef));
  const qtyByVariant = new Map<string, number>();
  for (const line of lines) {
    qtyByVariant.set(line.variantId, (qtyByVariant.get(line.variantId) ?? 0) + line.qty);
  }
  const variantQtys = [...qtyByVariant.entries()];
  if (variantQtys.length > 0) {
    await tx.execute(sql`
      UPDATE deal_skus AS ds
      SET quantity_sold = GREATEST(ds.quantity_sold - v.qty, 0)
      FROM (
        VALUES ${sql.join(
          variantQtys.map(([variantId, qty]) => sql`(${variantId}::uuid, ${qty}::int)`),
          sql`, `,
        )}
      ) AS v(id, qty)
      WHERE ds.id = v.id
    `);
  }
  for (const dealId of [...new Set(lines.map((line) => line.dealId))]) {
    await refreshDealCache(tx, dealId);
  }
}

export async function settleCartCheckoutCompensatedOrder(
  tx: TxDrizzleClient,
  orderId: string,
): Promise<void> {
  const orderRef = toModuleRef(asOrderId(orderId));
  let [ord] = await tx
    .select({ status: order.status })
    .from(order)
    .where(eq(order.id, orderRef))
    .for('update');
  if (!ord) throw new Error(`compensated cart order not found: ${orderId}`);
  if (ord.status === 'paid') {
    await recomputeOrderRefundStatus(tx, orderRef);
    const [recomputed] = await tx
      .select({ status: order.status })
      .from(order)
      .where(eq(order.id, orderRef));
    if (!recomputed) throw new Error(`compensated cart order not found: ${orderId}`);
    ord = recomputed;
  }
  if (ord.status === 'failed') return;
  if (ord.status === 'pending' || ord.status === 'charging') {
    await failOrder(tx as unknown as Transaction<OrdersSchema>, orderRef, 'payment_failed');
  } else if (ord.status !== 'refunded') {
    throw new Error(`expected refunded order after cart compensation, got ${ord.status}`);
  }
  await restoreCartOrderStock(tx, orderRef);
}

export async function failOrderAndRestoreStock(
  db: DrizzleClient,
  orderId: string,
  reason: string,
  opts: { restoreStock: boolean },
): Promise<{ transitioned: boolean }> {
  const orderRef = toModuleRef(asOrderId(orderId));
  return (db as TxDrizzleClient).transaction(async (tx) => {
    const [ord] = await tx
      .select({ status: order.status })
      .from(order)
      .where(eq(order.id, orderRef))
      .for('update');
    if (!ord || (ord.status !== 'pending' && ord.status !== 'charging')) {
      return { transitioned: false };
    }

    await failOrder(tx as unknown as Transaction<OrdersSchema>, orderRef, reason);

    if (opts.restoreStock) {
      await restoreCartOrderStock(tx, orderRef);
    }

    return { transitioned: true };
  });
}
