import { DEFAULT_VIRAL_THRESHOLD } from './types.js';
import type { CommentSnapshot, DailyStat, ViralConfig } from './types.js';
export type { ViralConfig } from './types.js';

/** 떡상: a reel is viral if, within `windowHours` of upload, it has ≥minViews views
 *  OR ≥minComments comments (current cumulative values). Pure. */
export function isViral(
  uploadedAtIso: string,
  viewCount: number | null,
  commentCount: number,
  nowIso: string,
  cfg: ViralConfig,
): boolean {
  const ageHours = (new Date(nowIso).getTime() - new Date(uploadedAtIso).getTime()) / 3_600_000;
  if (ageHours > cfg.windowHours) return false;
  return (viewCount ?? 0) >= cfg.minViews || commentCount >= cfg.minComments;
}

/**
 * Velocity: a reel is "터진/급상승" when it GAINED at least `threshold` comments over
 * the tracked window. This is the spike signal the product is actually about —
 * distinct from isViral, which only measures the lifetime total.
 */
export function isSpiking(growth: number, threshold: number = DEFAULT_VIRAL_THRESHOLD): boolean {
  return growth >= threshold;
}

/** Shift a 'YYYY-MM-DD' date back by `days` (UTC, calendar-safe). */
function isoDateMinusDays(date: string, days: number): string {
  const d = new Date(`${date}T00:00:00Z`);
  d.setUTCDate(d.getUTCDate() - days);
  return d.toISOString().slice(0, 10);
}

/**
 * Cumulative comment growth over the last `windowDays`.
 * Baseline = latest snapshot on or before (anchor - windowDays); if history is
 * shorter than the window, falls back to the earliest snapshot.
 *
 * `asOfDate` anchors the window. Default = the reel's own latest snapshot date
 * (growth over its most recent tracked window). Pass today's date to anchor on
 * *now* instead: a reel not collected recently then scores 0 (its baseline and
 * latest collapse to the same old snapshot), so stale reels drop out of a
 * "지금 가장 빠르게 크는" ranking rather than showing a weeks-old spike as current.
 */
export function commentGrowth(stats: CommentSnapshot[], windowDays: number, asOfDate?: string): number {
  if (stats.length === 0) return 0;
  const sorted = [...stats].sort((a, b) => a.date.localeCompare(b.date));
  const latest = sorted[sorted.length - 1]!;
  const cutoff = isoDateMinusDays(asOfDate ?? latest.date, windowDays);

  let baseline = sorted[0]!;
  for (const s of sorted) {
    if (s.date <= cutoff) baseline = s;
    else break;
  }
  return latest.commentCount - baseline.commentCount;
}

/**
 * Growth of an arbitrary metric (likes/views) over `windowDays`, same baseline
 * rule as commentGrowth. Null points (hidden likes, image-post views) are skipped
 * so they don't poison the delta; 0 if nothing comparable.
 */
export function seriesGrowth(points: Array<{ date: string; value: number | null }>, windowDays: number): number {
  const pts = points.filter((p): p is { date: string; value: number } => p.value != null);
  if (pts.length === 0) return 0;
  pts.sort((a, b) => a.date.localeCompare(b.date));
  const latest = pts[pts.length - 1]!;
  const cutoff = isoDateMinusDays(latest.date, windowDays);
  let baseline = pts[0]!;
  for (const s of pts) {
    if (s.date <= cutoff) baseline = s;
    else break;
  }
  return latest.value - baseline.value;
}

/**
 * Fill commentDelta/followerDelta by diffing each day against the previous day.
 * The earliest snapshot keeps null deltas. Input is sorted by date ascending.
 */
export function fillDailyDeltas(stats: DailyStat[]): DailyStat[] {
  const sorted = [...stats].sort((a, b) => a.date.localeCompare(b.date));
  const diff = (a: number | null, b: number | null): number | null =>
    a !== null && b !== null ? a - b : null;
  return sorted.map((s, i) => {
    if (i === 0) return { ...s, commentDelta: null, likeDelta: null, viewDelta: null, followerDelta: null };
    const prev = sorted[i - 1]!;
    return {
      ...s,
      commentDelta: s.commentCount - prev.commentCount,
      likeDelta: diff(s.likeCount, prev.likeCount),
      viewDelta: diff(s.viewCount, prev.viewCount),
      followerDelta: diff(s.followerCount, prev.followerCount),
    };
  });
}
