/**
 * computeProductsCacheTTL
 *
 * Computes the Redis TTL (in milliseconds) for an opportunity's products
 * cache entry.
 *
 * Rules (highest priority first):
 *  1.  Won / Lost status → null (do not cache — products are final)
 *  2.  Opp not cacheable (closed > 30 days) → null
 *  3.  Updated within 3 days → PRODUCTS_TTL_HOT (45 s)
 *  4.  Everything else → PRODUCTS_TTL_LAZY (20 min)
 */

import { computeCacheTTL } from "./computeCacheTTL";

export const PRODUCTS_TTL_HOT = 45_000;       // 45 seconds
export const PRODUCTS_TTL_LAZY = 1_200_000;   // 20 minutes

/** CW Opportunity Status IDs that indicate a final won/lost state. */
export const WON_LOST_STATUS_IDS = new Set<number>([
  29, // Won
  53, // Lost
  59, // Canceled
]);

export interface ComputeProductsCacheTTLInput {
  statusCwId: number | null;
  closedFlag: boolean;
  closedDate: Date | null;
  expectedCloseDate: Date | null;
  lastUpdated: Date | null;
  /** Override "now" for deterministic tests. Defaults to `new Date()`. */
  now?: Date;
}

const DAY_MS = 24 * 60 * 60 * 1000;

export function computeProductsCacheTTL(
  input: ComputeProductsCacheTTLInput
): number | null {
  const now = input.now ?? new Date();

  // Rule 1 — Won/Lost status means products are final
  if (input.statusCwId !== null && WON_LOST_STATUS_IDS.has(input.statusCwId)) {
    return null;
  }

  // Rule 2 — If the parent opportunity wouldn't be cached, skip products too
  const oppTTL = computeCacheTTL({
    closedFlag: input.closedFlag,
    closedDate: input.closedDate,
    expectedCloseDate: input.expectedCloseDate,
    lastUpdated: input.lastUpdated,
    now,
  });
  if (oppTTL === null) {
    return null;
  }

  // Rule 3 — Recently updated → hot cache
  if (input.lastUpdated !== null) {
    const ageDays =
      Math.abs(now.getTime() - input.lastUpdated.getTime()) / DAY_MS;
    if (ageDays <= 3) {
      return PRODUCTS_TTL_HOT;
    }
  }

  // Rule 4 — Everything else → lazy cache
  return PRODUCTS_TTL_LAZY;
}
