import { CURRENT_DATE } from "@/lib/constants";
import { ReviewSchema } from "@/lib/schemas";
import type { Market, Review, Sentiment, Source } from "@/lib/types";
import { clamp } from "@/lib/utils";
import { cuvees, getCuvee } from "./cuvees";
import {
  espritDescriptors,
  redDescriptors,
  roseDescriptors,
  sweetDescriptors,
  whiteDescriptors
} from "./descriptors-library";
import { getMarketMetadata } from "./markets-metadata";

type AuthorType = Review["authorType"];

const sources: Source[] = [
  "vivino",
  "cellartracker",
  "reddit",
  "lpv",
  "rvf",
  "decanter",
  "suckling",
  "millesima",
  "wine.com",
  "berry-bros",
  "hawesko"
];

const markets: Market[] = ["FR", "US", "UK", "JP", "HK", "CN", "DE", "BE", "CH", "CA", "SG", "KR", "BR", "RU"];
const vintages = [2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023];
const topics = [
  "élevage",
  "garde",
  "rapport qualité-prix",
  "signature aromatique",
  "place de Bordeaux",
  "primeurs",
  "service à table",
  "potentiel de cave",
  "typicité",
  "comparaison concurrentielle"
];

const authorNames = {
  consumer: ["BordeauxNotebook", "ClaretTable", "CaveRueFondaudège", "LeftBankGlass", "MillésimePatient"],
  sommelier: ["Marie L., sommelière", "Thomas V., chef sommelier", "Kenji H., sommelier", "Ariane D., restaurant"],
  critic: ["RVF tasting panel", "Decanter notes", "Suckling team", "Fine Wine Review"],
  collector: ["Cellar 47", "TokyoCollector", "MayfairCave", "HK Left Bank", "Boston Bordeaux"]
} satisfies Record<AuthorType, string[]>;

function createRandom(seed: number) {
  return function random() {
    let value = (seed += 0x6d2b79f5);
    value = Math.imul(value ^ (value >>> 15), value | 1);
    value ^= value + Math.imul(value ^ (value >>> 7), value | 61);
    return ((value ^ (value >>> 14)) >>> 0) / 4294967296;
  };
}

const random = createRandom(1983);

function pick<T>(items: T[]) {
  return items[Math.floor(random() * items.length)];
}

function pickWeighted<T>(items: T[], weights: number[]) {
  const total = weights.reduce((sum, weight) => sum + weight, 0);
  let threshold = random() * total;
  for (let index = 0; index < items.length; index += 1) {
    threshold -= weights[index];
    if (threshold <= 0) return items[index];
  }
  return items[items.length - 1];
}

function pickSeveral<T>(items: T[], count: number) {
  const copy = [...items];
  const result: T[] = [];
  for (let index = 0; index < count && copy.length > 0; index += 1) {
    const selected = Math.floor(random() * copy.length);
    result.push(copy[selected]);
    copy.splice(selected, 1);
  }
  return result;
}

function seasonalDate() {
  const candidates = Array.from({ length: 24 }, (_, index) => {
    const date = new Date(CURRENT_DATE);
    date.setDate(1);
    date.setMonth(date.getMonth() - index);
    return date;
  });
  const weights = candidates.map((date) => {
    const month = date.getMonth() + 1;
    if ([4, 5, 6].includes(month)) return 2.7;
    if ([9, 10].includes(month)) return 2.2;
    if ([11, 12].includes(month)) return 1.25;
    return 0.85;
  });
  const month = pickWeighted(candidates, weights);
  const day = 1 + Math.floor(random() * 27);
  const date = new Date(month);
  date.setDate(day);
  return date.toISOString().slice(0, 10);
}

function getMarketWeights() {
  return markets.map((market) => {
    const weight: Record<Market, number> = {
      FR: 22,
      US: 17,
      UK: 14,
      JP: 11,
      HK: 7,
      CN: 5,
      DE: 8,
      BE: 4,
      CH: 4,
      CA: 4,
      SG: 4,
      KR: 3,
      BR: 2,
      RU: 1,
      OTHER: 0
    };
    return weight[market];
  });
}

function getSourceForMarket(market: Market) {
  const base = sources;
  const weights = base.map((source) => {
    if (market === "FR") return source === "rvf" || source === "lpv" || source === "millesima" || source === "vivino" ? 5 : 1.4;
    if (market === "UK") return source === "decanter" || source === "berry-bros" || source === "cellartracker" ? 5 : 1.3;
    if (market === "US") return source === "cellartracker" || source === "reddit" || source === "wine.com" || source === "suckling" ? 5 : 1.4;
    if (market === "DE") return source === "hawesko" || source === "vivino" ? 5 : 1.2;
    if (["JP", "HK", "SG", "KR"].includes(market)) return source === "cellartracker" || source === "suckling" || source === "reddit" ? 4.5 : 1.5;
    return source === "vivino" || source === "cellartracker" ? 4 : 1.5;
  });
  return pickWeighted(base, weights);
}

function authorTypeForSource(source: Source): AuthorType {
  if (["rvf", "decanter", "wine-spectator", "suckling"].includes(source)) return "critic";
  if (["cellartracker", "berry-bros"].includes(source)) return random() > 0.45 ? "collector" : "consumer";
  if (["lpv", "reddit"].includes(source)) return random() > 0.55 ? "sommelier" : "collector";
  return "consumer";
}

function sourceLabel(source: Source) {
  const labels: Record<Source, string> = {
    vivino: "Vivino",
    cellartracker: "CellarTracker",
    reddit: "Reddit r/wine",
    lpv: "La Passion du Vin",
    "wine-spectator": "Wine Spectator",
    decanter: "Decanter",
    rvf: "La Revue du Vin de France",
    suckling: "James Suckling",
    vinatis: "Vinatis",
    millesima: "Millésima",
    "wine.com": "Wine.com",
    "berry-bros": "Berry Bros. & Rudd",
    hawesko: "Hawesko"
  };
  return labels[source];
}

function sentimentFromScore(score: number): Sentiment {
  if (score >= 0.18) return "positive";
  if (score <= -0.12) return "negative";
  return "neutral";
}

function sentimentScore(cuveeId: string, vintage: number, market: Market, source: Source) {
  const cuvee = getCuvee(cuveeId);
  const marketModifier: Record<Market, number> = {
    FR: 0.04,
    US: 0.18,
    UK: 0.1,
    JP: 0.34,
    HK: 0.25,
    CN: 0.09,
    DE: -0.02,
    BE: 0.06,
    CH: 0.14,
    CA: 0.15,
    SG: 0.28,
    KR: 0.2,
    BR: 0.08,
    RU: 0.02,
    OTHER: 0
  };
  const vintageModifier: Record<number, number> = {
    2015: 0.18,
    2016: 0.19,
    2017: -0.22,
    2018: 0.17,
    2019: 0.23,
    2020: 0.25,
    2021: -0.12,
    2022: 0.11,
    2023: 0.08
  };
  const cuveeModifier = cuvee.color === "white" ? 0.12 : cuvee.color === "sweet" ? 0.16 : cuvee.id.startsWith("esprit") ? 0.02 : 0.04;
  const sourceModifier = ["rvf", "decanter", "suckling"].includes(source) ? 0.05 : source === "hawesko" ? -0.05 : 0;
  const noise = (random() - 0.5) * 0.44;
  return clamp(marketModifier[market] + vintageModifier[vintage] + cuveeModifier + sourceModifier + noise, -0.88, 0.96);
}

function descriptorsFor(cuveeId: string, market: Market, score: number) {
  const cuvee = getCuvee(cuveeId);
  let library = cuvee.color === "white" ? whiteDescriptors : cuvee.color === "sweet" ? sweetDescriptors : cuvee.color === "rose" ? roseDescriptors : redDescriptors;
  if (cuvee.id.startsWith("esprit")) library = [...espritDescriptors, ...(cuvee.color === "white" ? whiteDescriptors.slice(0, 12) : redDescriptors.slice(0, 12))];
  if (cuvee.estate === "solitude") library = cuvee.color === "white" ? ["discret", "classique", "fraîcheur", ...whiteDescriptors] : ["discret", "classique", "montée en gamme", ...redDescriptors];
  if (cuvee.estate === "lespault-martillac") library = cuvee.color === "white" ? ["argile", "agrumes", "densité", ...whiteDescriptors] : ["fruit noir", "argile", "souplesse", "densité", ...redDescriptors];
  if (cuvee.estate === "guiraud" && cuvee.color === "white") library = ["blanc sec", "agrumes", "pierre à fusil", "tension", ...whiteDescriptors];
  if (cuvee.estate === "clos-des-lunes") library = ["blanc sec", "fraîcheur", "agrumes", "fleurs blanches", ...whiteDescriptors];
  if (cuvee.estate === "suau") library = ["barsac", "orange confite", "fraîcheur", ...sweetDescriptors];
  if (cuvee.estate === "soubian") library = ["replantation", "fruit noir", "structure", ...redDescriptors];
  if (cuvee.estate === "poumey") library = ["conversion bio", "fruit croquant", "souplesse", ...redDescriptors];
  const whiteLike = cuvee.color === "white" || cuvee.color === "sweet";
  const marketTerms: Partial<Record<Market, string[]>> = {
    JP: whiteLike ? ["salinité", "tension", "pureté"] : ["finesse", "retenue", "allonge"],
    US: whiteLike ? ["pierre à fusil", "longueur"] : ["graphite", "cassis", "structure"],
    UK: ["classicisme", "garde", "boîte à cigares"],
    FR: ["typicité", "élevage", "équilibre"],
    DE: score < 0 ? ["boisé", "patience", "élevage"] : ["structure", "fraîcheur"],
    HK: ["collection", "allonge", "garde"],
    SG: ["pureté", "longueur", "garde"]
  };
  const merged = [...(marketTerms[market] ?? []), ...library];
  return pickSeveral(merged, 3 + Math.floor(random() * 3));
}

function ratingFromScore(score: number, source: Source) {
  const base = 87 + score * 8 + random() * 4;
  const criticBias = ["rvf", "decanter", "suckling", "wine-spectator"].includes(source) ? 2 : 0;
  return Math.round(clamp(base + criticBias, 78, 99));
}

function makeText(params: {
  cuveeId: string;
  vintage: number;
  descriptors: string[];
  sentiment: Sentiment;
  market: Market;
  source: Source;
}) {
  const cuvee = getCuvee(params.cuveeId);
  const descriptors = params.descriptors.slice(0, 3);
  const language = getMarketMetadata(params.market).defaultLanguage;
  const wine = `${cuvee.name} ${params.vintage}`;
  const positiveFr = [
    `${wine} offre une lecture nette : ${descriptors.join(", ")}. La bouche reste droite, longue, avec cette retenue qui signe Chevalier.`,
    `Très belle bouteille. ${descriptors[0]} en tête, puis ${descriptors[1]} et une finale sur ${descriptors[2]}. Rien d'appuyé, beaucoup de fond.`,
    `Le vin se place dans une ligne classique et précise. ${descriptors.join(", ")}. À attendre encore, mais la trace est déjà claire.`
  ];
  const neutralFr = [
    `${wine} paraît encore fermé. On devine ${descriptors.join(", ")}, mais l'ensemble demande du temps et un service attentif.`,
    `Bouteille sérieuse, sans effet immédiat. ${descriptors[0]} et ${descriptors[1]} dominent. Finale correcte, à revoir dans trois ans.`,
    `Un Chevalier lisible, mais plus austère que charmeur aujourd'hui. Le nez donne ${descriptors.join(", ")}.`
  ];
  const negativeFr = [
    `${wine} laisse une impression stricte. ${descriptors[0]} et ${descriptors[1]} sont là, mais l'élevage prend encore trop de place.`,
    `J'attendais plus d'ampleur. La matière est sérieuse, pourtant la finale paraît courte et le bois reste perceptible.`,
    `Bouteille décevante au regard du rang. Le vin semble fermé, avec une austérité qui masque le fruit.`
  ];
  const positiveEn = [
    `${wine} is precise and quietly deep, with ${descriptors.join(", ")}. It feels built for patience rather than immediate charm.`,
    `A very composed bottle: ${descriptors[0]}, ${descriptors[1]} and a long finish around ${descriptors[2]}. Classic Pessac restraint.`,
    `Clear Chevalier signature here. The palate is measured, fresh, and carried by ${descriptors.join(", ")}.`
  ];
  const neutralEn = [
    `${wine} is still guarded. There is ${descriptors.join(", ")}, but the wine needs more air and more cellar time.`,
    `Serious rather than generous today. ${descriptors[0]} and ${descriptors[1]} lead the profile, with a reserved finish.`,
    `A precise bottle, but not an easy one. The structure is present; the charm may come later.`
  ];
  const negativeEn = [
    `${wine} feels a little severe. ${descriptors[0]} and ${descriptors[1]} show, but the oak and structure dominate for now.`,
    `Expected more lift from this bottle. The finish is tight and the fruit is partly hidden by the frame.`,
    `Respectable wine, but not persuasive tonight. Too much restraint, not enough pleasure at this stage.`
  ];
  const bucketFr = params.sentiment === "positive" ? positiveFr : params.sentiment === "neutral" ? neutralFr : negativeFr;
  const bucketEn = params.sentiment === "positive" ? positiveEn : params.sentiment === "neutral" ? neutralEn : negativeEn;
  const textFrench = pick(bucketFr);
  const textOriginal = language === "fr" ? textFrench : pick(bucketEn);
  return { textOriginal, textFrench, language: language === "ja" || language === "ko" || language === "zh" ? "en" : language };
}

const mandatoryReviews: Review[] = [
  {
    id: "review-rvf-hero-2019",
    cuveeId: "chevalier-rouge",
    vintage: 2019,
    source: "rvf",
    market: "FR",
    language: "fr",
    date: "2026-03-12",
    rating: 97,
    authorPseudonym: "La Revue du Vin de France",
    authorType: "sommelier",
    textOriginal: "Un Chevalier 2019 d'une précision sidérante. Le cèdre, la cendre froide, la mâche graphitée. Encore jeune mais déjà bouleversant.",
    textFrench: "Un Chevalier 2019 d'une précision sidérante. Le cèdre, la cendre froide, la mâche graphitée. Encore jeune mais déjà bouleversant.",
    sentiment: "positive",
    sentimentScore: 0.88,
    descriptors: ["cèdre", "cendre froide", "graphite", "mâche"],
    topics: ["garde", "typicité", "signature aromatique"],
    url: "https://example.com/rvf/chevalier-2019"
  },
  {
    id: "review-cellartracker-jp-white",
    cuveeId: "chevalier-blanc",
    vintage: 2020,
    source: "cellartracker",
    market: "JP",
    language: "en",
    date: "2026-04-17",
    rating: 98,
    authorPseudonym: "TokyoCollector",
    authorType: "collector",
    textOriginal: "Picked this up at a tasting in Tokyo. The whites from Chevalier have this incredible saline tension — completely different from the Burgundians I'm used to.",
    textFrench: "Dégusté à Tokyo. Les blancs de Chevalier ont cette tension saline incroyable, très différente des bourgognes auxquels je suis habitué.",
    sentiment: "positive",
    sentimentScore: 0.91,
    descriptors: ["salinité", "tension", "pierre à fusil", "pureté"],
    topics: ["signature aromatique", "garde"],
    url: "https://example.com/cellartracker/chevalier-blanc-2020"
  },
  {
    id: "review-vivino-fr-negative-2017",
    cuveeId: "chevalier-rouge",
    vintage: 2017,
    source: "vivino",
    market: "FR",
    language: "fr",
    date: "2024-09-21",
    rating: 84,
    authorPseudonym: "CaveRueFondaudège",
    authorType: "consumer",
    textOriginal: "Cher pour ce que c'est. J'attendais mieux d'un cru classé. Boisé encore présent à 7 ans. Patience.",
    textFrench: "Cher pour ce que c'est. J'attendais mieux d'un cru classé. Boisé encore présent à 7 ans. Patience.",
    sentiment: "negative",
    sentimentScore: -0.52,
    descriptors: ["boisé", "patience", "élevage"],
    topics: ["rapport qualité-prix", "élevage"],
    url: "https://example.com/vivino/chevalier-2017"
  },
  {
    id: "review-reddit-us-haut-bailly",
    cuveeId: "chevalier-rouge",
    vintage: 2018,
    source: "reddit",
    market: "US",
    language: "en",
    date: "2026-01-18",
    rating: 95,
    authorPseudonym: "LeftBankGlass",
    authorType: "collector",
    textOriginal: "Compared blind to Haut-Bailly 2018, Chevalier won on length but lost on charm. Both magnificent.",
    textFrench: "Comparé à l'aveugle avec Haut-Bailly 2018, Chevalier l'emporte sur la longueur mais cède sur le charme. Deux vins magnifiques.",
    sentiment: "positive",
    sentimentScore: 0.62,
    descriptors: ["longueur", "charme", "garde"],
    comparisonsTo: ["Haut-Bailly 2018"],
    topics: ["comparaison concurrentielle", "garde"],
    url: "https://example.com/reddit/wine/chevalier-haut-bailly-2018"
  }
];

function makeGeneratedReview(index: number): Review {
  const market = pickWeighted(markets, getMarketWeights());
  const cuvee = pickWeighted(
    cuvees,
    cuvees.map((item) => (item.id === "chevalier-rouge" ? 3.2 : item.id === "chevalier-blanc" ? 2.2 : item.id.startsWith("esprit") ? 1.7 : item.estate === "chevalier" ? 1.4 : 0.8))
  );
  const vintage = pickWeighted(
    vintages,
    vintages.map((year) => (year === 2019 || year === 2020 ? 2.5 : year === 2017 || year === 2021 ? 1.2 : 1.8))
  );
  const source = getSourceForMarket(market);
  const score = sentimentScore(cuvee.id, vintage, market, source);
  const sentiment = sentimentFromScore(score);
  const descriptors = descriptorsFor(cuvee.id, market, score);
  const authorType = authorTypeForSource(source);
  const text = makeText({ cuveeId: cuvee.id, vintage, descriptors, sentiment, market, source });
  const comparisons = random() > 0.84 ? [pick(["Smith Haut Lafitte 2019", "Haut-Bailly 2018", "Pape Clément 2020", "Carmes Haut-Brion 2019"])] : undefined;
  return {
    id: `review-${String(index + 1).padStart(4, "0")}`,
    cuveeId: cuvee.id,
    vintage,
    source,
    market,
    language: text.language,
    date: seasonalDate(),
    rating: ratingFromScore(score, source),
    authorPseudonym: pick(authorNames[authorType]),
    authorType,
    textOriginal: text.textOriginal,
    textFrench: text.textFrench,
    sentiment,
    sentimentScore: Number(score.toFixed(2)),
    descriptors,
    comparisonsTo: comparisons,
    topics: pickSeveral(topics, 2 + Math.floor(random() * 3)),
    url: `https://example.com/${source}/${cuvee.id}-${vintage}-${index}`
  };
}

function buildReviews() {
  const generated = Array.from({ length: 796 }, (_, index) => makeGeneratedReview(index));
  const haweskoSpike = Array.from({ length: 23 }, (_, index): Review => {
    const descriptors = ["boisé", "stockage", "finale courte", "fatigue"];
    return {
      id: `review-hawesko-spike-${index + 1}`,
      cuveeId: "chevalier-rouge",
      vintage: 2017,
      source: "hawesko",
      market: "DE",
      language: "de",
      date: `2025-09-${String(3 + (index % 20)).padStart(2, "0")}`,
      rating: 80 + (index % 3),
      authorPseudonym: `HaweskoKunde${index + 11}`,
      authorType: "consumer",
      textOriginal: "Die Flasche wirkt müde. Holz und kurze Länge dominieren, vielleicht ein Lagerproblem.",
      textFrench: "La bouteille paraît fatiguée. Bois et finale courte dominent, possiblement un problème de stockage.",
      sentiment: "negative",
      sentimentScore: -0.68,
      descriptors,
      topics: ["élevage", "rapport qualité-prix", "distribution"],
      url: `https://example.com/hawesko/chevalier-2017-${index}`
    };
  });
  const all = [...mandatoryReviews, ...generated.slice(0, 773), ...haweskoSpike];
  return all.map((review) => ReviewSchema.parse(review));
}

export const reviews: Review[] = buildReviews();
export const reviewSources = sources;
