"use client";

import Image from "next/image";
import { useEffect, useMemo, useState } from "react";
import { MainTabBar } from "@/features/predictions/components/main-tab-bar";
import styles from "@/features/predictions/styles/predictions.module.css";
import { authenticatedFetch, getApiUrl, getCurrentProjectId, PROJECT_CHANGED_EVENT } from "@/lib/auth";
import { getPredictionBoard, getPredictionScoreboard } from "@/lib/predictions-client";
import { getMyProjects, getProjectParticipants } from "@/lib/projects-client";
import type {
  PredictionBoardCard,
  PredictionBoardResponse,
  PredictionScoreboardItem,
} from "@/types/predictions";
import type { ProjectParticipant, ProjectSummary } from "@/types/projects";

type CurrentProfile = {
  id: string;
  username: string;
  displayName: string | null;
  profileImageUrl: string | null;
  profileBgColor: string | null;
};

type ParticipantProfile = {
  id: string;
  username: string;
  displayName: string | null;
  profileImageUrl: string | null;
  profileBgColor: string | null;
};

function formatNumber(value: number | null, suffix?: string | null) {
  if (value == null) {
    return "-";
  }
  const displayed = Number.isInteger(value) ? String(value) : value.toFixed(3);
  return displayed + (suffix ? `\u00A0${suffix}` : "");
}

function formatPercent(value: number) {
  return `${Math.round(value * 100)}%`;
}

function formatDateLabel(value: string | null) {
  if (!value) {
    return "-";
  }

  const date = new Date(value);
  if (Number.isNaN(date.getTime())) {
    return value;
  }

  return new Intl.DateTimeFormat("fr-FR", {
    day: "2-digit",
    month: "short",
    year: "numeric",
  }).format(date);
}

function formatBoardValue(
  value: { fieldLabel: string; valueText: string | null; valueNumber: number | null; valueDate: string | null },
  unit?: string | null,
) {
  if (value.valueNumber != null) {
    return `${value.fieldLabel}: ${formatNumber(value.valueNumber, unit)}`;
  }
  if (value.valueText) {
    return `${value.fieldLabel}: ${value.valueText}`;
  }
  if (value.valueDate) {
    return `${value.fieldLabel}: ${formatDateLabel(value.valueDate.slice(0, 10))}`;
  }
  return `${value.fieldLabel}: -`;
}

function getDisplayName(user: { displayName: string | null; username: string }) {
  return user.displayName?.trim() || user.username;
}

function resolveProfileImageSrc(apiUrl: string, profileImageUrl: string | null) {
  if (!profileImageUrl) {
    return null;
  }

  if (/^https?:\/\//i.test(profileImageUrl) || profileImageUrl.startsWith("data:")) {
    return profileImageUrl;
  }

  return `${apiUrl}${profileImageUrl}`;
}

function buildParticipantProfileMap(
  participants: ProjectParticipant[],
  currentProfile: CurrentProfile | null,
) {
  const profiles = new Map<string, ParticipantProfile>();

  for (const participant of participants) {
    profiles.set(participant.userId, {
      id: participant.user.id,
      username: participant.user.username,
      displayName: participant.user.displayName,
      profileImageUrl: participant.user.profileImageUrl,
      profileBgColor: participant.user.profileBgColor,
    });
  }

  if (currentProfile) {
    profiles.set(currentProfile.id, currentProfile);
  }

  return profiles;
}

function mergeParticipantProfilesInScoreboard(
  items: PredictionScoreboardItem[],
  profiles: Map<string, ParticipantProfile>,
) {
  return items.map((item) => {
    const profile = profiles.get(item.userId);
    if (!profile) {
      return item;
    }

    return {
      ...item,
      displayName: item.displayName ?? profile.displayName,
      profileImageUrl: item.profileImageUrl ?? profile.profileImageUrl,
      profileBgColor: item.profileBgColor ?? profile.profileBgColor,
    };
  });
}

function mergeParticipantProfilesInBoard(
  payload: PredictionBoardResponse,
  profiles: Map<string, ParticipantProfile>,
) {
  return {
    ...payload,
    cards: payload.cards.map((card) => ({
      ...card,
      entries: card.entries.map((entry) => {
        const profile = profiles.get(entry.userId);
        if (!profile) {
          return entry;
        }

        return {
          ...entry,
          displayName: entry.displayName ?? profile.displayName,
          profileImageUrl: entry.profileImageUrl ?? profile.profileImageUrl,
          profileBgColor: entry.profileBgColor ?? profile.profileBgColor,
        };
      }),
    })),
  };
}

function isNamesCard(card: PredictionBoardCard) {
  return card.title.toLowerCase().includes("prenom");
}

function isSexCard(card: PredictionBoardCard) {
  return card.title.toLowerCase().includes("sexe");
}

function isWeightCard(card: PredictionBoardCard) {
  const t = card.title.toLowerCase();
  return t.includes("poid") || t.includes("poids") || t.includes("gramme") || t.includes("kg");
}

function capitalizeLabel(value: string) {
  if (!value) {
    return value;
  }

  return value.charAt(0).toUpperCase() + value.slice(1);
}

function getTextVoteSummary(values: string[]) {
  const counts = new Map<string, number>();

  for (const value of values) {
    counts.set(value, (counts.get(value) ?? 0) + 1);
  }

  const sorted = [...counts.entries()].sort((left, right) => right[1] - left[1]);
  const dominant = sorted[0] ?? null;
  const runnerUp = sorted[1] ?? null;

  if (!dominant) {
    return null;
  }

  const isExAequo = runnerUp != null && runnerUp[1] === dominant[1];

  return {
    dominant,
    sorted,
    isExAequo,
    percent: formatPercent(dominant[1] / values.length),
    tiePercent: runnerUp ? `${formatPercent(dominant[1] / values.length)} / ${formatPercent(runnerUp[1] / values.length)}` : null,
  };
}

const SEX_ICONS: Record<string, string> = {
  fille: "/images/pictos/choix-fille.png",
  garcon: "/images/pictos/choix-garcon.png",
  "garçon": "/images/pictos/choix-garcon.png",
};

function formatValueShort(
  value: { fieldLabel: string; valueText: string | null; valueNumber: number | null; valueDate: string | null },
  unit?: string | null,
): string {
  if (value.valueNumber != null) {
    return formatNumber(value.valueNumber, unit);
  }
  if (value.valueText) {
    return value.valueText;
  }
  if (value.valueDate) {
    return formatDateLabel(value.valueDate.slice(0, 10));
  }
  return "-";
}

function getNamesOnlyLines(card: PredictionBoardCard, values: PredictionBoardCard["entries"][number]["values"]) {
  if (!isNamesCard(card)) {
    return [];
  }

  const names = values
    .map((value) => value.valueText?.trim() ?? "")
    .filter((value) => value.length > 0)
    .slice(0, 6);

  return Array.from({ length: 6 }, (_, index) => names[index] ?? "");
}

function mergeCurrentProfileInScoreboard(
  items: PredictionScoreboardItem[],
  currentProfile: CurrentProfile | null,
) {
  if (!currentProfile) {
    return items;
  }

  return items.map((item) => {
    if (item.userId !== currentProfile.id) {
      return item;
    }

    return {
      ...item,
      displayName: item.displayName ?? currentProfile.displayName,
      profileImageUrl: item.profileImageUrl ?? currentProfile.profileImageUrl,
      profileBgColor: item.profileBgColor ?? currentProfile.profileBgColor,
    };
  });
}

function mergeCurrentProfileInBoard(
  payload: PredictionBoardResponse,
  currentProfile: CurrentProfile | null,
) {
  if (!currentProfile) {
    return payload;
  }

  return {
    ...payload,
    cards: payload.cards.map((card) => ({
      ...card,
      entries: card.entries.map((entry) => {
        if (entry.userId !== currentProfile.id) {
          return entry;
        }

        return {
          ...entry,
          displayName: entry.displayName ?? currentProfile.displayName,
          profileImageUrl: entry.profileImageUrl ?? currentProfile.profileImageUrl,
          profileBgColor: entry.profileBgColor ?? currentProfile.profileBgColor,
        };
      }),
    })),
  };
}

function getCardStats(card: PredictionBoardCard) {
  const rawValues = card.entries.flatMap((entry) => entry.values);

  if (card.type === "NUMBER") {
    const numbers = rawValues
      .map((value) => value.valueNumber)
      .filter((value): value is number => value != null)
      .sort((a, b) => a - b);

    if (numbers.length === 0) {
      return {
        headline: "Pas encore de tendance numerique",
        details: ["Moyenne: -", "Mediane: -"],
      };
    }

    const mean = numbers.reduce((sum, current) => sum + current, 0) / numbers.length;
    const mid = Math.floor(numbers.length / 2);
    const median =
      numbers.length % 2 === 0 ? (numbers[mid - 1] + numbers[mid]) / 2 : numbers[mid];

    return {
      headline: `Moyenne ${formatNumber(mean, card.unit)}`,
      details: [
        `Mediane ${formatNumber(median, card.unit)}`,
        `Min ${formatNumber(numbers[0], card.unit)} / Max ${formatNumber(numbers[numbers.length - 1], card.unit)}`,
      ],
    };
  }

  const normalizedTexts = rawValues
    .map((value) => value.valueText?.trim().toLowerCase() ?? "")
    .filter((value) => value.length > 0);

  if (card.type === "DATE") {
    const dates = rawValues
      .map((value) => value.valueDate?.slice(0, 10) ?? "")
      .filter((value) => value.length > 0);

    if (dates.length === 0) {
      return {
        headline: "Pas encore de date dominante",
        details: ["Tendance: -"],
      };
    }

    const counts = new Map<string, number>();
    for (const date of dates) {
      counts.set(date, (counts.get(date) ?? 0) + 1);
    }

    const dominant = [...counts.entries()].sort((a, b) => b[1] - a[1])[0];
    return {
      headline: `Date dominante: ${dominant[0]}`,
      details: [`Consensus: ${formatPercent(dominant[1] / dates.length)}`],
    };
  }

  if (isSexCard(card)) {
    const sexValues = rawValues
      .map((value) => value.valueText?.trim().toLowerCase() ?? "")
      .filter((value) => value.length > 0);

    const summary = getTextVoteSummary(sexValues);

    if (!summary) {
      return {
        headline: "Pas encore de reponse dominante",
        details: ["Top choix: -"],
      };
    }

    if (summary.isExAequo && summary.sorted.length >= 2) {
      return {
        headline: `Ex aequo: ${summary.sorted
          .slice(0, 2)
          .map(([value]) => value)
          .join(" / ")}`,
        details: [
          `Consensus: ${summary.tiePercent ?? summary.percent}`,
          `Top choix: ${summary.sorted
            .slice(0, 3)
            .map(([value, count]) => `${value} (${count})`)
            .join(", ")}`,
        ],
      };
    }

    const [dominantValue] = summary.dominant;

    return {
      headline: `Reponse dominante: ${dominantValue}`,
      details: [
        `Consensus: ${summary.percent}`,
        `Top choix: ${summary.sorted
          .slice(0, 3)
          .map(([value, count]) => `${value} (${count})`)
          .join(", ")}`,
      ],
    };
  }

  if (normalizedTexts.length === 0) {
    return {
      headline: "Pas encore de reponse dominante",
      details: ["Top choix: -"],
    };
  }

  const counts = new Map<string, number>();
  for (const text of normalizedTexts) {
    counts.set(text, (counts.get(text) ?? 0) + 1);
  }

  const sorted = [...counts.entries()].sort((a, b) => b[1] - a[1]);
  const dominant = sorted[0];

  if (dominant && sorted[1] && sorted[1][1] === dominant[1]) {
    return {
      headline: `Ex aequo: ${sorted
        .slice(0, 2)
        .map(([value]) => value)
        .join(" / ")}`,
      details: [
        `Consensus: ${formatPercent(dominant[1] / normalizedTexts.length)} / ${formatPercent(sorted[1][1] / normalizedTexts.length)}`,
        `Top choix: ${sorted
          .slice(0, 3)
          .map(([value, count]) => `${value} (${count})`)
          .join(", ")}`,
      ],
    };
  }

  return {
    headline: `Reponse dominante: ${dominant[0]}`,
    details: [
      `Consensus: ${formatPercent(dominant[1] / normalizedTexts.length)}`,
      `Top choix: ${sorted
        .slice(0, 3)
        .map(([value, count]) => `${value} (${count})`)
        .join(", ")}`,
    ],
  };
}

function Avatar({
  user,
  apiUrl,
  size = "md",
}: {
  user: {
    displayName: string | null;
    username: string;
    profileImageUrl: string | null;
    profileBgColor: string | null;
  };
  apiUrl: string;
  size?: "sm" | "md" | "lg";
}) {
  const label = getDisplayName(user);
  const initials = label
    .split(/\s+/)
    .filter(Boolean)
    .slice(0, 2)
    .map((part) => part[0]?.toUpperCase() ?? "")
    .join("") || "?";
  const imageSrc = resolveProfileImageSrc(apiUrl, user.profileImageUrl);

  return (
    <div
      className={`${styles.recapAvatar} ${size === "sm" ? styles.recapAvatarSm : ""} ${size === "lg" ? styles.recapAvatarLg : ""}`}
      style={user.profileBgColor ? { background: user.profileBgColor } : undefined}
      aria-hidden="true"
    >
      {imageSrc ? <img src={imageSrc} alt="" /> : <span>{initials}</span>}
    </div>
  );
}

export default function PredictionsRecapPage() {
  const apiUrl = getApiUrl();
  const [board, setBoard] = useState<PredictionBoardResponse | null>(null);
  const [scoreboard, setScoreboard] = useState<PredictionScoreboardItem[]>([]);
  const [currentProject, setCurrentProject] = useState<ProjectSummary | null>(null);
  const [loading, setLoading] = useState(true);
  const [message, setMessage] = useState("");
  const [projectRefreshKey, setProjectRefreshKey] = useState(0);
  const [activeBabyIndex, setActiveBabyIndex] = useState(1);
  const [babyCount, setBabyCount] = useState(1);
  const [selectedUserId, setSelectedUserId] = useState<string | null>(null);
  const [openCardId, setOpenCardId] = useState<string | null>(null);
  const [showParticipantDropdown, setShowParticipantDropdown] = useState(false);

  useEffect(() => {
    const onProjectChanged = () => {
      setLoading(true);
      setMessage("");
      setProjectRefreshKey((current) => current + 1);
    };

    window.addEventListener(PROJECT_CHANGED_EVENT, onProjectChanged);
    return () => {
      window.removeEventListener(PROJECT_CHANGED_EVENT, onProjectChanged);
    };
  }, []);

  useEffect(() => {
    Promise.all([
      getPredictionBoard(undefined, activeBabyIndex),
      getPredictionScoreboard(),
      getMyProjects(),
      authenticatedFetch(apiUrl, "/users/me")
        .then(async (response) => {
          const payload = (await response.json()) as CurrentProfile | { message?: string };
          if (!response.ok || !("id" in payload)) {
            return null;
          }
          return payload;
        })
        .catch(() => null),
    ])
      .then(async ([boardPayload, scoreboardPayload, projects, currentProfile]) => {
        const currentProjectId = getCurrentProjectId();
        const project =
          projects.find((item) => item.id === currentProjectId) ?? projects[0] ?? null;

        const participants = project
          ? await getProjectParticipants(project.id).catch(() => [] as ProjectParticipant[])
          : [];

        const participantProfiles = buildParticipantProfileMap(participants, currentProfile);

        setBoard(mergeParticipantProfilesInBoard(boardPayload, participantProfiles));
        setScoreboard(mergeParticipantProfilesInScoreboard(scoreboardPayload, participantProfiles));
        setCurrentProject(project);
        setBabyCount(Math.max(1, project?.babyCount ?? 1));
      })
      .catch((error) => {
        setMessage(error instanceof Error ? error.message : "Impossible de charger le recap");
      })
      .finally(() => {
        setLoading(false);
      });
  }, [activeBabyIndex, apiUrl, projectRefreshKey]);

  const getBabyLabel = (idx: number): string =>
    currentProject?.babies?.find((b) => b.babyIndex === idx)?.label ?? `Bébé ${idx}`;

  const totalParticipants = useMemo(() => {
    const ids = new Set<string>();
    for (const card of board?.cards ?? []) {
      for (const entry of card.entries) {
        ids.add(entry.userId);
      }
    }
    return ids.size;
  }, [board]);

  const heroTone = board?.game?.finalized
    ? "finalized"
    : board?.game?.status === "CLOSED"
      ? "closed"
      : "open";

  const heroTitle = board?.game?.finalized
    ? "Le jeu est termine, les scores sont figes"
    : board?.game?.status === "CLOSED"
      ? "Le jeu est cloture"
      : "Les tendances evoluent encore";

  const heroText = board?.game?.finalized
    ? "C\u2019est fini\u00a0! Retrouve les pronostics de chacun, le recap des tendances et le classement definitif."
    : board?.game?.status === "CLOSED"
      ? "Les pronostics sont verrouilles. Plus personne ne peut modifier sa mise\u00a0\u2014 compare les predictions de la famille en attendant le verdict."
      : "Les participants jouent encore\u00a0! Consulte les pronostics de chacun et vois ce qui ressort le plus.";

  const mostActiveCard = useMemo(() => {
    const cards = board?.cards ?? [];
    return cards.reduce<PredictionBoardCard | null>((best, card) => {
      if (!best || card.totalPredictions > best.totalPredictions) {
        return card;
      }
      return best;
    }, null);
  }, [board]);

  const strongestConsensus = useMemo(() => {
    const cards = board?.cards ?? [];
    const candidates = cards
      .map((card) => {
        const stats = getCardStats(card);
        const consensusLine = stats.details.find((detail) => detail.startsWith("Consensus:"));
        const consensus = consensusLine ? Number.parseInt(consensusLine.replace(/[^0-9]/g, ""), 10) : null;
        return {
          card,
          stats,
          consensus: Number.isFinite(consensus) ? consensus : null,
        };
      })
      .filter((item) => item.consensus != null);

    return candidates.sort((a, b) => (b.consensus ?? 0) - (a.consensus ?? 0))[0] ?? null;
  }, [board]);

  const isFinalized = Boolean(board?.game?.finalized);

  const displayParticipants = useMemo(() => {
    if (isFinalized) {
      return scoreboard;
    }

    return [...scoreboard].sort((left, right) =>
      getDisplayName(left).localeCompare(getDisplayName(right), "fr", { sensitivity: "base" }),
    );
  }, [isFinalized, scoreboard]);

  const scoreboardLeader = displayParticipants[0] ?? null;

  const heroStats = useMemo(() => {
    const cards = board?.cards ?? [];

    const weightCard = cards.find(isWeightCard);
    let weight: string | null = null;
    if (weightCard?.type === "NUMBER") {
      const nums = weightCard.entries.flatMap((e) => e.values).map((v) => v.valueNumber).filter((v): v is number => v != null);
      if (nums.length > 0) {
        const mean = nums.reduce((s, n) => s + n, 0) / nums.length;
        weight = formatNumber(mean, weightCard.unit);
      }
    }

    const heightCard = cards.find((c) => c.title.toLowerCase().includes("taille"));
    let height: string | null = null;
    if (heightCard?.type === "NUMBER") {
      const nums = heightCard.entries.flatMap((e) => e.values).map((v) => v.valueNumber).filter((v): v is number => v != null);
      if (nums.length > 0) {
        const mean = nums.reduce((s, n) => s + n, 0) / nums.length;
        height = formatNumber(mean, heightCard.unit);
      }
    }

    const cranialCard = cards.find((c) => {
      const t = c.title.toLowerCase();
      return t.includes("crane") || t.includes("cr\u00e2ne") || t.includes("perimetre") || t.includes("p\u00e9rim\u00e8tre");
    });
    let cranial: string | null = null;
    if (cranialCard?.type === "NUMBER") {
      const nums = cranialCard.entries.flatMap((e) => e.values).map((v) => v.valueNumber).filter((v): v is number => v != null);
      if (nums.length > 0) {
        const mean = nums.reduce((s, n) => s + n, 0) / nums.length;
        cranial = formatNumber(mean, cranialCard.unit);
      }
    }

    const sexCard = cards.find(isSexCard);
    let sex: { label: string; percent: string; caption: string } | null = null;
    if (sexCard) {
      const texts = sexCard.entries
        .flatMap((e) => e.values)
        .map((v) => v.valueText?.trim().toLowerCase())
        .filter((v): v is string => Boolean(v));

      const summary = getTextVoteSummary(texts);
      if (summary) {
        if (summary.isExAequo && summary.sorted.length >= 2) {
          sex = {
            label: "Ex aequo",
            percent: summary.tiePercent ?? summary.percent,
            caption: "Sexe partagé",
          };
        } else {
          const [dominantValue] = summary.dominant;
          sex = {
            label: capitalizeLabel(dominantValue),
            percent: summary.percent,
            caption: "Sexe en tete",
          };
        }
      }
    }

    return { weight, height, cranial, sex };
  }, [board]);

  const activeSelectedUserId =
    selectedUserId && displayParticipants.some((item) => item.userId === selectedUserId)
      ? selectedUserId
      : displayParticipants[0]?.userId ?? null;

  useEffect(() => {
    setShowParticipantDropdown(false);
  }, [activeSelectedUserId, displayParticipants.length]);

  const selectedParticipant = useMemo(() => {
    const targetId = activeSelectedUserId;
    if (!targetId) {
      return null;
    }

    const baseUser = displayParticipants.find((item) => item.userId === targetId);
    if (!baseUser) {
      return null;
    }

    const predictions = (board?.cards ?? [])
      .map((card) => ({
        card,
        entry: card.entries.find((entry) => entry.userId === targetId) ?? null,
        stats: getCardStats(card),
      }));

    return {
      user: baseUser,
      predictions,
    };
  }, [activeSelectedUserId, board, displayParticipants]);

  return (
    <>
      <section className={`${styles.recapHero} ${heroTone === "closed" ? styles.recapHeroClosed : ""} ${heroTone === "finalized" ? styles.recapHeroFinalized : ""}`}>
        <div className={styles.recapHeroContent}>
          <span className={styles.recapHeroEyebrow}>
            {heroTone === "open" ? "Moyennes de tous les participants en direct" : heroTone === "closed" ? "Moyennes finales" : "Moyennes definitives"}
          </span>
          <div className={styles.recapHeroStats}>
            {heroStats.weight && (
              <div className={styles.recapHeroStatChip}>
                <span className={styles.recapHeroStatValue}>{heroStats.weight}</span>
                <span className={styles.recapHeroStatLabel}>Poids</span>
              </div>
            )}
            {heroStats.height && (
              <div className={styles.recapHeroStatChip}>
                <span className={styles.recapHeroStatValue}>{heroStats.height}</span>
                <span className={styles.recapHeroStatLabel}>Taille</span>
              </div>
            )}
            {heroStats.cranial && (
              <div className={styles.recapHeroStatChip}>
                <span className={styles.recapHeroStatValue}>{heroStats.cranial}</span>
                <span className={styles.recapHeroStatLabel}>Périm. crânien</span>
              </div>
            )}
            {heroStats.sex && (
              <div className={`${styles.recapHeroStatChip} ${styles.recapHeroStatChipSex}`}>
                <span className={styles.recapHeroStatValue}>{heroStats.sex.label}</span>
                <span className={styles.recapHeroStatPercent}>{heroStats.sex.percent}</span>
                <span className={styles.recapHeroStatLabel}>{heroStats.sex.caption}</span>
              </div>
            )}
            {!heroStats.weight && !heroStats.height && !heroStats.cranial && !heroStats.sex && (
              <p className={styles.recapHeroEmpty}>Pas encore de pronostics enregistrés.</p>
            )}
          </div>
        </div>
      </section>

      <MainTabBar />

      {babyCount > 1 ? (
        <section className={`panel ${styles.babyBanner}`}>
          <p className={styles.babyBannerLabel}>Vous consultez les stats pour :</p>
          <div className={styles.babyTabs}>
            {Array.from({ length: babyCount }, (_, i) => i + 1).map((babyIdx) => (
              <button
                key={babyIdx}
                className={`${styles.babyTab} ${babyIdx === activeBabyIndex ? styles.babyTabActive : ""}`}
                onClick={() => {
                  setLoading(true);
                  setMessage("");
                  setActiveBabyIndex(babyIdx);
                }}
                type="button"
              >
                {getBabyLabel(babyIdx)}
              </button>
            ))}
          </div>
        </section>
      ) : null}

      <div className={styles.recapSectionHeading} style={{ padding: "0 0.5rem" }}>
        <div>
          <span className={styles.recapSectionEyebrow}>{isFinalized ? "Classement" : "Pronostics"}</span>
          <h2>{isFinalized ? "Classement final" : "Pronostics par participant"}</h2>
        </div>
      </div>
      {!loading && !message && displayParticipants.length === 0 ? (
        <p className={styles.empty} style={{ padding: "0 0.5rem" }}>Classement vide pour le moment.</p>
      ) : null}

        {!loading && !message && displayParticipants.length > 0 && selectedParticipant ? (() => {
          const selectedIndex = displayParticipants.findIndex((item) => item.userId === activeSelectedUserId);
          const prevUser = selectedIndex > 0 ? displayParticipants[selectedIndex - 1] : null;
          const nextUser = selectedIndex < displayParticipants.length - 1 ? displayParticipants[selectedIndex + 1] : null;
          const totalCount = displayParticipants.length;
          const canChooseParticipant = totalCount > 1;

          return (
            <div className={styles.recapParticipantFocusPanel}>
              <div className={styles.recapViewerWrapper}>
                <section className={styles.recapViewerPanel} aria-label="Participant consulté">
                  <button
                    type="button"
                    className={styles.recapViewerArrow}
                    disabled={!prevUser}
                    aria-label="Participant précédent"
                    onClick={() => { if (prevUser) setSelectedUserId(prevUser.userId); }}
                  >
                    ‹
                  </button>

                  <div className={styles.recapViewerMain}>
                    <div className={styles.recapViewerIdentityDropdownAnchor}>
                      {canChooseParticipant ? (
                        <button
                          type="button"
                          className={`${styles.recapViewerIdentityButton} ${showParticipantDropdown ? styles.recapViewerIdentityButtonOpen : ""}`}
                          onClick={() => setShowParticipantDropdown((v) => !v)}
                          aria-expanded={showParticipantDropdown}
                          aria-controls="recap-participant-dropdown"
                        >
                          <div className={styles.recapViewerIdentity}>
                            <Avatar user={selectedParticipant.user} apiUrl={apiUrl} size="lg" />
                            <div className={styles.recapViewerText}>
                              <span className={styles.recapViewerKicker}>Pronostics de</span>
                              <strong className={styles.recapViewerName}>{getDisplayName(selectedParticipant.user)}</strong>
                              <span className={styles.recapViewerMeta}>
                                {isFinalized
                                  ? `${selectedParticipant.user.totalPoints} pts · Joueur ${selectedIndex + 1} sur ${totalCount}`
                                  : `Joueur ${selectedIndex + 1} sur ${totalCount}`}
                              </span>
                            </div>
                          </div>

                          <span className={styles.recapViewerIdentityHint} aria-hidden="true">
                            <span className={styles.recapViewerListChevron}>{showParticipantDropdown ? "▴" : "▾"}</span>
                          </span>
                        </button>
                      ) : (
                        <div className={styles.recapViewerIdentityStatic}>
                          <div className={styles.recapViewerIdentity}>
                            <Avatar user={selectedParticipant.user} apiUrl={apiUrl} size="lg" />
                            <div className={styles.recapViewerText}>
                              <span className={styles.recapViewerKicker}>Pronostics de</span>
                              <strong className={styles.recapViewerName}>{getDisplayName(selectedParticipant.user)}</strong>
                              <span className={styles.recapViewerMeta}>
                                {isFinalized
                                  ? `${selectedParticipant.user.totalPoints} pts · Joueur ${selectedIndex + 1} sur ${totalCount}`
                                  : `Joueur ${selectedIndex + 1} sur ${totalCount}`}
                              </span>
                            </div>
                          </div>
                        </div>
                      )}

                      {canChooseParticipant && showParticipantDropdown ? (
                        <>
                          <button
                            type="button"
                            className={styles.recapDropdownOverlay}
                            onClick={() => setShowParticipantDropdown(false)}
                            aria-label="Fermer la liste des participants"
                          />
                          <div id="recap-participant-dropdown" className={styles.recapParticipantDropdown} aria-label="Liste des participants">
                            {displayParticipants.map((item) => (
                              <button
                                type="button"
                                aria-current={item.userId === activeSelectedUserId ? "true" : undefined}
                                key={item.userId}
                                className={`${styles.recapDropdownItem} ${item.userId === activeSelectedUserId ? styles.recapDropdownItemActive : ""}`}
                                onClick={() => { setSelectedUserId(item.userId); setShowParticipantDropdown(false); }}
                              >
                                <Avatar user={item} apiUrl={apiUrl} size="sm" />
                                <span>{getDisplayName(item)}</span>
                                {isFinalized ? <span className={styles.recapDropdownPoints}>{item.totalPoints} pts</span> : null}
                              </button>
                            ))}
                          </div>
                        </>
                      ) : null}
                    </div>
                  </div>

                  <button
                    type="button"
                    className={styles.recapViewerArrow}
                    disabled={!nextUser}
                    aria-label="Participant suivant"
                    onClick={() => { if (nextUser) setSelectedUserId(nextUser.userId); }}
                  >
                    ›
                  </button>
                </section>
              </div>

              {selectedParticipant.predictions.length > 0 ? (
                <div className={styles.recapAnswersGrid}>
                  {selectedParticipant.predictions.map(({ card, entry }) => {
                    const isSex = isSexCard(card);
                    const isNames = isNamesCard(card);
                    const isWeight = isWeightCard(card);
                    const sexVal = isSexCard(card) ? (entry?.values[0]?.valueText ?? "") : "";
                    const sexKey = sexVal.toLowerCase();
                    const sexIcon = SEX_ICONS[sexKey] ?? (sexKey.startsWith("g") ? SEX_ICONS.garcon : SEX_ICONS.fille);
                    const isFille = sexKey.startsWith("f");
                    let cardLayoutClass = styles.recapParticipantCardMetric;
                    if (isSex) {
                      cardLayoutClass = styles.recapParticipantCardSexCompact;
                    } else if (isNames) {
                      cardLayoutClass = styles.recapParticipantCardNamesCompact;
                    }

                    return (
                      <article
                        key={`${selectedParticipant.user.userId}-${card.id}`}
                        className={`${styles.recapParticipantCard} ${isSex ? styles.recapParticipantCardSex : ""} ${cardLayoutClass}`.trim()}
                      >
                        <div className={styles.recapParticipantCardMeta}>
                          <span className={styles.recapParticipantCardLabel}>{card.title}</span>
                          {isFinalized && entry?.isScored ? (
                            <span className={styles.recapParticipantCardPoints}>{entry.totalPoints} pts</span>
                          ) : isFinalized ? (
                            <span className={styles.recapParticipantCardPointsMuted}>Non classé</span>
                          ) : null}
                        </div>

                        {!entry ? (
                          <p className={styles.recapParticipantCardEmpty}>Aucune réponse</p>
                        ) : isSex ? (
                          <div className={`${styles.recapSexAnswer} ${isFille ? styles.recapSexAnswerFille : styles.recapSexAnswerGarcon}`}>
                            {sexIcon ? (
                              <Image src={sexIcon} alt="" width={48} height={48} className={styles.recapSexIcon} />
                            ) : null}
                            <span className={styles.recapSexLabel}>{sexVal || "—"}</span>
                          </div>
                        ) : isNames ? (
                          <div className={styles.recapParticipantNamesList}>
                            {getNamesOnlyLines(card, entry.values).filter(Boolean).map((name, index) => (
                              <span key={`${card.id}-name-${index}`} className={styles.recapParticipantNameChip}>
                                {name}
                              </span>
                            ))}
                          </div>
                        ) : isWeight ? (
                          <div className={styles.recapWeightAnswer}>
                            {entry.values.map((value, i) => (
                              <span key={i} className={styles.recapWeightBadge}>
                                {formatValueShort(value, card.unit)}
                              </span>
                            ))}
                          </div>
                        ) : (
                          <p className={styles.recapParticipantCardValue}>
                            {entry.values.map((value) => formatValueShort(value, card.unit)).join(" • ")}
                          </p>
                        )}
                      </article>
                    );
                  })}
                </div>
              ) : (
                <p className={styles.empty}>Aucun pronostic detaillé pour ce participant.</p>
              )}
            </div>
          );
        })() : null}

      <hr className={styles.recapDivider} />

      <div className={styles.recapSectionHeading} style={{ padding: "0 0.5rem" }}>
        <div>
          <span className={styles.recapSectionEyebrow}>Tendances</span>
          <h2>Moyennes et pronostics{babyCount > 1 ? ` — ${getBabyLabel(activeBabyIndex)}` : ""}</h2>
        </div>
        <p>Les signaux clés en un coup d&apos;œil. Dépliez une carte pour voir les réponses de chacun.</p>
      </div>
      {loading ? <p style={{ padding: "0 0.5rem" }}>Chargement des statistiques...</p> : null}
      {message ? <p className={styles.error} style={{ padding: "0 0.5rem" }}>{message}</p> : null}

        {!loading && !message ? (
          <>
            <div className={styles.recapKpiGrid}>
              <article className={styles.recapKpiCard}>
                <span className={styles.recapKpiLabel}>Participants</span>
                <strong>{totalParticipants}</strong>
                <p>Toutes cartes confondues.</p>
              </article>
            </div>

            {(board?.cards.length ?? 0) === 0 ? (
              <p className={styles.empty}>Aucune donnee de pronostic disponible.</p>
            ) : (
              <div className={styles.recapAccordionList}>
                {(board?.cards ?? []).map((card) => {
                  const stats = getCardStats(card);
                  const isOpen = openCardId === card.id;

                  return (
                    <div
                      key={card.id}
                      className={`${styles.recapAccordionCard} ${isOpen ? styles.recapAccordionCardOpen : ""}`}
                    >
                      <button
                        type="button"
                        className={styles.recapAccordionSummary}
                        aria-expanded={isOpen}
                        onClick={() => setOpenCardId(isOpen ? null : card.id)}
                      >
                        <div>
                          <strong>{card.title}</strong>
                          <p className={styles.activityMeta}>{stats.headline}</p>
                        </div>
                        <div className={styles.recapAccordionMeta}>
                          <span>{card.totalPredictions} pronostics</span>
                          <span className={styles.recapAccordionToggle}>{isOpen ? "Fermer" : "Voir le détail"}</span>
                        </div>
                      </button>

                      {isOpen ? (
                        <div className={styles.recapAccordionBody}>
                          <div className={styles.recapTrendDetails}>
                            {stats.details.map((detail) => (
                              <p key={`${card.id}-recap-${detail}`}>{detail}</p>
                            ))}
                          </div>

                          <div className={styles.boardEntries}>
                            {card.entries.map((entry) => {
                              const sexVal = isSexCard(card) ? (entry.values[0]?.valueText ?? "") : "";
                              const sexKey = sexVal.toLowerCase();
                              const sexIcon = SEX_ICONS[sexKey] ?? (sexKey.startsWith("g") ? SEX_ICONS.garcon : SEX_ICONS.fille);
                              const isFille = sexKey.startsWith("f");

                              return (
                                <button
                                  key={entry.entryId}
                                  className={`${styles.boardEntry} ${selectedParticipant?.user.userId === entry.userId ? styles.boardEntryActive : ""}`}
                                  type="button"
                                  onClick={() => setSelectedUserId(entry.userId)}
                                >
                                  <div className={styles.boardEntryHeader}>
                                    <div className={styles.recapIdentityRow}>
                                      <Avatar user={entry} apiUrl={apiUrl} size="sm" />
                                      <strong>{getDisplayName(entry)}</strong>
                                    </div>
                                    {entry.totalPoints > 0 ? <span>{entry.totalPoints} pts</span> : null}
                                  </div>

                                  {isSexCard(card) ? (
                                    <div className={`${styles.recapSexAnswer} ${isFille ? styles.recapSexAnswerFille : styles.recapSexAnswerGarcon}`}>
                                      {sexIcon ? <Image src={sexIcon} alt="" width={40} height={40} className={styles.recapSexIcon} /> : null}
                                      <span className={styles.recapSexLabel}>{sexVal || "—"}</span>
                                    </div>
                                  ) : isNamesCard(card) ? (
                                    <div className={styles.recapParticipantNamesList}>
                                      {entry.values
                                        .map((v) => v.valueText?.trim())
                                        .filter(Boolean)
                                        .map((name, i) => (
                                          <span key={i} className={styles.recapParticipantNameChip}>{name}</span>
                                        ))}
                                    </div>
                                  ) : isWeightCard(card) ? (
                                    <div className={styles.recapWeightAnswer}>
                                      {entry.values.map((value, i) => (
                                        <span key={i} className={styles.recapWeightBadge}>
                                          {formatValueShort(value, card.unit)}
                                        </span>
                                      ))}
                                    </div>
                                  ) : (
                                    <p className={styles.boardEntryValues}>
                                      {entry.values.map((value) => formatValueShort(value, card.unit)).join(" • ")}
                                    </p>
                                  )}
                                </button>
                              );
                            })}
                          </div>
                        </div>
                      ) : null}
                    </div>
                  );
                })}
              </div>
            )}
          </>
        ) : null}

    </>
  );
}