"use client";

import Image from "next/image";
import { useCallback, useEffect, useMemo, useRef, useState, type MutableRefObject } from "react";
import { PredictionCardFactory } from "@/features/predictions/components/tiles/prediction-card-factory";
import { MainTabBar } from "@/features/predictions/components/main-tab-bar";
import {
  getThemeFromSexValue,
  type PredictionTheme,
} from "@/features/predictions/domain/prediction-theme";
import { getCardIcon } from "@/features/predictions/domain/card-icons";
import type { PredictionTileController } from "@/features/predictions/hooks/use-prediction-tile-controller";
import styles from "@/features/predictions/styles/predictions.module.css";
import { usePredictionTheme } from "@/features/predictions/context/prediction-theme-context";
import {
  getMyPredictionEntries,
  getPredictionBoard,
  getPredictionCards,
  getPredictionScoreboard,
  upsertPredictionEntry,
} from "@/lib/predictions-client";
import { getCurrentProjectId, getApiUrl, PROJECT_CHANGED_EVENT } from "@/lib/auth";
import { getMyProjects } from "@/lib/projects-client";
import type {
  PredictionBoardResponse,
  PredictionCard,
  PredictionEntry,
  PredictionScoreboardItem,
} from "@/types/predictions";
import type { ProjectSummary } from "@/types/projects";

type FinalAvatarProps = Readonly<{
  apiUrl: string;
  className: string;
  user: {
    displayName: string | null;
    username: string;
    profileImageUrl: string | null;
    profileBgColor: string | null;
  };
}>;

function formatNumber(value: number, unit?: string | null) {
  const suffix = unit ? ` ${unit}` : "";
  return `${value}${suffix}`;
}

function formatDateLabel(value: string) {
  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 getDisplayName(user: { displayName: string | null; username: string }) {
  return user.displayName?.trim() || user.username;
}

function getInitials(label: string) {
  return label
    .split(/\s+/)
    .filter(Boolean)
    .slice(0, 2)
    .map((part) => part[0]?.toUpperCase() ?? "")
    .join("") || "?";
}

function resolveImageSrc(apiUrl: string, imageUrl: string | null) {
  if (!imageUrl) {
    return null;
  }

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

  return `${apiUrl}${imageUrl}`;
}

function formatValueShort(
  value: { fieldLabel?: string; field?: { label: string }; valueText: string | null; valueNumber: number | null; valueDate: string | null },
  unit?: string | null,
) {
  if (value.valueNumber != null) {
    return formatNumber(value.valueNumber, unit);
  }

  if (value.valueText && value.valueText.trim().length > 0) {
    return value.valueText;
  }

  if (value.valueDate) {
    return formatDateLabel(value.valueDate.slice(0, 10));
  }

  return "-";
}

function FinalAvatar({
  apiUrl,
  className,
  user,
}: FinalAvatarProps) {
  const label = getDisplayName(user);
  const imageSrc = resolveImageSrc(apiUrl, user.profileImageUrl);
  const style = imageSrc
    ? { backgroundColor: user.profileBgColor ?? undefined, backgroundImage: `url("${imageSrc}")` }
    : { background: user.profileBgColor ?? undefined };

  return (
    <div className={className} style={style} aria-hidden="true">
      {imageSrc ? null : <span>{getInitials(label)}</span>}
    </div>
  );
}

export default function PredictionsPage() {
  const [projectRefreshKey, setProjectRefreshKey] = useState(0);
  const [randomTheme] = useState<PredictionTheme>(() => {
    if (typeof window === "undefined") return "girl";
    const last = localStorage.getItem("ljp_theme_last") as PredictionTheme | null;
    return last === "girl" ? "boy" : "girl";
  });
  const hasWrittenRandomTheme = useRef(false) as MutableRefObject<boolean>;
  useEffect(() => {
    if (hasWrittenRandomTheme.current) return;
    hasWrittenRandomTheme.current = true;
    localStorage.setItem("ljp_theme_last", randomTheme);
  }, [randomTheme]);
  const [draftSexValuesByBaby, setDraftSexValuesByBaby] = useState<Record<number, string | null>>({});

  const [cards, setCards] = useState<PredictionCard[]>([]);
  const [entriesByCardId, setEntriesByCardId] = useState<Record<string, PredictionEntry>>({});
  const [board, setBoard] = useState<PredictionBoardResponse | null>(null);
  const [scoreboard, setScoreboard] = useState<PredictionScoreboardItem[]>([]);
  const [currentProject, setCurrentProject] = useState<ProjectSummary | null>(null);
  const [activeBabyIndex, setActiveBabyIndex] = useState(1);
  const [babyCount, setBabyCount] = useState(1);
  const [loading, setLoading] = useState(true);
  const [message, setMessage] = useState("");
  const [globalMessage, setGlobalMessage] = useState("");
  const [globalSaving, setGlobalSaving] = useState(false);
  const [dirtyVersion, setDirtyVersion] = useState(0);

  const controllersRef = useRef<Record<string, PredictionTileController>>({});

  const entryKey = useCallback((cardId: string, selectedBabyIndex: number) => {
    return `${cardId}::${selectedBabyIndex}`;
  }, []);

  const isGameClosed = board?.game?.status === "CLOSED";
  const isFinalized = Boolean(board?.game?.finalized);

  useEffect(() => {
    const onProjectChanged = () => {
      setProjectRefreshKey((current) => current + 1);
    };

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

  useEffect(() => {
    setLoading(true);
    setMessage("");

    Promise.all([
      getPredictionCards(),
      getMyPredictionEntries(),
      getPredictionBoard(),
      getPredictionScoreboard(),
      getMyProjects(),
    ])
      .then(([nextCards, entries, nextBoard, nextScoreboard, projects]) => {
        setCards(nextCards);
        setBoard(nextBoard);
        setScoreboard(nextScoreboard);

        const currentProjectId = getCurrentProjectId();
        const selectedProject =
          projects.find((project) => project.id === currentProjectId) ?? projects[0] ?? null;

        setCurrentProject(selectedProject ?? null);
        const nextBabyCount = Math.max(1, selectedProject?.babyCount ?? 1);
        setBabyCount(nextBabyCount);
        setActiveBabyIndex((current) => Math.min(current, nextBabyCount));

        const byCardId = entries.reduce<Record<string, PredictionEntry>>((accumulator, entry) => {
          const key = entryKey(entry.cardId, entry.selectedBabyIndex ?? 1);
          accumulator[key] = entry;
          return accumulator;
        }, {});

        setEntriesByCardId(byCardId);

        setDraftSexValuesByBaby({});
      })
      .catch((error) => {
        setMessage(error instanceof Error ? error.message : "Impossible de charger les pronostics");
      })
      .finally(() => {
        setLoading(false);
      });
  }, [entryKey, projectRefreshKey]);

  const makeCardSaveHandler = useCallback((babyIdx: number) => {
    return async (cardId: string, payload: { values: { fieldId: string; valueText?: string; valueNumber?: number; valueDate?: string }[] }) => {
      const updated = await upsertPredictionEntry(cardId, {
        ...payload,
        selectedBabyIndex: babyIdx,
      });
      setEntriesByCardId((current) => ({
        ...current,
        [entryKey(cardId, babyIdx)]: updated,
      }));
      return updated;
    };
  }, [entryKey]);

  const saveHandler = async (cardId: string, payload: { values: { fieldId: string; valueText?: string; valueNumber?: number; valueDate?: string }[] }) => {
    const updated = await upsertPredictionEntry(cardId, {
      ...payload,
      selectedBabyIndex: activeBabyIndex,
    });
    setEntriesByCardId((current) => ({
      ...current,
      [entryKey(cardId, activeBabyIndex)]: updated,
    }));
    return updated;
  };

  const registerController = useCallback(
    (key: string, controller: PredictionTileController | null) => {
      if (controller) {
        controllersRef.current[key] = controller;
      } else {
        delete controllersRef.current[key];
      }
    },
    [],
  );

  const notifyDirtyChange = useCallback(() => {
    setDirtyVersion((v) => v + 1);
  }, []);

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

  const totalPredictions = useMemo(
    () =>
      board?.cards.reduce((total, card) => {
        const entriesForBaby = card.entries.filter(
          (entry) => (entry.selectedBabyIndex ?? 1) === activeBabyIndex,
        );
        return total + entriesForBaby.length;
      }, 0) ?? 0,
    [activeBabyIndex, board],
  );

  const dirtyBabyIndices = useMemo(() => {
    const indices = new Set<number>();
    for (const key of Object.keys(controllersRef.current)) {
      const controller = controllersRef.current[key];
      if (controller?.dirty) {
        const babyIdx = Number(key.split("::")[1]);
        if (!Number.isNaN(babyIdx)) {
          indices.add(babyIdx);
        }
      }
    }
    return indices;
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [dirtyVersion]);

  const winnerSummary = useMemo(() => {
    if (!isFinalized || scoreboard.length === 0) {
      return null;
    }

    const topScore = scoreboard[0].totalPoints;
    const winners = scoreboard.filter((item) => item.totalPoints === topScore);

    return {
      winners,
      topScore,
    };
  }, [isFinalized, scoreboard]);

  const mainWinnerId = winnerSummary?.winners[0]?.userId ?? null;

  const winnerResponses = useMemo(() => {
    if (!isFinalized || !mainWinnerId) {
      return [];
    }

    return (board?.cards ?? []).map((card) => ({
      card,
      entry:
        card.entries.find(
          (entry) =>
            entry.userId === mainWinnerId && (entry.selectedBabyIndex ?? 1) === activeBabyIndex,
        ) ?? null,
    }));
  }, [activeBabyIndex, board?.cards, isFinalized, mainWinnerId]);

  const winnerResponsesByCardId = useMemo(() => {
    return winnerResponses.reduce<Record<string, typeof winnerResponses[number]>>((accumulator, item) => {
      accumulator[item.card.id] = item;
      return accumulator;
    }, {});
  }, [winnerResponses]);

  const myResponses = useMemo(() => {
    return cards.map((card) => ({
      card,
      entry: entriesByCardId[entryKey(card.id, activeBabyIndex)] ?? null,
    }));
  }, [activeBabyIndex, cards, entriesByCardId, entryKey]);

  const { setTheme } = usePredictionTheme();

  const predictionTheme = useMemo<PredictionTheme>(() => {
    const sexCard = cards.find((card) => card.code === "legacy_sex");

    if (!sexCard) {
      return randomTheme;
    }

    // Priorité 1 : sélection en brouillon pour le bébé actif
    const draftTheme = getThemeFromSexValue(draftSexValuesByBaby[activeBabyIndex]);
    if (draftTheme) {
      return draftTheme;
    }

    // Priorité 2 : valeur enregistrée
    const sexEntry = entriesByCardId[entryKey(sexCard.id, activeBabyIndex)];
    if (sexEntry) {
      const savedSexValue =
        sexEntry.values.find((value) => value.valueText && value.valueText.trim().length > 0)?.valueText ??
        null;
      const savedTheme = getThemeFromSexValue(savedSexValue);
      if (savedTheme) return savedTheme;
    }

    return randomTheme;
  }, [activeBabyIndex, cards, draftSexValuesByBaby, entriesByCardId, entryKey, randomTheme]);

  useEffect(() => {
    setTheme(predictionTheme);
  }, [predictionTheme, setTheme]);

  const heroBabyImage =
    predictionTheme === "girl"
      ? "/depotARanger/bebe-sur-un-nuage-girl.png"
      : "/depotARanger/bebe-sur-un-nuage.png";

  const heroProjectImageUrl = currentProject?.projectImageUrl
    ? `${getApiUrl()}${currentProject.projectImageUrl}`
    : null;
  const heroProjectBg = currentProject?.projectBgColor ?? "#e4f4fb";
  const apiUrl = getApiUrl();

  const onGlobalSave = async () => {
    if (isGameClosed) {
      setGlobalMessage("Le jeu est cloture. Les pronostics ne peuvent plus etre modifies.");
      return;
    }

    const dirtyKeys = Object.keys(controllersRef.current).filter(
      (key) => controllersRef.current[key]?.dirty,
    );

    if (dirtyKeys.length === 0) {
      setGlobalMessage("Aucune modification a enregistrer.");
      return;
    }

    setGlobalSaving(true);
    setGlobalMessage("");

    let successCount = 0;
    let failureCount = 0;

    for (const key of dirtyKeys) {
      const controller = controllersRef.current[key];
      if (!controller) {
        failureCount += 1;
        continue;
      }

      const ok = await controller.submit();
      if (ok) {
        successCount += 1;
      } else {
        failureCount += 1;
      }
    }

    if (failureCount === 0) {
      setGlobalMessage(`${successCount} categorie(s) enregistree(s) avec succes.`);
    } else {
      setGlobalMessage(
        `${successCount} categorie(s) enregistree(s), ${failureCount} en echec. Verifie les tuiles en erreur.`,
      );
    }

    setGlobalSaving(false);
  };

  return (
    <>
      <section className={`${styles.hero} ${predictionTheme === "girl" ? styles.heroSoftPink : ""}`}>
        <div className={`${styles.heroCornerDecoFrame} ${styles.heroCornerDecoFrameLeft}`} aria-hidden="true">
          <Image
            src="/depotARanger/baniere-deco-gauche-haut.png"
            alt=""
            fill
            sizes="128px"
            className={`${styles.heroCornerDeco} ${styles.heroCornerDecoLeft}`}
          />
        </div>
        <div className={`${styles.heroCornerDecoFrame} ${styles.heroCornerDecoFrameRight}`} aria-hidden="true">
          <Image
            src="/depotARanger/baniere-deco-droite-haut.png"
            alt=""
            fill
            sizes="148px"
            className={`${styles.heroCornerDeco} ${styles.heroCornerDecoRight}`}
          />
        </div>
        <div className={styles.heroInner}>
          <h2>
            {isFinalized ? "Concours terminé !" : "Bienvenue !"}
          </h2>
          <p>
            {isFinalized
              ? "Le classement est validé. Retrouve le gagnant et tes réponses."
              : "Amusez-vous à pronostiquer l'arrivée de Bubulle !"}
          </p>
        </div>
        <div className={styles.heroProjectPhoto} aria-hidden="true" style={{ background: heroProjectBg }}>
          {heroProjectImageUrl ? (
            // eslint-disable-next-line @next/next/no-img-element
            <img
              src={heroProjectImageUrl}
              alt=""
              style={{ width: "100%", height: "100%", objectFit: "cover" }}
            />
          ) : (
            <Image
              src={heroBabyImage}
              alt=""
              fill
              sizes="124px"
              style={{ objectFit: "cover" }}
            />
          )}
        </div>
        <div className={styles.heroBannerRow}>
          <div className={styles.heroBannerImageWrap} aria-hidden="true">
            <Image
              src="/depotARanger/baniere-familleGauche.png"
              alt=""
              width={1200}
              height={504}
              sizes="199px"
              className={`${styles.heroBannerImage} ${styles.heroBannerImageLeft}`}
            />
          </div>
          <div className={styles.heroBannerImageWrap} aria-hidden="true">
            <Image
              src="/depotARanger/baniere-familleDroite.png"
              alt=""
              width={1248}
              height={624}
              sizes="202px"
              className={`${styles.heroBannerImage} ${styles.heroBannerImageRight}`}
            />
          </div>
        </div>
        <div className={styles.heroWave} aria-hidden="true">
          <svg
            viewBox="0 0 1415 142"
            preserveAspectRatio="none"
            xmlns="http://www.w3.org/2000/svg"
            style={{ width: "100%", height: "50px", display: "block" }}
          >
            <path d="M0 41.8835V142H1414.29V16.0802C1395.91 9.71545 1350.59 1.83686 1316.33 21.2409C1281.38 0.59831 1224.27 6.27502 1082.5 34.6586C928.42 6.79108 743.327 21.2409 627.151 48.5923C569.556 27.4337 490.3 26.9176 455.841 55.8172C456.17 51.1726 447.276 37.755 409.076 21.2409C361.326 0.598308 327.851 25.8855 277.148 21.2409C226.444 16.5963 127.043 -35.0102 0 41.8835Z" fill={predictionTheme === "girl" ? "#ffb5aa" : "#A7DFC1"}/>
          </svg>
        </div>
      </section>

      <MainTabBar />

      <section className={styles.sectionTitle}>
        <h2>Vos pronostics</h2>
      </section>

      {babyCount > 1 && !isFinalized ? (
        <section className={`panel ${styles.babyBanner}`}>
          <p className={styles.babyBannerLabel}>Vous saisissez vos pronostics pour :</p>
          <div className={styles.babyTabs}>
            {Array.from({ length: babyCount }, (_, index) => index + 1).map((babyIndex) => (
              <button
                key={babyIndex}
                className={`${styles.babyTab} ${babyIndex === activeBabyIndex ? styles.babyTabActive : ""}`}
                onClick={() => {
                  setActiveBabyIndex(babyIndex);
                  setGlobalMessage("");
                }}
                type="button"
              >
                {getBabyLabel(babyIndex)}
                {dirtyBabyIndices.has(babyIndex) ? (
                  <span className={styles.babyTabDirtyDot} title="Modifications non enregistrees" />
                ) : null}
              </button>
            ))}
          </div>
        </section>
      ) : null}

      {message ? (
        <section className={`panel ${styles.panelBlock}`}>
          <p className={styles.error}>{message}</p>
        </section>
      ) : null}

      {loading ? (
        <section className={`panel ${styles.panelBlock}`}>
          <p>Chargement des cartes...</p>
        </section>
      ) : null}

      {!loading && cards.length === 0 ? (
        <section className={`panel ${styles.panelBlock}`}>
          <p className={styles.empty}>Aucune carte active pour le moment.</p>
        </section>
      ) : null}

      {!loading && isFinalized ? (
        <section className={styles.finalResults}>
          {winnerSummary ? (
            <div className={styles.finalWinnerCard}>
              <div className={styles.finalWinnerMain}>
                <span className={styles.recapSectionEyebrow}>Résultats validés</span>
                <div className={styles.finalWinnerIdentityRow}>
                  <FinalAvatar apiUrl={apiUrl} className={styles.finalWinnerAvatar} user={winnerSummary.winners[0]} />
                  <div>
                    <p className={styles.finalWinnerKicker}>Champion du concours</p>
                    <h3>{winnerSummary.winners.map((winner) => getDisplayName(winner)).join(" / ")}</h3>
                    <p className={styles.finalWinnerSubtitle}>
                      {winnerSummary.topScore} points au compteur{winnerSummary.winners.length > 1 ? " chacun" : ""}.
                    </p>
                  </div>
                </div>
              </div>
              <div className={styles.finalWinnerScoreBadge}>
                <strong>{winnerSummary.topScore}</strong>
                <span>points</span>
              </div>
            </div>
          ) : (
            <p className={styles.empty}>Classement indisponible.</p>
          )}

          {scoreboard.length > 0 ? (
            <div className={styles.finalScoreboardList} aria-label="Classement final">
              {scoreboard.map((item, index) => (
                <article
                  key={item.userId}
                  className={`${styles.finalScoreRow} ${index === 0 ? styles.finalScoreRowWinner : ""}`}
                >
                  <span className={styles.finalRank}>#{index + 1}</span>
                  <FinalAvatar apiUrl={apiUrl} className={styles.finalScoreAvatar} user={item} />
                  <strong>{getDisplayName(item)}</strong>
                  <span className={styles.finalScorePoints}>{item.totalPoints} pts</span>
                </article>
              ))}
            </div>
          ) : null}

          <div className={styles.finalAnswersSection}>
            <div className={styles.recapSectionHeading}>
              <div>
                <span className={styles.recapSectionEyebrow}>Verdict</span>
                <h2>Tes réponses face au résultat</h2>
              </div>
            </div>

            <div className={styles.finalAnswerGrid}>
              {myResponses.map((item) => {
                const winnerItem = winnerResponsesByCardId[item.card.id];
                const winnerValue = winnerItem?.entry?.values.length
                  ? winnerItem.entry.values.map((value) => formatValueShort(value, winnerItem.card.unit)).join(" • ")
                  : "Pas de réponse";
                const myValue = item.entry?.values.length
                  ? item.entry.values.map((value) => formatValueShort(value, item.card.unit)).join(" • ")
                  : "Aucune réponse";

                return (
                  <article key={item.card.id} className={styles.finalAnswerCard}>
                    <div className={styles.finalAnswerIconWrap} aria-hidden="true">
                      <Image src={getCardIcon(item.card.code)} alt="" width={52} height={52} className={styles.finalAnswerIcon} />
                    </div>
                    <div className={styles.finalAnswerText}>
                      <h3>{item.card.title}</h3>
                      <dl>
                        <div>
                          <dt>Résultat</dt>
                          <dd>{winnerValue}</dd>
                        </div>
                        <div>
                          <dt>Toi</dt>
                          <dd>{myValue}</dd>
                        </div>
                      </dl>
                    </div>
                  </article>
                );
              })}
            </div>
          </div>
        </section>
      ) : null}

      {!loading && !isFinalized && cards.length > 0 ? (
        <>
          {Array.from({ length: babyCount }, (_, i) => i + 1).map((babyIdx) => (
            <section
              key={babyIdx}
              className={styles.tilesGrid}
              style={{ display: babyIdx === activeBabyIndex ? undefined : "none" }}
              aria-hidden={babyIdx !== activeBabyIndex}
            >
              {cards.map((card) => (
                <PredictionCardFactory
                  key={`${card.id}::${babyIdx}`}
                  card={card}
                  entry={entriesByCardId[entryKey(card.id, babyIdx)]}
                  theme={predictionTheme}
                  disabled={isGameClosed}
                  showSubmitButton={false}
                  onSave={makeCardSaveHandler(babyIdx)}
                  onController={(cardId, controller) =>
                    registerController(`${cardId}::${babyIdx}`, controller)
                  }
                  onDirtyChange={notifyDirtyChange}
                  onSexSelect={(value) => {
                    setDraftSexValuesByBaby((current) => ({
                      ...current,
                      [babyIdx]: value,
                    }));
                  }}
                />
              ))}
            </section>
          ))}

          <section className={`panel ${styles.globalSaveBar}`}>
            <div>
              <strong>Sauvegarde globale</strong>
              <p className={styles.activityMeta}>
                {babyCount > 1
                  ? "Toutes tes modifications (pour tous les bebes) seront enregistrees."
                  : "Seules les valeurs changees seront envoyees et historisees."}
              </p>
            </div>
            <button
              className="btn btn-primary"
              onClick={() => {
                void onGlobalSave();
              }}
              disabled={globalSaving || isGameClosed}
            >
              {globalSaving ? "Enregistrement..." : "Enregistrer mes pronostics"}
            </button>
          </section>

          {globalMessage ? (
            <section className={`panel ${styles.panelBlock}`}>
              <p className={failureCountFromMessage(globalMessage) ? styles.error : styles.success}>
                {globalMessage}
              </p>
            </section>
          ) : null}
        </>
      ) : null}
    </>
  );
}

function failureCountFromMessage(message: string) {
  return message.includes("echec");
}
