/**
 * エラー一覧で保存できなかった行の下に出す文言を、失敗の種類から決める（純粋関数のみ。指示書BF §1）。
 *
 * ★期待値表は `lib/errors/__tests__/save-error-message.test.ts`★（指示書BF §1 の表の行と同じ並び）。
 * ★エラー一覧の画面（`ErrorsScreen.tsx`）だけ★で使う。アラート・D-Sec アラートの画面は変えない。
 *
 * ★戻り値は辞書のキー（日本語原文）★ — 表示のときに `renderSaveErrorMessage()`（`t()` を通す）で出す。
 *   保存前の検証で止めた行（その他・備考なし）は `saveErrorDraftsWithCheck()` が `NOTE_REQUIRED_ROW_MESSAGE` を入れる（今のまま）。
 *
 * 判定に使うもの（`ApiFetchError` の `status` / `details` / `serverMessage`。`lib/api/client.ts`）:
 *   - 422 の区別は ★`details` のキー★で行う（`error_list_id` / `note`）。サーバーの `code` はどれも `validation_failed` で区別できない
 *   - ★選べない行と、選択肢に無い・無効との区別は `details.error_list_id` の中の文言★
 *     （「この行ではエラー内容を選べません」。`lib/errors/status-update.ts` の手順2）。それ以外の `error_list_id` は手順3（無効）とみなす
 *   - ★備考の長さと、その他・備考なしの区別は `details.note` の中の文言★ —
 *     長さは「…文字以内で…」（`lib/prechecks/status-update.ts` の `parsePrecheckStatusUpdateBody()`）、それ以外は備考なし（手順4）
 *   - ★`details` は実行時に絞る★（`ApiErrorDetails` は union。文字列の配列でなければ「無い」とみなす）
 */
import { ApiFetchError } from "@/lib/api/client";
import { PRECHECK_NOTE_MAX_LENGTH } from "@/lib/config";
import type { ApiErrorDetails } from "@/lib/types";
import { NOTE_REQUIRED_ROW_MESSAGE } from "./edit-drafts";

export const SAVE_ERROR_LIST_NOT_SELECTABLE = "この行ではエラー内容を選べません";
export const SAVE_ERROR_LIST_UNAVAILABLE = "選んだエラー内容は今は使えません。選び直してください";
/** `{n}` は `PRECHECK_NOTE_MAX_LENGTH`（表示のときに `renderSaveErrorMessage()` が埋める） */
export const SAVE_ERROR_NOTE_TOO_LONG = "備考は{n}文字以内で入力してください";
export const SAVE_ERROR_VALIDATION_FALLBACK = "入力内容を確かめてください";
export const SAVE_ERROR_NOT_FOUND = "このエラーは削除されたため保存できません。画面を読み込み直してください";
export const SAVE_ERROR_UNAUTHORIZED = "ログインし直してください";
export const SAVE_ERROR_SERVER =
  "サーバーで保存に失敗しました（DB への書き込みエラー）。時間をおいて保存し直し、続くときは管理者に連絡してください";
export const SAVE_ERROR_NETWORK = "通信に失敗しました。接続を確かめて保存し直してください";
export const SAVE_ERROR_DEFAULT = "保存に失敗しました";

/** サーバーが「選べない行」で返す `details.error_list_id` の文言（`lib/errors/status-update.ts` の手順2） */
const NOT_SELECTABLE_DETAIL = "この行ではエラー内容を選べません";
/** サーバーが備考の長さで返す `details.note` の文言の目印（「400文字以内で入力してください」） */
const NOTE_LENGTH_DETAIL_MARKER = "文字以内";

/** `details[key]` の文字列（1つ以上）。キーが無い・配列でない・文字列が1つも無い（数値の配列など）ときは null */
function stringDetails(details: ApiErrorDetails, key: string): string[] | null {
  if (details === null || typeof details !== "object") return null;
  const value = (details as Record<string, unknown>)[key];
  if (!Array.isArray(value)) return null;
  const strings = value.filter((v): v is string => typeof v === "string");
  return strings.length > 0 ? strings : null;
}

/** 保存の失敗（`saveErrorDraftsWithCheck()` の `failed` の値）→ 行の下に出す文言（辞書のキー） */
export function saveErrorMessage(e: unknown): string {
  if (!(e instanceof ApiFetchError)) return SAVE_ERROR_DEFAULT;
  const { status } = e;

  if (status === 422) {
    const errorList = stringDetails(e.details, "error_list_id");
    if (errorList !== null) {
      return errorList.includes(NOT_SELECTABLE_DETAIL)
        ? SAVE_ERROR_LIST_NOT_SELECTABLE
        : SAVE_ERROR_LIST_UNAVAILABLE;
    }
    const note = stringDetails(e.details, "note");
    if (note !== null) {
      return note.some((m) => m.includes(NOTE_LENGTH_DETAIL_MARKER))
        ? SAVE_ERROR_NOTE_TOO_LONG
        : NOTE_REQUIRED_ROW_MESSAGE;
    }
    return e.serverMessage ?? SAVE_ERROR_VALIDATION_FALLBACK;
  }
  if (status === 404) return SAVE_ERROR_NOT_FOUND;
  if (status === 401) return SAVE_ERROR_UNAUTHORIZED;
  if (status >= 500) return SAVE_ERROR_SERVER;
  if (status === 0) return SAVE_ERROR_NETWORK;
  return SAVE_ERROR_DEFAULT;
}

/** 行の文言を表示する（`t()` を通し、備考の長さの `{n}` を埋める）。★保存前の検証の文言もこれで出す★ */
export function renderSaveErrorMessage(message: string, t: (text: string) => string): string {
  const text = t(message);
  return message === SAVE_ERROR_NOTE_TOO_LONG
    ? text.replace("{n}", String(PRECHECK_NOTE_MAX_LENGTH))
    : text;
}
