import type { BrowsableSourceItemView, Meal, SourceView, WeekDay } from "@batch-cooking/shared";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { apiClient } from "../../api/client";
import { SourceItemPreviewPanel, type SourceItemPreviewState } from "./SourceItemPreviewPanel";
import { SourceItemTable } from "./SourceItemTable";
import "./recipes.scss";

/** Debounce for the search field — same idea/value as `RecipesPage`'s own search. */
const SEARCH_DEBOUNCE_MS = 300;

type EnabledSourcesState =
  | { status: "loading" }
  | { status: "loaded"; sources: SourceView[] }
  | { status: "error" };

type BrowseState =
  | { status: "loading" }
  | { status: "loaded"; items: BrowsableSourceItemView[]; nextCursor: string | null }
  | { status: "error" };

/**
 * "Sources" tab content of the recipe catalog (`RecipesPage`) — a
 * self-contained master-detail pair of its own (source selector + browsable
 * list on the left, `SourceItemPreviewPanel` on the right), independent of
 * `RecipeTable`/`RecipeDetailPanel`: it browses a source's *live* catalog
 * (`GET /sources/:sourceKey/browse`), not the saved `Recipe` table, so it
 * doesn't share their `RecipeTab`-based fetching at all.
 *
 * Selecting an already-imported item navigates straight to its real
 * recipe (`/recettes/:id`, leaving this tab) — selecting one that isn't
 * imported yet shows a read-only preview here instead. Turning that
 * preview into an actual saved recipe (reviewing/fixing unresolved
 * ingredients first) is a later stage of the same plan, not built here.
 *
 * `onSelectImportedRecipe` hands back the id instead of this panel
 * navigating anywhere itself — what "viewing" an already-imported item
 * means depends on the caller: `RecipesPage` switches its own active tab
 * away from `"sources"` (its `RecipeDetailPanel`/`RecipeTable` only render
 * outside that tab, so without switching first the URL would change but
 * this panel would keep rendering over it) and navigates to the recipe's
 * detail page, while `RecipePickerDialog` instead treats it exactly like
 * picking that recipe from one of the regular tabs — moving to its own
 * confirm-portions step, no navigation at all.
 */
export function RecipeSourcesPanel({
  onSelectImportedRecipe,
  planningSlot,
}: {
  onSelectImportedRecipe: (recipeId: number) => void;
  /** Forwarded as-is to `SourceItemPreviewPanel` — see its own doc comment. Only ever set by `RecipePickerDialog`. */
  planningSlot?: { date: string; weekDay: WeekDay; meal: Meal };
}) {
  const { t } = useTranslation();

  const [enabledSources, setEnabledSources] = useState<EnabledSourcesState>({ status: "loading" });
  const [selectedSourceKey, setSelectedSourceKey] = useState<string | null>(null);
  const [search, setSearch] = useState("");
  const [debouncedSearch, setDebouncedSearch] = useState("");
  const [browseState, setBrowseState] = useState<BrowseState>({ status: "loading" });
  const [selectedExternalId, setSelectedExternalId] = useState<string | null>(null);
  const [previewState, setPreviewState] = useState<SourceItemPreviewState>({ status: "empty" });

  // Loaded once — which sources exist, crossed with which the household
  // has enabled (`/parametres/foyer`). Defaults the selector to the first
  // enabled one, if any.
  useEffect(() => {
    let cancelled = false;
    Promise.all([apiClient.getSources(), apiClient.getHouseSourceIds()])
      .then(([sources, enabledIds]) => {
        if (cancelled) return;
        const enabled = sources.filter((source) => enabledIds.includes(source.id));
        setEnabledSources({ status: "loaded", sources: enabled });
        setSelectedSourceKey((current) => current ?? enabled[0]?.key ?? null);
      })
      .catch(() => {
        if (!cancelled) setEnabledSources({ status: "error" });
      });
    return () => {
      cancelled = true;
    };
  }, []);

  useEffect(() => {
    const timeout = window.setTimeout(() => setDebouncedSearch(search), SEARCH_DEBOUNCE_MS);
    return () => window.clearTimeout(timeout);
  }, [search]);

  useEffect(() => {
    if (selectedSourceKey === null) return;
    let cancelled = false;
    setBrowseState({ status: "loading" });
    setSelectedExternalId(null);
    setPreviewState({ status: "empty" });

    apiClient
      .browseSource(selectedSourceKey, { query: debouncedSearch.trim() || undefined })
      .then(({ items, nextCursor }) => {
        if (!cancelled) setBrowseState({ status: "loaded", items, nextCursor });
      })
      .catch(() => {
        if (!cancelled) setBrowseState({ status: "error" });
      });

    return () => {
      cancelled = true;
    };
  }, [selectedSourceKey, debouncedSearch]);

  function handleLoadMore() {
    if (selectedSourceKey === null || browseState.status !== "loaded" || !browseState.nextCursor) {
      return;
    }
    const cursor = browseState.nextCursor;
    apiClient
      .browseSource(selectedSourceKey, { query: debouncedSearch.trim() || undefined, cursor })
      .then(({ items, nextCursor }) => {
        setBrowseState((prev) =>
          prev.status === "loaded"
            ? { status: "loaded", items: [...prev.items, ...items], nextCursor }
            : prev,
        );
      })
      .catch(() => setBrowseState({ status: "error" }));
  }

  function handleSelectItem(item: BrowsableSourceItemView) {
    if (item.alreadyImported && item.recipeId !== null) {
      onSelectImportedRecipe(item.recipeId);
      return;
    }
    if (selectedSourceKey === null) return;
    setSelectedExternalId(item.externalId);
    setPreviewState({ status: "loading" });
    apiClient
      .previewSourceItem(selectedSourceKey, item.externalId)
      .then((draft) => setPreviewState({ status: "loaded", draft }))
      .catch(() => setPreviewState({ status: "error" }));
  }

  if (enabledSources.status === "loading") {
    return <p className="recipes-page__status">{t("recipes.loading")}</p>;
  }
  if (enabledSources.status === "error") {
    return (
      <p className="recipes-page__status recipes-page__status--error">{t("common.loadError")}</p>
    );
  }
  if (enabledSources.sources.length === 0) {
    return (
      <p className="recipes-page__status">
        {t("recipes.sources.noneEnabled")}{" "}
        <Link to="/parametres/foyer">{t("recipes.sources.noneEnabledLink")}</Link>
      </p>
    );
  }

  return (
    <>
      <div className="recipes-page__header recipes-page__header--sources">
        {enabledSources.sources.length > 1 && (
          <select
            className="source-sources-select"
            aria-label={t("recipes.sources.sourceLabel")}
            value={selectedSourceKey ?? ""}
            onChange={(e) => setSelectedSourceKey(e.target.value)}
          >
            {enabledSources.sources.map((source) => (
              <option key={source.key} value={source.key}>
                {source.name}
              </option>
            ))}
          </select>
        )}
        <input
          type="search"
          className="recipes-page__search"
          placeholder={t("recipes.sources.searchPlaceholder")}
          value={search}
          onChange={(e) => setSearch(e.target.value)}
        />
      </div>

      <div className="recipes-page__catalog">
        {browseState.status === "loading" && (
          <p className="recipes-page__status">{t("recipes.sources.loading")}</p>
        )}
        {browseState.status === "error" && (
          <p className="recipes-page__status recipes-page__status--error">
            {t("recipes.sources.loadError")}
          </p>
        )}
        {browseState.status === "loaded" && browseState.items.length === 0 && (
          <p className="recipes-page__status">{t("recipes.sources.empty")}</p>
        )}
        {browseState.status === "loaded" && browseState.items.length > 0 && (
          <div className="source-items-column">
            <SourceItemTable
              items={browseState.items}
              selectedExternalId={selectedExternalId}
              onSelect={handleSelectItem}
            />
            {browseState.nextCursor && (
              <button type="button" className="source-items-load-more" onClick={handleLoadMore}>
                {t("recipes.sources.loadMore")}
              </button>
            )}
          </div>
        )}

        <SourceItemPreviewPanel state={previewState} planningSlot={planningSlot} />
      </div>
    </>
  );
}
