import { existsSync, readFileSync } from "fs";
import { join } from "path";

export interface PayTypeMapping {
  rippling_name: string;
  amd_type_ids: number[];
  category: string;
  cpt_codes?: string[];
}

export interface ProviderRate {
  rippling_email: string;
  amd_column_heading: string;
  rates: Record<string, number>;
  tiered?: { threshold: number; tier1_pay_type: string; tier2_pay_type: string };
}

export interface PayMappingConfig {
  pay_types: PayTypeMapping[];
  providers: ProviderRate[];
  appt_status: { completed: number[]; noshow: number[]; excluded: number[] };
  pay_period: { cadence: string; delay_days: number };
}

let config: PayMappingConfig | null = null;

export function loadConfig(configPath?: string): PayMappingConfig {
  if (config) return config;
  const p = configPath ?? defaultConfigPath();
  config = JSON.parse(readFileSync(p, "utf-8"));
  return config!;
}

function defaultConfigPath(): string {
  const candidates = [
    join(process.cwd(), "config/rippling-pay-mapping.json"),
    join(process.cwd(), "../../config/rippling-pay-mapping.json"),
  ];
  const found = candidates.find((candidate) => existsSync(candidate));
  if (!found) throw new Error("Could not find config/rippling-pay-mapping.json from the current working directory.");
  return found;
}

export function classifyVisit(
  appointmentTypeId: number,
  appointmentTypeName: string,
  apptStatus: number,
  cfg: PayMappingConfig
): string | null {
  const results = classifyVisitFull(appointmentTypeId, appointmentTypeName, apptStatus, cfg);
  return results.length > 0 ? results[0] : null;
}

export function classifyVisitFull(
  appointmentTypeId: number,
  appointmentTypeName: string,
  apptStatus: number,
  cfg: PayMappingConfig,
  cptCodes?: string[]
): string[] {
  const isNoShow = cfg.appt_status.noshow.includes(apptStatus);
  const isCompleted = cfg.appt_status.completed.includes(apptStatus);
  if (!isNoShow && !isCompleted) return [];

  const results: string[] = [];

  const isPsych = cfg.pay_types
    .filter((pt) => pt.category.startsWith("psych_") && !pt.category.endsWith("noshow") && !pt.cpt_codes)
    .some((pt) => pt.amd_type_ids.includes(appointmentTypeId));

  const isTherapy = cfg.pay_types
    .filter((pt) => pt.category.startsWith("therapy_") && !pt.category.endsWith("noshow"))
    .some((pt) => pt.amd_type_ids.includes(appointmentTypeId));

  if (isNoShow) {
    if (isPsych) results.push("Psychiatry No-Show / Late Cancellation");
    if (isTherapy) results.push("Therapy No-Show / Late Cancellation");
    return results;
  }

  for (const pt of cfg.pay_types) {
    if (pt.category.endsWith("noshow")) continue;
    if (pt.cpt_codes) continue;
    if (pt.amd_type_ids.includes(appointmentTypeId)) {
      results.push(pt.rippling_name);
      break;
    }
  }

  if (cptCodes && isCompleted) {
    for (const pt of cfg.pay_types) {
      if (!pt.cpt_codes) continue;
      if (pt.cpt_codes.some((c) => cptCodes.includes(c))) {
        results.push(pt.rippling_name);
      }
    }
  }

  return results;
}

export interface WeekBucket {
  isoWeek: string;
  count: number;
}

export function calculateTiers(
  weeklyVisits: WeekBucket[],
  threshold: number
): { tier1: number; tier2: number } {
  let tier1 = 0;
  let tier2 = 0;
  for (const week of weeklyVisits) {
    if (week.count <= threshold) {
      tier1 += week.count;
    } else {
      tier1 += threshold;
      tier2 += week.count - threshold;
    }
  }
  return { tier1, tier2 };
}

export function getISOWeek(dateStr: string): string {
  const d = new Date(dateStr);
  d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7));
  const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
  const weekNo = Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
  return `${d.getUTCFullYear()}-W${String(weekNo).padStart(2, "0")}`;
}

export function findProvider(columnHeading: string, cfg: PayMappingConfig): ProviderRate | null {
  const normalized = columnHeading.replace(/\s*\(.*\)$/, "").trim().toUpperCase();
  return cfg.providers.find((p) =>
    p.amd_column_heading.toUpperCase() === normalized
  ) ?? null;
}
