import { CURRENT_DATE } from "@/lib/constants";
import { getCompetitors, getCuvees, getMarkets, getReviews, getSources } from "@/lib/data";
import { sourceLabels } from "@/lib/labels";
import type { Market, PeriodPreset, Review, Sentiment, Source } from "@/lib/types";
import { clamp, percent, sentimentToScore, stableHash } from "@/lib/utils";

export type ReviewFilters = {
  cuveeIds?: string[];
  markets?: Market[];
  sources?: Source[];
  authorTypes?: Review["authorType"][];
  vintageRange?: [number, number];
  period?: { from: string; to: string };
};

export function getPeriodRange(preset: PeriodPreset = "90d") {
  const to = new Date(CURRENT_DATE);
  const from = new Date(CURRENT_DATE);
  const ranges: Record<PeriodPreset, number> = {
    "7d": 7,
    "30d": 30,
    "90d": 90,
    "6m": 183,
    "12m": 365,
    "24m": 730
  };
  from.setDate(from.getDate() - ranges[preset]);
  return { from: from.toISOString().slice(0, 10), to: to.toISOString().slice(0, 10) };
}

export function filterReviews(reviews: Review[], filters: ReviewFilters = {}) {
  return reviews.filter((review) => {
    const reviewTime = new Date(review.date).getTime();
    if (filters.period) {
      const from = new Date(filters.period.from).getTime();
      const to = new Date(filters.period.to).getTime();
      if (reviewTime < from || reviewTime > to) return false;
    }
    if (filters.cuveeIds?.length && !filters.cuveeIds.includes(review.cuveeId)) return false;
    if (filters.markets?.length && !filters.markets.includes(review.market)) return false;
    if (filters.sources?.length && !filters.sources.includes(review.source)) return false;
    if (filters.authorTypes?.length && !filters.authorTypes.includes(review.authorType)) return false;
    if (filters.vintageRange && (review.vintage < filters.vintageRange[0] || review.vintage > filters.vintageRange[1])) return false;
    return true;
  });
}

export function average(values: number[]) {
  if (values.length === 0) return 0;
  return values.reduce((sum, value) => sum + value, 0) / values.length;
}

export function averageSentiment(reviews: Review[]) {
  return average(reviews.map((review) => review.sentimentScore));
}

export function averageRating(reviews: Review[]) {
  return average(reviews.map((review) => review.rating ?? 0).filter(Boolean));
}

export function getSentimentDistribution(reviews: Review[]) {
  const counts: Record<Sentiment, number> = { positive: 0, neutral: 0, negative: 0 };
  reviews.forEach((review) => {
    counts[review.sentiment] += 1;
  });
  const total = reviews.length;
  return {
    counts,
    percentages: {
      positive: percent(counts.positive, total),
      neutral: percent(counts.neutral, total),
      negative: percent(counts.negative, total)
    }
  };
}

export function getTopDescriptors(reviews: Review[], limit = 20) {
  const map = new Map<string, { term: string; count: number; sentimentTotal: number; sparkline: number[] }>();
  reviews.forEach((review) => {
    review.descriptors.forEach((term) => {
      const current = map.get(term) ?? { term, count: 0, sentimentTotal: 0, sparkline: Array.from({ length: 8 }, () => 0) };
      current.count += 1;
      current.sentimentTotal += review.sentimentScore;
      current.sparkline[stableHash(`${term}-${review.date}`) % current.sparkline.length] += 1;
      map.set(term, current);
    });
  });
  return [...map.values()]
    .map((item) => ({
      term: item.term,
      count: item.count,
      sentiment: item.sentimentTotal / item.count,
      sparkline: item.sparkline
    }))
    .sort((a, b) => b.count - a.count)
    .slice(0, limit);
}

export function getEmergingTerms(reviews: Review[], limit = 6) {
  const range = getPeriodRange("24m");
  const midpoint = new Date(range.from);
  midpoint.setDate(midpoint.getDate() + 365);
  const before = reviews.filter((review) => new Date(review.date) < midpoint);
  const after = reviews.filter((review) => new Date(review.date) >= midpoint);
  const beforeMap = descriptorCount(before);
  const afterMap = descriptorCount(after);
  return [...afterMap.entries()]
    .map(([term, count]) => {
      const previous = beforeMap.get(term) ?? 1;
      return { term, growthPct: Math.round(((count - previous) / previous) * 100), count };
    })
    .filter((item) => item.count >= 5)
    .sort((a, b) => b.growthPct - a.growthPct)
    .slice(0, limit);
}

export function getDecliningTerms(reviews: Review[], limit = 6) {
  const range = getPeriodRange("24m");
  const midpoint = new Date(range.from);
  midpoint.setDate(midpoint.getDate() + 365);
  const before = reviews.filter((review) => new Date(review.date) < midpoint);
  const after = reviews.filter((review) => new Date(review.date) >= midpoint);
  const beforeMap = descriptorCount(before);
  const afterMap = descriptorCount(after);
  return [...beforeMap.entries()]
    .map(([term, count]) => {
      const current = afterMap.get(term) ?? 1;
      return { term, growthPct: -Math.round(((count - current) / count) * 100), count: current };
    })
    .filter((item) => item.count >= 1)
    .sort((a, b) => a.growthPct - b.growthPct)
    .slice(0, limit);
}

function descriptorCount(reviews: Review[]) {
  const map = new Map<string, number>();
  reviews.forEach((review) => {
    review.descriptors.forEach((term) => {
      map.set(term, (map.get(term) ?? 0) + 1);
    });
  });
  return map;
}

export function getMarketStats(reviews: Review[]) {
  return getMarkets()
    .map((market) => {
      const marketReviews = reviews.filter((review) => review.market === market.code);
      const cuveeCounts = new Map<string, number>();
      marketReviews.forEach((review) => {
        cuveeCounts.set(review.cuveeId, (cuveeCounts.get(review.cuveeId) ?? 0) + 1);
      });
      const topCuveeId = [...cuveeCounts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? "chevalier-rouge";
      return {
        ...market,
        mentions: marketReviews.length,
        sentiment: averageSentiment(marketReviews),
        rating: averageRating(marketReviews),
        topCuvee: getCuvees().find((cuvee) => cuvee.id === topCuveeId)?.name ?? "Domaine de Chevalier"
      };
    })
    .filter((market) => market.code !== "OTHER")
    .sort((a, b) => b.mentions - a.mentions);
}

export function getMonthlySourceSeries(reviews: Review[], months = 12) {
  const monthKeys = Array.from({ length: months }, (_, index) => {
    const date = new Date(CURRENT_DATE);
    date.setDate(1);
    date.setMonth(date.getMonth() - (months - index - 1));
    return date.toISOString().slice(0, 7);
  });
  return monthKeys.map((month) => {
    const row: Record<string, string | number> = {
      month: new Intl.DateTimeFormat("fr-FR", { month: "short" }).format(new Date(`${month}-01`))
    };
    getSources().slice(0, 7).forEach((source) => {
      row[sourceLabels[source]] = reviews.filter((review) => review.date.startsWith(month) && review.source === source).length;
    });
    return row;
  });
}

export function getSentimentTimeline(reviews: Review[], months = 12) {
  const monthKeys = Array.from({ length: months }, (_, index) => {
    const date = new Date(CURRENT_DATE);
    date.setDate(1);
    date.setMonth(date.getMonth() - (months - index - 1));
    return date.toISOString().slice(0, 7);
  });
  return monthKeys.map((month) => {
    const scoped = reviews.filter((review) => review.date.startsWith(month));
    return {
      month: new Intl.DateTimeFormat("fr-FR", { month: "short" }).format(new Date(`${month}-01`)),
      sentiment: sentimentToScore(averageSentiment(scoped)),
      mentions: scoped.length
    };
  });
}

export function getCuveeCounts(reviews: Review[]) {
  return getCuvees()
    .map((cuvee) => ({
      ...cuvee,
      mentions: reviews.filter((review) => review.cuveeId === cuvee.id).length,
      sentiment: averageSentiment(reviews.filter((review) => review.cuveeId === cuvee.id))
    }))
    .sort((a, b) => b.mentions - a.mentions);
}

export function getHomeKpis() {
  const currentPeriod = getPeriodRange("90d");
  const previousPeriod = {
    from: "2025-11-27",
    to: "2026-02-25"
  };
  const current = filterReviews(getReviews(), { period: currentPeriod });
  const previous = filterReviews(getReviews(), { period: previousPeriod });
  const marketStats = getMarketStats(current);
  const cuveeStats = getCuveeCounts(current);
  return {
    reviews: current,
    mentions: current.length,
    mentionsDelta: previous.length ? Math.round(((current.length - previous.length) / previous.length) * 100) : 0,
    sentimentScore: sentimentToScore(averageSentiment(current)),
    sentimentDelta: Math.round((averageSentiment(current) - averageSentiment(previous)) * 100),
    topMarket: marketStats[0],
    topCuvee: cuveeStats[0],
    sourceCount: new Set(current.map((review) => review.source)).size,
    marketCount: new Set(current.map((review) => review.market)).size
  };
}

export function getSourceBreakdown(reviews: Review[]) {
  return getSources()
    .map((source) => {
      const scoped = reviews.filter((review) => review.source === source);
      return {
        source,
        label: sourceLabels[source],
        mentions: scoped.length,
        sentiment: averageSentiment(scoped),
        updatedAt: scoped.sort((a, b) => b.date.localeCompare(a.date))[0]?.date ?? "2026-05-26"
      };
    })
    .sort((a, b) => b.mentions - a.mentions);
}

export function getCompetitorRows(market?: Market) {
  const chevalierBase = market === "JP" ? 85 : market === "US" ? 74 : 78;
  const rows = [
    {
      id: "chevalier",
      name: "Domaine de Chevalier",
      sentiment: chevalierBase,
      mentions: market === "JP" ? 76 : market === "US" ? 112 : 188,
      uniqueDescriptors: 42,
      usPresence: 74,
      asiaPresence: 86,
      sommelierScore: 91,
      consumerScore: 78
    },
    ...getCompetitors().map((competitor, index) => {
      const hash = stableHash(`${competitor.id}-${market ?? "global"}`);
      return {
        id: competitor.id,
        name: competitor.name,
        sentiment: clamp(68 + ((hash + index * 7) % 22), 62, 86),
        mentions: 120 + ((hash + index * 17) % 150),
        uniqueDescriptors: 28 + ((hash + index) % 18),
        usPresence: 54 + ((hash + index * 3) % 34),
        asiaPresence: 48 + ((hash + index * 5) % 34),
        sommelierScore: 70 + ((hash + index * 11) % 20),
        consumerScore: 67 + ((hash + index * 13) % 21)
      };
    })
  ];
  return rows;
}

export function getShareOfVoiceRows() {
  const brands = ["Chevalier", "Smith Haut Lafitte", "Haut-Bailly", "Pape Clément", "Carmes"];
  return ["FR", "US", "UK", "JP", "HK", "DE", "SG"].map((market) => {
    const row: Record<string, string | number> = { market };
    brands.forEach((brand, index) => {
      row[brand] = 12 + (stableHash(`${market}-${brand}`) % (index === 0 ? 28 : 24));
    });
    return row;
  });
}

export function getMarketRadar(market: Market) {
  const axes = ["Élégance", "Puissance", "Complexité", "Rapport Q/P", "Garde", "Typicité"];
  return axes.map((axis) => {
    const hash = stableHash(`${market}-${axis}`);
    return {
      axis,
      market: clamp(62 + (hash % 35), 55, 96),
      global: clamp(68 + (stableHash(`global-${axis}`) % 22), 60, 90)
    };
  });
}

export function getLongTermSignals() {
  return Array.from({ length: 24 }, (_, index) => {
    const date = new Date(CURRENT_DATE);
    date.setDate(1);
    date.setMonth(date.getMonth() - (23 - index));
    const key = date.toISOString().slice(0, 7);
    const monthReviews = getReviews().filter((review) => review.date.startsWith(key));
    return {
      month: new Intl.DateTimeFormat("fr-FR", { month: "short", year: "2-digit" }).format(date),
      mentions: monthReviews.length,
      sentiment: sentimentToScore(averageSentiment(monthReviews)),
      geography: new Set(monthReviews.map((review) => review.market)).size
    };
  });
}
