/**
 * D-Sec ログ（/ds-logs）の備考の「編集モード」の下書き（純粋関数のみ。ユーザー決定 2026-10-19 U3。指示書BC 段階4。D-183）。
 *
 * ★規則は `lib/prechecks/edit-drafts.ts`（3画面の対応状況・備考）と同じ★ — ただし行に対応状況が無いので別に持つ:
 *   - ★下書きは「行の値と違う備考」だけを持つ★。行の値に戻したらエントリごと消す（変えた件数 ＝ Map の件数）
 *   - ★行の null と "" を同じとみなす★（比較は素の文字列。トリムと「空 → 削除」はサーバー側の規則。`app/api/ds-devices/[serial_number]/note/route.ts`）
 *   - ★`setNoteDraft` は入力の Map を書き換えない★（React の state にそのまま入れるため）
 *   - ★保存は rows の並び順に1行ずつ★（並列にしない）。★1行の失敗で止めない★
 *
 * ★期待値表は `lib/ds-devices/__tests__/note-drafts.test.ts`★（N1〜N6c / S1〜S4）。
 */

/** 下書きの対象の行（`DsDevice` が満たす） */
export interface NoteRow {
  serial_number: string;
  note: string | null;
}

/** serial_number → 入力中の備考 */
export type NoteDrafts = ReadonlyMap<string, string>;

/** 画面に出す備考（下書きがあればそれ、無ければ行の値。★行の null は ""★） */
export function noteDraftValue(row: NoteRow, drafts: NoteDrafts): string {
  return drafts.get(row.serial_number) ?? row.note ?? "";
}

/** 備考の下書きを入れる。★新しい Map を返す★。★行の値（null は ""）と同じならエントリを消す★ */
export function setNoteDraft(drafts: NoteDrafts, row: NoteRow, note: string): Map<string, string> {
  const next = new Map(drafts);
  if (note === (row.note ?? "")) next.delete(row.serial_number);
  else next.set(row.serial_number, note);
  return next;
}

export interface SaveNoteDraftsResult {
  /** 成功した行（serial_number → ★サーバーが返した備考★。空にしたら null） */
  updated: Map<string, string | null>;
  /** 失敗した行（serial_number → 投げられた例外そのまま） */
  failed: Map<string, unknown>;
  /** ★失敗した行の下書きだけ★ */
  remaining: Map<string, string>;
}

/** 下書きをまとめて保存する。★rows の並び順に、下書きが行の値と違う行だけを1行ずつ★ `send` する。★1行の失敗で止めない★ */
export async function saveNoteDrafts<T extends NoteRow>(
  rows: readonly T[],
  drafts: NoteDrafts,
  send: (row: T, note: string) => Promise<string | null>
): Promise<SaveNoteDraftsResult> {
  const updated = new Map<string, string | null>();
  const failed = new Map<string, unknown>();
  const remaining = new Map<string, string>();
  for (const row of rows) {
    const note = drafts.get(row.serial_number);
    if (note === undefined || note === (row.note ?? "")) continue;
    try {
      updated.set(row.serial_number, await send(row, note));
    } catch (error) {
      failed.set(row.serial_number, error);
      remaining.set(row.serial_number, note);
    }
  }
  return { updated, failed, remaining };
}
