/**
 * fileMove — ファイル/フォルダ移動（Box 化 Phase 2）の移動先解決ロジック。
 *
 * ドラッグ移動・複数選択移動の両方で使う純関数。サーバ（space-api の move）が
 * 構造ディレクトリ保護・自己内移動・衝突自動リネームを最終ゲートするが、UI 側でも
 * 「同じ場所への移動（no-op）」「フォルダを自分自身/配下へ移動」を事前に弾いて
 * 無駄な API 呼び出しとちらつきを避ける。
 *
 * パスはすべて spaceFilesDir からの相対パス完全形（先頭/末尾スラッシュなし）。
 * destDir === '' はルート直下を表す。
 */

export interface MovePlan {
  from: string;
  to: string;
}

export interface MoveResolution {
  /** 実際に move を発行すべき項目。 */
  moves: MovePlan[];
  /** 同じ場所・自己内などで除外した項目（呼出側は件数表示に使える）。 */
  skipped: string[];
}

/** 相対パスを正規化（空セグメント除去・スラッシュ統一）。 */
function norm(p: string): string {
  return String(p ?? '')
    .split(/[\\/]/)
    .filter(Boolean)
    .join('/');
}

/** パスの最後のセグメント（ファイル/フォルダ名）。 */
export function basename(p: string): string {
  const segs = norm(p).split('/');
  return segs[segs.length - 1] ?? '';
}

/** 親ディレクトリの相対パス（ルート直下は ''）。 */
export function parentDir(p: string): string {
  const segs = norm(p).split('/');
  segs.pop();
  return segs.join('/');
}

/**
 * sourcePaths を destDir へ移動する計画を解く。
 * - 移動元が既に destDir 直下にある → skip（no-op）
 * - destDir が移動元自身、または移動元フォルダの配下 → skip（自己内移動）
 * - それ以外 → to = destDir/basename(from) で move 計画に追加
 *
 * 衝突（同名が destDir に既にある）はサーバが `{stem} (N){ext}` に自動リネームするため
 * ここでは検出しない。
 */
export function resolveMoves(sourcePaths: string[], destDir: string): MoveResolution {
  const dest = norm(destDir);
  const moves: MovePlan[] = [];
  const skipped: string[] = [];
  const seen = new Set<string>();
  for (const raw of sourcePaths) {
    const from = norm(raw);
    if (!from || seen.has(from)) continue;
    seen.add(from);
    // 既に移動先フォルダ直下にある（同じ場所）。
    if (parentDir(from) === dest) {
      skipped.push(from);
      continue;
    }
    // フォルダを自分自身、またはその配下へ移動しようとしている。
    if (dest === from || dest.startsWith(`${from}/`)) {
      skipped.push(from);
      continue;
    }
    const name = basename(from);
    moves.push({ from, to: dest ? `${dest}/${name}` : name });
  }
  return { moves, skipped };
}
