import type { RecipeTab, SourceView } from "@batch-cooking/shared";
import type { LucideIcon } from "lucide-react";
import { useTranslation } from "react-i18next";
import {
  AccountIcon,
  FavoriteIcon,
  HouseholdIcon,
  PublicIcon,
  SourcesIcon,
} from "../../layouts/nav-icons";
import "./recipes.scss";

/** Prefix marking a tab value as "browse this one enabled source's live catalog" rather than a real `RecipeTab` — see {@link sourceTabValue}/{@link parseSourceTabValue}, the one place this shape is assembled/read apart. */
const SOURCE_TAB_PREFIX = "source:";

/**
 * A tab of the recipe catalog — either a real {@link RecipeTab} (`GET
 * /recipes?tab=`, `recipe.service.ts`'s `listRecipes`) or a `"source:<key>"`
 * value identifying one household-enabled external source, browsed live
 * (`GET /sources/:sourceKey/browse`, `RecipeSourcesPanel`) instead of
 * listing saved `Recipe` rows — one tab per enabled source (see
 * `RecipeTabs` below), so switching between sources is as direct as
 * switching between Perso/Foyer/Publique, not a single generic "Sources"
 * tab hiding a second selector inside it. Kept out of the shared
 * `RecipeTab` type on purpose — the API has no such `tab=` value to
 * validate, this is a web-only browsing mode.
 */
export type RecipesPageTab = RecipeTab | `${typeof SOURCE_TAB_PREFIX}${string}`;

/** Builds the tab value identifying `sourceKey`'s own tab. */
export function sourceTabValue(sourceKey: string): RecipesPageTab {
  return `${SOURCE_TAB_PREFIX}${sourceKey}`;
}

/** The reverse of {@link sourceTabValue} — `null` for any tab that isn't a source tab (a real `RecipeTab`). */
export function parseSourceTabValue(tab: RecipesPageTab): string | null {
  return isSourceTab(tab) ? tab.slice(SOURCE_TAB_PREFIX.length) : null;
}

/** Type guard version of the same check — narrows `tab` to a real `RecipeTab` in the `false` branch, which a plain `parseSourceTabValue(tab) === null` check can't (TS can't see through the function call). Needed wherever the narrowed value gets passed on to something typed as `RecipeTab`, e.g. `apiClient.listRecipes`. */
export function isSourceTab(tab: RecipesPageTab): tab is `${typeof SOURCE_TAB_PREFIX}${string}` {
  return tab.startsWith(SOURCE_TAB_PREFIX);
}

/** The four real, DB-backed tabs, in display order, with their icon — reuses `AccountIcon`/`HouseholdIcon` from the sidebar's own icon set (see nav-icons.tsx) rather than a second "person"/"house" glyph. No "toutes" tab among them: every recipe a viewer can see falls under exactly one of perso/foyer/publique (its own visibility) — see `recipe.service.ts`'s `listRecipes`. */
const REAL_TABS: Array<{ value: RecipeTab; Icon: LucideIcon }> = [
  { value: "favoris", Icon: FavoriteIcon },
  { value: "perso", Icon: AccountIcon },
  { value: "foyer", Icon: HouseholdIcon },
  { value: "publique", Icon: PublicIcon },
];

/**
 * Catalog tab bar — Favoris / Perso / Foyer / Publique, plus one tab per
 * household-enabled source (e.g. "TheMealDB"), in that order. A source's
 * own icon (`SourceView.iconUrl`) is used when it has one, `SourcesIcon`
 * otherwise — unlike the four real tabs, whose label comes from an i18n
 * key, a source tab's label is its own name as-is (there's no translation
 * for an arbitrary household-picked source's name).
 *
 * `tabs` narrows which of the four *real* tabs show — every enabled
 * source still gets its own tab regardless (narrowing individual sources
 * doesn't make sense the way narrowing the four real ones does).
 * `RecipePickerDialog` used to pass just the four real ones, before its
 * own review/import flow existed to hand a picked source item off to; now
 * every caller shows the full set, but the narrowing stays available for
 * a future caller that still wants it.
 */
export function RecipeTabs({
  active,
  onChange,
  sources,
  tabs = REAL_TABS.map((tab) => tab.value),
}: {
  active: RecipesPageTab;
  onChange: (tab: RecipesPageTab) => void;
  /** Household-enabled sources, one tab each. Pass `[]` while still loading (see `useEnabledSources`) — that's indistinguishable from "none enabled" for this bar, which simply renders no source tabs either way. */
  sources: readonly SourceView[];
  tabs?: readonly RecipeTab[];
}) {
  const { t } = useTranslation();

  return (
    <div className="recipe-tabs">
      {REAL_TABS.filter(({ value }) => tabs.includes(value)).map(({ value, Icon }) => (
        <button
          key={value}
          type="button"
          className={`recipe-tabs__tab${value === active ? " active" : ""}`}
          onClick={() => onChange(value)}
        >
          <Icon aria-hidden="true" />
          {t(`recipes.tabs.${value}`)}
        </button>
      ))}
      {sources.map((source) => {
        const value = sourceTabValue(source.key);
        return (
          <button
            key={source.key}
            type="button"
            className={`recipe-tabs__tab${value === active ? " active" : ""}`}
            onClick={() => onChange(value)}
          >
            {source.iconUrl ? (
              <img src={source.iconUrl} alt="" className="recipe-tabs__source-icon" />
            ) : (
              <SourcesIcon aria-hidden="true" />
            )}
            {source.name}
          </button>
        );
      })}
    </div>
  );
}
