/**
 * cart-resolver.ts — resolves a user's active cart into a ResolvedCart.
 *
 * Used at preview (zero DB writes) and at reserve/finalize paths.
 * Handles the fact that:
 *   - deals.categoryId is a single FK (not a join table)
 *   - deal ↔ tag is many-to-many via dealTagAssignments
 *   - cart_line.qty is the quantity column name
 *   - price source: cart_line.amount (stored at add-time, agorot)
 *   - join path: cart_line.variant_id → dealSkus.id → deals
 */

import { inArray, sql } from 'drizzle-orm';
import type { DrizzleClient } from '@/server/db/client.js';
import { dealTagAssignments } from '@/server/db/schema.js';
import { applyQtyTier } from '@/server/pricing/qty-tier.js';
import { getQtyTiersForSkus } from '@/server/db/queries/sku-qty-tiers.js';
import type { ResolvedCart, ResolvedCartLine } from './types.js';

/** Rewrites each line's lineSubtotalAgorot with its qty tier, then re-sums totalAgorot. */
export function applyTiersToLines(
  cart: Pick<ResolvedCart, 'userId'> & { lines: ResolvedCartLine[] },
): { lines: ResolvedCartLine[]; totalAgorot: number } {
  const lines = cart.lines.map((l) => {
    const { lineTotalAgorot } = applyQtyTier(l.unitPriceAgorot, l.quantity, l.qtyTiers);
    return { ...l, lineSubtotalAgorot: lineTotalAgorot };
  });
  return { lines, totalAgorot: lines.reduce((s, l) => s + l.lineSubtotalAgorot, 0) };
}

type CartResolverSqlRow = {
  id: string;
  deal_sku_id: string;
  qty: number;
  amount: string | bigint;
  deal_id: string;
  vendor_id: string;
  category_id: string | null;
};

export async function resolveCartForUser(db: DrizzleClient, userId: string): Promise<ResolvedCart> {
  const cartResult = await db.execute(sql`
    SELECT id FROM cart WHERE user_id = ${userId} LIMIT 1
  `);
  const cartRow = (cartResult as unknown as { rows: { id: string }[] }).rows[0];
  if (!cartRow) {
    return { userId, lines: [], totalAgorot: 0 };
  }
  const cartId = cartRow.id;

  const result = await db.execute(sql`
    SELECT cl.id, cl.variant_id AS deal_sku_id, cl.qty, cl.amount, dsk.deal_id, d.vendor_id, d.category_id
    FROM cart_line cl
    INNER JOIN deal_skus dsk ON dsk.id = cl.variant_id
    INNER JOIN deals d ON d.id = dsk.deal_id
    WHERE cl.cart_id = ${cartId}
  `);

  const rows = (result as unknown as { rows: CartResolverSqlRow[] }).rows;

  if (rows.length === 0) {
    return { userId, lines: [], totalAgorot: 0 };
  }

  const dealIds = rows.map((r) => r.deal_id);

  // Fetch tag assignments for all deals in one query
  const tagRows =
    dealIds.length > 0
      ? await db
          .select({ dealId: dealTagAssignments.dealId, tagId: dealTagAssignments.tagId })
          .from(dealTagAssignments)
          .where(inArray(dealTagAssignments.dealId, dealIds))
      : [];

  // Build tag map: dealId → tagId[]
  const tagByDeal = new Map<string, string[]>();
  for (const row of tagRows) {
    const existing = tagByDeal.get(row.dealId);
    if (existing) {
      existing.push(row.tagId);
    } else {
      tagByDeal.set(row.dealId, [row.tagId]);
    }
  }

  const validRows = rows.filter(
    (r): r is typeof r & { deal_sku_id: string } => r.deal_sku_id !== null,
  );
  const tierMap = await getQtyTiersForSkus(db, validRows.map((r) => r.deal_sku_id));

  const lines: ResolvedCartLine[] = validRows.map((r) => {
    const unitPriceAgorot = Number(r.amount);
    const qty = r.qty;
    return {
      lineId: r.id,
      dealId: r.deal_id,
      vendorId: r.vendor_id,
      // categories: single FK on deals.categoryId
      categoryIds: r.category_id ? [r.category_id] : [],
      tagIds: tagByDeal.get(r.deal_id) ?? [],
      quantity: qty,
      unitPriceAgorot,
      lineSubtotalAgorot: unitPriceAgorot * qty,
      qtyTiers: tierMap.get(r.deal_sku_id) ?? [],
    };
  });

  const tiered = applyTiersToLines({ userId, lines });
  return {
    userId,
    lines: tiered.lines,
    totalAgorot: tiered.totalAgorot,
  };
}
