import { useEffect, useMemo, useState } from "react";
import type { CardLayout, DataCard, ImagePopupState, SettingsState, TextState } from "./types";
import { DataCardWidget } from "./components/DataCardWidget";
import { TextPopup } from "./components/TextPopup";
import { ImagePopup } from "./components/ImagePopup";
import { NotFullscreen } from "./components/NotFullscreen";

const GRID_COLS = 4;
const GRID_ROWS = 4;

/** Deterministic hash of a string → non-negative integer */
function hashStr(s: string): number {
  let h = 0;
  for (let i = 0; i < s.length; i++) {
    h = Math.imul(31, h) + s.charCodeAt(i) | 0;
  }
  return Math.abs(h);
}

/**
 * Process cards in insertion order.
 * If two cards share the same starting (grid_col, grid_row), the later one
 * is relocated to a free starting position chosen deterministically via its id.
 */
function assignLayouts(cards: DataCard[]): Array<{ card: DataCard; resolvedLayout: CardLayout }> {
  const occupied = new Set<string>();

  return cards.map((card) => {
    const layout: CardLayout = card.layout ?? {
      grid_col: 1,
      grid_row: 4,
      col_span: 1,
      row_span: 1,
    };
    const key = `${layout.grid_col},${layout.grid_row}`;

    if (!occupied.has(key)) {
      occupied.add(key);
      return { card, resolvedLayout: layout };
    }

    // Collect all free starting positions
    const free: Array<[number, number]> = [];
    for (let r = 1; r <= GRID_ROWS; r++) {
      for (let c = 1; c <= GRID_COLS; c++) {
        if (!occupied.has(`${c},${r}`)) free.push([c, r]);
      }
    }

    if (free.length === 0) {
      // Every slot taken – let it overlap at its original position
      return { card, resolvedLayout: layout };
    }

    const [rc, rr] = free[hashStr(card.id) % free.length];
    occupied.add(`${rc},${rr}`);
    return { card, resolvedLayout: { ...layout, grid_col: rc, grid_row: rr } };
  });
}

function App() {
  const [screenStatus, setScreenStatus] = useState<"notfullscreen" | "fullscreen">("notfullscreen");
  const [textState, setTextState] = useState<TextState>({ showing: false, title: "" });
  const [imagePopup, setImagePopup] = useState<ImagePopupState>({ showing: false, image_url: "", caption: "" });
  const [dataCards, setDataCards] = useState<DataCard[]>([]);
  const [settings, setSettings] = useState<SettingsState>({ background_url: "" });
  const [fetchError, setFetchError] = useState(false);

  useEffect(() => {
    const handleFullscreenChange = () => {
      setScreenStatus(document.fullscreenElement ? "fullscreen" : "notfullscreen");
    };
    document.addEventListener("fullscreenchange", handleFullscreenChange);
    return () => document.removeEventListener("fullscreenchange", handleFullscreenChange);
  }, []);

  useEffect(() => {
    if (screenStatus === "fullscreen") {
      const handlePullState = () => {
        fetch(
          "https://shsf-api.reversed.dev/api/exec/15/1c44d8b5-4065-4a54-b259-748561021329/pull_full",
        )
          .then((response) => response.json())
          .then((data) => {
            const state = data.state ?? {};
            setTextState(state.text ?? { showing: false, title: "" });
            setImagePopup(state.image_popup ?? { showing: false, image_url: "", caption: "" });
            setDataCards(state.data_cards ?? []);
            setSettings(state.settings ?? { background_url: "" });
            setFetchError(false);
          })
          .catch((error) => {
            console.error("Error pulling state:", error);
            setFetchError(true);
          });
      };

      handlePullState();
      const interval = setInterval(handlePullState, 5000);
      return () => clearInterval(interval);
    }
  }, [screenStatus]);

  // Stable: only recompute when the serialised card list changes
  const resolvedCards = useMemo(
    () => assignLayouts(dataCards),
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [JSON.stringify(dataCards)],
  );

  if (screenStatus === "notfullscreen") {
    return <NotFullscreen />;
  }

  const isIdle = !textState.showing && !imagePopup.showing && dataCards.length === 0;

  return (
    <div className="w-screen h-screen relative">
      {fetchError && (
        <div className="absolute inset-0 z-50 flex flex-col items-center justify-center bg-black/80 backdrop-blur-sm">
          <span className="text-red-500 text-[10rem] leading-none select-none">!</span>
          <p className="text-white text-5xl font-bold mt-4">Connection failed</p>
          <p className="text-gray-400 text-2xl mt-3">Retrying every 5 seconds…</p>
        </div>
      )}
      {settings.background_url && (
        <img
          src={settings.background_url}
          className="absolute inset-0 w-full h-full object-cover z-0"
          alt=""
        />
      )}
      <TextPopup state={textState} />
      <ImagePopup state={imagePopup} />

      {isIdle && (
        <div className="flex items-center justify-center w-full h-full">
          <p className="text-gray-600 text-lg">
            Waiting for content
            <span className="dot-1">.</span>
            <span className="dot-2">.</span>
            <span className="dot-3">.</span>
          </p>
        </div>
      )}

      {resolvedCards.length > 0 && (
        <div
          className="absolute inset-0 z-0 p-4"
          style={{
            display: "grid",
            gridTemplateColumns: `repeat(${GRID_COLS}, 1fr)`,
            gridTemplateRows: `repeat(${GRID_ROWS}, 1fr)`,
            gap: "12px",
          }}
        >
          {resolvedCards.map(({ card, resolvedLayout }) => (
            <div
              key={card.id}
              style={{
                gridColumn: `${resolvedLayout.grid_col} / span ${resolvedLayout.col_span}`,
                gridRow: `${resolvedLayout.grid_row} / span ${resolvedLayout.row_span}`,
                minWidth: 0,
                minHeight: 0,
              }}
            >
              <DataCardWidget card={card} layout={resolvedLayout} />
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

export default App;
