import type { CatalogPlaceholderGroupView } from "@batch-cooking/shared";
import { useCallback, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { adminApiClient } from "../../../api/admin-client";
import "../admin-page.scss";
import "./catalog-page.scss";
import { type CatalogTab, formatDate, reviewedParam, splitSpellings } from "./catalog";

type CatalogState =
  | { status: "loading" }
  | { status: "loaded"; groups: CatalogPlaceholderGroupView[] }
  | { status: "error" };

/**
 * Off-catalog ingredient review. Lists every placeholder `Ingredient` (the
 * free text users typed when the seeded catalog fell short — see
 * `Ingredient.isPlaceholder` in the API schema), grouped by normalized
 * name, so a maintainer sees what the catalog is missing and how many
 * recipes are waiting on it. Actions are deliberately minimal: mark a gap
 * as handled, or purge rows no recipe references any more. Actually adding
 * the catalog entry stays a manual edit of `reference-seed-data.ts` + the
 * locale files.
 */
export function CatalogPage() {
  const { t } = useTranslation();
  const [tab, setTab] = useState<CatalogTab>("pending");
  const [state, setState] = useState<CatalogState>({ status: "loading" });
  const [pendingIds, setPendingIds] = useState<number[] | null>(null);
  const [pruneMessage, setPruneMessage] = useState<string | null>(null);
  const [isPruning, setIsPruning] = useState(false);

  // Monotonic id of the most recent `load()` call — a response from an
  // earlier one (switching tabs fast, or React 18 StrictMode's double-mount
  // firing the effect twice) must not clobber the current tab's data.
  const requestSeq = useRef(0);

  const load = useCallback((forTab: CatalogTab) => {
    const seq = ++requestSeq.current;
    setState({ status: "loading" });
    adminApiClient
      .getPlaceholders(reviewedParam(forTab))
      .then((groups) => {
        if (seq === requestSeq.current) setState({ status: "loaded", groups });
      })
      .catch(() => {
        if (seq === requestSeq.current) setState({ status: "error" });
      });
  }, []);

  useEffect(() => {
    load(tab);
  }, [tab, load]);

  function markReviewed(group: CatalogPlaceholderGroupView) {
    setPendingIds(group.ingredientIds);
    adminApiClient
      .markPlaceholdersReviewed({ ingredientIds: group.ingredientIds })
      .then(() => load(tab))
      .catch(() => load(tab))
      .finally(() => setPendingIds(null));
  }

  function pruneOrphans() {
    setIsPruning(true);
    setPruneMessage(null);
    adminApiClient
      .pruneOrphanPlaceholders()
      .then(({ deleted }) => {
        setPruneMessage(
          deleted === 0
            ? t("admin.catalog.prunedNone")
            : t("admin.catalog.pruned", { count: deleted }),
        );
        load(tab);
      })
      .catch(() => setPruneMessage(t("admin.common.loadError")))
      .finally(() => setIsPruning(false));
  }

  return (
    <div className="admin-page">
      <h1 className="admin-page__title">{t("admin.catalog.title")}</h1>
      <p className="admin-page__lead">{t("admin.catalog.lead")}</p>

      <div className="catalog-toolbar">
        <div className="catalog-tabs" role="tablist">
          {(["pending", "reviewed"] as const).map((value) => (
            <button
              key={value}
              type="button"
              role="tab"
              aria-selected={tab === value}
              className={tab === value ? "active" : undefined}
              onClick={() => setTab(value)}
            >
              {t(`admin.catalog.tab.${value}`)}
            </button>
          ))}
        </div>
        <button type="button" onClick={pruneOrphans} disabled={isPruning}>
          {isPruning ? t("admin.catalog.pruning") : t("admin.catalog.pruneOrphans")}
        </button>
      </div>
      {pruneMessage && <p className="catalog-prune-message">{pruneMessage}</p>}

      {state.status === "loading" && (
        <p className="admin-page__placeholder">{t("admin.common.loading")}</p>
      )}
      {state.status === "error" && (
        <p className="admin-page__placeholder">{t("admin.common.loadError")}</p>
      )}
      {state.status === "loaded" &&
        (state.groups.length === 0 ? (
          <p className="admin-page__placeholder">{t("admin.catalog.empty")}</p>
        ) : (
          <ul className="catalog-list">
            {state.groups.map((group) => (
              <PlaceholderGroupCard
                key={group.normalizedName}
                group={group}
                busy={pendingIds === group.ingredientIds}
                onMarkReviewed={() => markReviewed(group)}
              />
            ))}
          </ul>
        ))}
    </div>
  );
}

function PlaceholderGroupCard({
  group,
  busy,
  onMarkReviewed,
}: {
  group: CatalogPlaceholderGroupView;
  busy: boolean;
  onMarkReviewed: () => void;
}) {
  const { t } = useTranslation();
  const { headline, variants } = splitSpellings(group);
  const firstSeen = formatDate(group.firstSeenAt);

  return (
    <li className="catalog-card">
      <div className="catalog-card__head">
        <h2 className="catalog-card__name">{headline}</h2>
        <span className="catalog-card__count">
          {t("admin.catalog.recipeCount", { count: group.recipeCount })}
        </span>
      </div>

      {variants.length > 0 && (
        <p className="catalog-card__variants">
          {t("admin.catalog.alsoWritten", { variants: variants.join(" · ") })}
        </p>
      )}

      {group.sampleRecipes.length > 0 && (
        <p className="catalog-card__recipes">
          {t("admin.catalog.seenIn")} {group.sampleRecipes.map((recipe) => recipe.name).join(", ")}
        </p>
      )}

      <div className="catalog-card__foot">
        {firstSeen && (
          <span className="catalog-card__seen">
            {t("admin.catalog.firstSeen", { date: firstSeen })}
          </span>
        )}
        {!group.allReviewed && (
          <button type="button" onClick={onMarkReviewed} disabled={busy}>
            {busy ? t("admin.catalog.marking") : t("admin.catalog.markReviewed")}
          </button>
        )}
      </div>
    </li>
  );
}
