/**
 * Rate card lookup logic for auto-filling demand line rates.
 *
 * Match priority (highest specificity wins):
 *   1. Exact client + chapter + role
 *   2. Client + chapter (any role)
 *   3. Client + role (any chapter)
 *   4. Client only (fallback)
 *   5. Default rate card (no client) + best match
 *
 * Within each priority tier, additional criteria (seniority, location,
 * workType) increase the score.
 */

export interface RateCardLookupParams {
  clientId?: string | null | undefined;
  chapter?: string | null | undefined;
  roleId?: string | null | undefined;
  seniority?: string | null | undefined;
  location?: string | null | undefined;
  workType?: string | null | undefined;
  effectiveDate?: Date | null | undefined;
}

export interface RateCardLookupResult {
  costRateCents: number;
  billRateCents: number;
  currency: string;
  rateCardId: string;
  rateCardLineId: string;
  rateCardName: string;
}

interface RateCardLineRow {
  id: string;
  rateCardId: string;
  roleId: string | null;
  chapter: string | null;
  location: string | null;
  seniority: string | null;
  workType: string | null;
  costRateCents: number;
  billRateCents: number | null;
  rateCard: {
    id: string;
    name: string;
    currency: string;
    clientId: string | null;
  };
}

const RATE_CARD_LINE_SELECT = {
  id: true,
  rateCardId: true,
  roleId: true,
  chapter: true,
  location: true,
  seniority: true,
  workType: true,
  costRateCents: true,
  billRateCents: true,
  rateCard: {
    select: {
      id: true,
      name: true,
      currency: true,
      clientId: true,
    },
  },
} as const;

function scoreLine(
  line: RateCardLineRow,
  params: RateCardLookupParams,
): { score: number; mismatch: boolean } {
  let score = 0;
  let mismatch = false;

  if (params.clientId && line.rateCard.clientId === params.clientId) {
    score += 100;
  } else if (params.clientId && line.rateCard.clientId != null) {
    mismatch = true;
  }

  if (params.roleId && line.roleId) {
    if (line.roleId === params.roleId) score += 16;
    else mismatch = true;
  }

  if (params.chapter && line.chapter) {
    if (line.chapter === params.chapter) score += 8;
    else mismatch = true;
  }

  if (params.seniority && line.seniority) {
    if (line.seniority === params.seniority) score += 4;
    else mismatch = true;
  }

  if (params.location && line.location) {
    if (line.location === params.location) score += 2;
    else mismatch = true;
  }

  if (params.workType && line.workType) {
    if (line.workType === params.workType) score += 1;
    else mismatch = true;
  }

  return { score, mismatch };
}

function findBestMatch(
  lines: RateCardLineRow[],
  params: RateCardLookupParams,
): RateCardLookupResult | null {
  if (lines.length === 0) return null;

  let bestLine: RateCardLineRow | null = null;
  let bestScore = -1;

  for (const line of lines) {
    const { score, mismatch } = scoreLine(line, params);
    if (!mismatch && score > bestScore) {
      bestScore = score;
      bestLine = line;
    }
  }

  if (!bestLine) return null;

  return {
    costRateCents: bestLine.costRateCents,
    billRateCents: bestLine.billRateCents ?? 0,
    currency: bestLine.rateCard.currency,
    rateCardId: bestLine.rateCard.id,
    rateCardLineId: bestLine.id,
    rateCardName: bestLine.rateCard.name,
  };
}

function buildRateCardWhere(
  clientId: string | null | undefined,
  effectiveDate: Date,
): Record<string, unknown> {
  const where: Record<string, unknown> = {
    isActive: true,
    OR: [{ effectiveFrom: null }, { effectiveFrom: { lte: effectiveDate } }],
    AND: [
      {
        OR: [{ effectiveTo: null }, { effectiveTo: { gte: effectiveDate } }],
      },
    ],
  };
  if (clientId) {
    where.clientId = { in: [clientId, null] };
  }
  return where;
}

/**
 * Look up the best-matching rate card line for a given set of criteria.
 * Returns null when no active rate card line matches.
 */
export async function lookupRate(
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  db: any,
  params: RateCardLookupParams,
): Promise<RateCardLookupResult | null> {
  const effectiveDate = params.effectiveDate ?? new Date();
  const lines = (await db.rateCardLine.findMany({
    where: { rateCard: buildRateCardWhere(params.clientId, effectiveDate) },
    select: RATE_CARD_LINE_SELECT,
  })) as RateCardLineRow[];

  return findBestMatch(lines, params);
}

/**
 * Batch-optimized rate lookup: loads rate card lines once, then scores
 * each param set against the cached lines. Use this when looking up rates
 * for multiple demand lines sharing the same clientId/effectiveDate.
 */
export async function lookupRatesBatch(
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  db: any,
  clientId: string | null | undefined,
  paramsList: RateCardLookupParams[],
): Promise<(RateCardLookupResult | null)[]> {
  if (paramsList.length === 0) return [];

  const effectiveDate = paramsList[0]?.effectiveDate ?? new Date();
  const lines = (await db.rateCardLine.findMany({
    where: { rateCard: buildRateCardWhere(clientId, effectiveDate) },
    select: RATE_CARD_LINE_SELECT,
  })) as RateCardLineRow[];

  return paramsList.map((params) => findBestMatch(lines, { ...params, clientId }));
}
