/**
 * ORAC assessment trend buckets by creation date — aligned with Risk Assessment executor dashboard.
 */

/** @typedef {'week'|'month'|'quarter'|'year'} TrendGranularity */

export const TREND_GRANULARITY = {
  WEEK: "week",
  MONTH: "month",
  QUARTER: "quarter",
  YEAR: "year",
};

function parseRowDate(row) {
  const raw = row.created_at ?? row.video_capture_date;
  if (raw == null || raw === "") return null;
  const d = new Date(raw);
  return Number.isNaN(d.getTime()) ? null : d;
}

function startOfWeekMonday(d) {
  const x = new Date(d.getFullYear(), d.getMonth(), d.getDate());
  const day = x.getDay();
  const diff = day === 0 ? -6 : 1 - day;
  x.setDate(x.getDate() + diff);
  x.setHours(0, 0, 0, 0);
  return x;
}

function startOfMonth(d) {
  return new Date(d.getFullYear(), d.getMonth(), 1, 0, 0, 0, 0);
}

function startOfQuarter(d) {
  const q0 = Math.floor(d.getMonth() / 3) * 3;
  return new Date(d.getFullYear(), q0, 1, 0, 0, 0, 0);
}

function addMonths(date, delta) {
  return new Date(date.getFullYear(), date.getMonth() + delta, 1, 0, 0, 0, 0);
}

function addQuarters(date, delta) {
  return addMonths(date, delta * 3);
}

function formatQuarterLabel(quarterStart) {
  const y = quarterStart.getFullYear();
  const q = Math.floor(quarterStart.getMonth() / 3) + 1;
  return `Q${q} ${y}`;
}

function formatWeekLabel(weekStart) {
  const end = new Date(weekStart);
  end.setDate(end.getDate() + 6);
  const opts = { month: "short", day: "numeric" };
  return `${weekStart.toLocaleDateString(undefined, opts)} – ${end.toLocaleDateString(undefined, opts)}`;
}

function formatMonthLabel(monthStart) {
  return monthStart.toLocaleDateString(undefined, { month: "short", year: "numeric" });
}

function resolveCategory(row, categorizeFn, statusIdToCategory) {
  const id = row.assessment_status_id;
  if (statusIdToCategory != null && id != null && statusIdToCategory.has(Number(id))) {
    return statusIdToCategory.get(Number(id));
  }
  return categorizeFn(row);
}

function countInRange(list, rangeStart, rangeEnd, categorizeFn, statusIdToCategory) {
  let assigned = 0;
  let completed = 0;
  for (const row of list) {
    const d = parseRowDate(row);
    if (!d || d < rangeStart || d >= rangeEnd) continue;
    const cat = resolveCategory(row, categorizeFn, statusIdToCategory);
    if (cat === "done") completed += 1;
    else assigned += 1;
  }
  return { assigned, completed };
}

/**
 * @param {object[]} rows
 * @param {(row: object) => string} categorizeFn
 * @param {Map<number, string>|null|undefined} statusIdToCategory
 * @param {TrendGranularity} granularity
 * @param {{ periods?: number }} [options]
 */
export function buildTrendSeriesData(rows, categorizeFn, statusIdToCategory, granularity, options = {}) {
  const list = Array.isArray(rows) ? rows : [];
  const now = new Date();

  if (granularity === TREND_GRANULARITY.WEEK) {
    const n = Math.max(1, Math.min(24, options.periods ?? 8));
    const anchor = startOfWeekMonday(now);
    const out = [];
    for (let i = n - 1; i >= 0; i--) {
      const rangeStart = new Date(anchor);
      rangeStart.setDate(rangeStart.getDate() - i * 7);
      const rangeEnd = new Date(rangeStart);
      rangeEnd.setDate(rangeEnd.getDate() + 7);
      const label = formatWeekLabel(rangeStart);
      out.push({
        label,
        ...countInRange(list, rangeStart, rangeEnd, categorizeFn, statusIdToCategory),
      });
    }
    return out;
  }

  if (granularity === TREND_GRANULARITY.MONTH) {
    const n = Math.max(1, Math.min(36, options.periods ?? 12));
    const anchor = startOfMonth(now);
    const out = [];
    for (let i = n - 1; i >= 0; i--) {
      const rangeStart = addMonths(anchor, -(n - 1 - i));
      const rangeEnd = addMonths(anchor, -(n - 1 - i) + 1);
      const label = formatMonthLabel(rangeStart);
      out.push({
        label,
        ...countInRange(list, rangeStart, rangeEnd, categorizeFn, statusIdToCategory),
      });
    }
    return out;
  }

  if (granularity === TREND_GRANULARITY.QUARTER) {
    const n = Math.max(1, Math.min(20, options.periods ?? 8));
    const anchor = startOfQuarter(now);
    const out = [];
    for (let i = n - 1; i >= 0; i--) {
      const rangeStart = addQuarters(anchor, -(n - 1 - i));
      const rangeEnd = addQuarters(anchor, -(n - 1 - i) + 1);
      const label = formatQuarterLabel(rangeStart);
      out.push({
        label,
        ...countInRange(list, rangeStart, rangeEnd, categorizeFn, statusIdToCategory),
      });
    }
    return out;
  }

  if (granularity === TREND_GRANULARITY.YEAR) {
    const n = Math.max(1, Math.min(10, options.periods ?? 5));
    const currentYear = now.getFullYear();
    const out = [];
    for (let i = n - 1; i >= 0; i--) {
      const year = currentYear - (n - 1 - i);
      const rangeStart = new Date(year, 0, 1, 0, 0, 0, 0);
      const rangeEnd = new Date(year + 1, 0, 1, 0, 0, 0, 0);
      out.push({
        label: String(year),
        ...countInRange(list, rangeStart, rangeEnd, categorizeFn, statusIdToCategory),
      });
    }
    return out;
  }

  return buildTrendSeriesData(rows, categorizeFn, statusIdToCategory, TREND_GRANULARITY.WEEK, options);
}
