import type { SocialChannel, SocialInsight, SocialSnapshot } from "@/lib/marketing/types";

type DailyMetric = {
  channel: SocialChannel;
  metricDate: string;
  followers: number;
  reach: number;
  impressions: number;
  engagements: number;
  engagementRate?: number;
};

function round(value: number): number {
  return Math.round(value * 100) / 100;
}

function startOfDay(date: Date): number {
  return new Date(`${date.toISOString().slice(0, 10)}T00:00:00.000Z`).getTime();
}

function toIsoDay(date: Date): string {
  return date.toISOString().slice(0, 10);
}

function estimateInteractions(impressions: number, engagementRate: number): number {
  return Math.max(0, Math.round((impressions * engagementRate) / 100));
}

const DAY_MS = 24 * 60 * 60 * 1000;

function dayDiff(currentCapturedAt: string, previousCapturedAt: string): number | null {
  const current = new Date(currentCapturedAt).getTime();
  const previous = new Date(previousCapturedAt).getTime();
  if (!Number.isFinite(current) || !Number.isFinite(previous)) {
    return null;
  }

  const diffDays = (startOfDay(new Date(current)) - startOfDay(new Date(previous))) / DAY_MS;
  if (diffDays <= 0) {
    return null;
  }

  return diffDays;
}

function comparisonWindowDays(range: "7d" | "30d"): number {
  return range === "30d" ? 30 : 7;
}

function findComparablePreviousSnapshot(current: SocialSnapshot, candidates: SocialSnapshot[], range: "7d" | "30d"): SocialSnapshot | null {
  const targetDiffDays = comparisonWindowDays(range);
  const minDiffDays = Math.max(1, targetDiffDays - 2);
  const maxDiffDays = targetDiffDays + 2;
  let selected: SocialSnapshot | null = null;
  let selectedDistance = Number.POSITIVE_INFINITY;

  for (const candidate of candidates) {
    const diffDays = dayDiff(current.capturedAt, candidate.capturedAt);
    if (diffDays === null || diffDays < minDiffDays || diffDays > maxDiffDays) {
      continue;
    }

    const distance = Math.abs(diffDays - targetDiffDays);
    if (distance < selectedDistance) {
      selected = candidate;
      selectedDistance = distance;
    }
  }

  return selected;
}

export function followerGrowthPct(followersStart: number, followersEnd: number): number {
  if (followersStart <= 0) {
    return 0;
  }

  return round(((followersEnd - followersStart) / followersStart) * 100);
}

export function buildInsightsFromSnapshots(snapshots: SocialSnapshot[]): SocialInsight[] {
  const grouped = new Map<string, SocialSnapshot[]>();

  for (const snapshot of snapshots) {
    const key = `${snapshot.channel}:${snapshot.range}`;
    const list = grouped.get(key) ?? [];
    list.push(snapshot);
    grouped.set(key, list);
  }

  const insights: SocialInsight[] = [];
  for (const [key, list] of grouped.entries()) {
    const [channel, range] = key.split(":") as [SocialChannel, "7d" | "30d"];
    const sorted = [...list].sort((a, b) => new Date(b.capturedAt).getTime() - new Date(a.capturedAt).getTime());
    const current = sorted[0];
    if (!current) {
      continue;
    }

    const previous = findComparablePreviousSnapshot(current, sorted.slice(1), range);
    const followerDelta = current.followersEnd - current.followersStart;
    const interactions = estimateInteractions(current.impressions, current.engagementRate);
    const previousInteractions = previous ? estimateInteractions(previous.impressions, previous.engagementRate) : null;

    insights.push({
      channel,
      range,
      followersStart: current.followersStart,
      followersEnd: current.followersEnd,
      followerDelta,
      followerGrowthPct: followerGrowthPct(current.followersStart, current.followersEnd),
      engagementRatePct: round(current.engagementRate),
      interactions,
      reach: current.reach,
      impressions: current.impressions,
      previousReach: previous ? previous.reach : null,
      previousImpressions: previous ? previous.impressions : null,
      previousInteractions,
      previousFollowerDelta: previous ? previous.followersEnd - previous.followersStart : null,
      previousGrowthPct: previous ? followerGrowthPct(previous.followersStart, previous.followersEnd) : null,
      previousEngagementRatePct: previous ? round(previous.engagementRate) : null,
    });
  }

  return insights.sort((a, b) => a.channel.localeCompare(b.channel) || a.range.localeCompare(b.range));
}

function aggregateWindowMetrics(records: DailyMetric[], windowDays: number, referenceDate: Date) {
  const today = startOfDay(referenceDate);
  const start = today - (windowDays - 1) * 24 * 60 * 60 * 1000;
  const scoped = records
    .filter((record) => {
      const metricAt = startOfDay(new Date(`${record.metricDate}T00:00:00.000Z`));
      return metricAt >= start && metricAt <= today;
    })
    .sort((a, b) => new Date(a.metricDate).getTime() - new Date(b.metricDate).getTime());

  if (scoped.length === 0) {
    return null;
  }

  const first = scoped[0];
  const last = scoped[scoped.length - 1];
  const avgEngagementRate =
    scoped.reduce((acc, item) => acc + (item.engagementRate ?? (item.impressions > 0 ? (item.engagements / item.impressions) * 100 : 0)), 0) /
    scoped.length;

  return {
    followersStart: first.followers,
    followersEnd: last.followers,
    engagementRate: round(avgEngagementRate),
    reach: scoped.reduce((acc, item) => acc + item.reach, 0),
    impressions: scoped.reduce((acc, item) => acc + item.impressions, 0),
  };
}

export function rollupSnapshotsFromDailyMetrics(records: DailyMetric[], referenceDate = new Date()): Array<{
  channel: SocialChannel;
  range: "7d" | "30d";
  followersStart: number;
  followersEnd: number;
  engagementRate: number;
  reach: number;
  impressions: number;
  capturedAt: string;
}> {
  const grouped = new Map<SocialChannel, DailyMetric[]>();
  for (const record of records) {
    const list = grouped.get(record.channel) ?? [];
    list.push(record);
    grouped.set(record.channel, list);
  }

  const snapshots: Array<{
    channel: SocialChannel;
    range: "7d" | "30d";
    followersStart: number;
    followersEnd: number;
    engagementRate: number;
    reach: number;
    impressions: number;
    capturedAt: string;
  }> = [];

  for (const [channel, rows] of grouped.entries()) {
    const short = aggregateWindowMetrics(rows, 7, referenceDate);
    const long = aggregateWindowMetrics(rows, 30, referenceDate);

    if (short) {
      snapshots.push({
        channel,
        range: "7d",
        ...short,
        capturedAt: toIsoDay(referenceDate),
      });
    }

    if (long) {
      snapshots.push({
        channel,
        range: "30d",
        ...long,
        capturedAt: toIsoDay(referenceDate),
      });
    }
  }

  return snapshots;
}
