"use client";

/**
 * 分類（ホールディングス → 屋号）のカスケード絞り込みを5画面で共有するフック。
 * （指示書AK 段階7 の下準備。★この時点では画面に組み込まない★ — 組み込みは後続担当）
 *
 * 絞り込みの規則そのものは `lib/masters/options.ts`（純粋関数）にある。
 * ここが持つのは ★マスタの取得タイミング★ と ★入力中（draft）の選択★ だけ。
 *
 * ------------------------------------------------------------------------
 * ★★何をフックが持ち、何を画面が持つか★★
 * ------------------------------------------------------------------------
 * | 持ち物 | どこが持つ | なぜ |
 * |---|---|---|
 * | `/groups` `/customers` の取得結果 | ★フック★ | 5画面で同じ。画面ごとに書くと取得条件がずれる |
 * | 絞り込みUIを出してよいか（`available`） | ★フック★ | 取得の成否を知っているのはフックだけ |
 * | 選択肢の配列（`groupOptions` / `customerOptions`） | ★フック★ | カスケードの絞り込み込み |
 * | ★入力中の選択（draft）★ | ★フック★ | セレクトの `value` そのもの。カスケードの整合（傘下外の屋号を外す）をここで守る |
 * | ★確定した選択★ | ★★画面★★ | 「検索」ボタンの有無・位置・他の条件との同時確定は画面ごとに違う |
 * | クエリ文字列の組み立て | ★画面★ | 期間・施設名など他の条件と混ぜる場所が画面だから |
 *
 * ★draft と確定を分ける理由★は `ReportsScreen.tsx`（:84-92）と同じ —
 * ★1操作ごとに API を叩かないため★。画面側は「検索」ボタンで
 * `setConfirmed(classification.draft)` と写し取り、確定値だけを
 * `appendClassificationParams()` でクエリに載せる。
 *
 * ```tsx
 * const { t } = useTheme();
 * const classification = useClassificationFilter(t);
 * const [confirmed, setConfirmed] = useState(EMPTY_SELECTION);
 *
 * const query = useMemo(() => {
 *   const params = new URLSearchParams();
 *   params.set("date", date);
 *   return appendClassificationParams(params, confirmed).toString();
 * }, [date, confirmed]);
 *
 * const handleSearch = (e: React.FormEvent) => {
 *   e.preventDefault();
 *   setConfirmed(classification.draft);   // ← ここで初めて確定する
 * };
 *
 * {classification.available && (
 *   <>
 *     {classification.hasGroups && (
 *       <select
 *         value={String(classification.draft.groupId ?? ALL_OPTION_VALUE)}
 *         onChange={(e) => classification.selectGroup(e.target.value)}
 *       >
 *         {classification.groupOptions.map((o) => (
 *           <option key={o.value} value={o.value}>{o.label}</option>
 *         ))}
 *       </select>
 *     )}
 *     <select
 *       value={String(classification.draft.customerId ?? ALL_OPTION_VALUE)}
 *       onChange={(e) => classification.selectCustomer(e.target.value)}
 *     >
 *       {classification.customerOptions.map((o) => (
 *         <option key={o.value} value={o.value}>{o.label}</option>
 *       ))}
 *     </select>
 *   </>
 * )}
 * ```
 *
 * ★`t` を引数で受ける理由★ — `useTheme()` を直接呼ぶと `lib/` が
 *   `components/theme/ThemeProvider` に依存する（依存の向きが逆になる）。
 *   画面は既に `useTheme()` を呼んでいるので、`t` を1つ渡すだけで済む。
 */
import { useCallback, useEffect, useMemo, useState } from "react";
import {
  ALL_CUSTOMERS_LABEL,
  ALL_GROUPS_LABEL,
  buildCustomerOptions,
  buildGroupOptions,
  EMPTY_SELECTION,
  hasGroupChoices,
  isClassificationAvailable,
  loadClassificationMasters,
  normalizeSelection,
  parseOptionValue,
  UNAVAILABLE_MASTERS,
  type ClassificationMasters,
  type ClassificationSelection,
  type MasterOption,
} from "@/lib/masters/options";

/** 取得の状態。`"loading"` の間は ★まだ空のセレクトを出さない★（`available` が false） */
export type ClassificationStatus = "loading" | "ready" | "unavailable";

export interface ClassificationFilter {
  status: ClassificationStatus;
  /**
   * ★絞り込みUIを出してよいか★。取得中・取得失敗・屋号0件はすべて `false`
   * （指示書AK 10-3 / 落とし穴11）。
   */
  available: boolean;
  /**
   * ホールディングスのセレクトを出してよいか。
   * ★0件なら「全ホールディングス」しか並ばないので出さない★。
   * `available` が false のときは常に false。
   */
  hasGroups: boolean;
  /** ★入力中★ の選択。セレクトの `value` に使う */
  draft: ClassificationSelection;
  groupOptions: MasterOption[];
  /** ★`draft.groupId` の傘下だけ★に絞られた屋号 */
  customerOptions: MasterOption[];
  /** `<select onChange>` からの生値を受ける。★屋号の選択が傘下から外れたら自動で外す★ */
  selectGroup: (value: string) => void;
  selectCustomer: (value: string) => void;
  /** draft を「全ホールディングス / 全屋号」に戻す（確定値は画面側が戻す） */
  reset: () => void;
  /** 取得に失敗したあとの再試行 */
  reload: () => void;
}

export function useClassificationFilter(
  t: (text: string) => string
): ClassificationFilter {
  const [masters, setMasters] =
    useState<ClassificationMasters>(UNAVAILABLE_MASTERS);
  const [status, setStatus] = useState<ClassificationStatus>("loading");
  const [draft, setDraft] = useState<ClassificationSelection>(EMPTY_SELECTION);
  // 再取得のトリガ。値そのものに意味はなく、effect を走らせ直すためだけに使う
  const [reloadToken, setReloadToken] = useState(0);

  useEffect(() => {
    let cancelled = false;
    // eslint-disable-next-line react-hooks/set-state-in-effect -- fetch 起点のマスタ取得
    setStatus("loading");
    void loadClassificationMasters().then((loaded) => {
      // 画面を離れたあとの setState を避ける（マスタ取得は画面本体の fetch より遅れうる）
      if (cancelled) return;
      setMasters(loaded);
      setStatus(loaded.ok ? "ready" : "unavailable");
      // 取得できた一覧に照らして draft の整合を取り直す。
      // ★マスタが未取得の間に draft が入ることは無い（UIを出していない）ので、
      //   ここは実質「何もしない」経路だが、reload 後に古い選択が残る場合に効く★
      setDraft((prev) => normalizeSelection(prev, loaded.customers));
    });
    return () => {
      cancelled = true;
    };
  }, [reloadToken]);

  const available = status === "ready" && isClassificationAvailable(masters);
  const hasGroups = available && hasGroupChoices(masters);

  const groupOptions = useMemo(
    () => buildGroupOptions(masters.groups, t(ALL_GROUPS_LABEL)),
    [masters.groups, t]
  );

  const customerOptions = useMemo(
    () =>
      buildCustomerOptions(
        masters.customers,
        draft.groupId,
        t(ALL_CUSTOMERS_LABEL)
      ),
    [masters.customers, draft.groupId, t]
  );

  const selectGroup = useCallback(
    (value: string) => {
      const groupId = parseOptionValue(value);
      setDraft((prev) =>
        normalizeSelection({ ...prev, groupId }, masters.customers)
      );
    },
    [masters.customers]
  );

  const selectCustomer = useCallback((value: string) => {
    // 屋号は `customerOptions`（= 傘下だけ）から選ばれるので、
    // ここでホールディングス側を触る必要はない。
    setDraft((prev) => ({ ...prev, customerId: parseOptionValue(value) }));
  }, []);

  const reset = useCallback(() => setDraft(EMPTY_SELECTION), []);

  const reload = useCallback(() => setReloadToken((n) => n + 1), []);

  return {
    status,
    available,
    hasGroups,
    draft,
    groupOptions,
    customerOptions,
    selectGroup,
    selectCustomer,
    reset,
    reload,
  };
}
