import fs from 'node:fs/promises';
import path from 'node:path';

import type { MediaAsset, Project } from '../../shared/types';
import type { AssetId } from '../../shared/types/ids';

/** Все asset id, на которые есть ссылки из сцен (превью, видео, аудио). */
export function collectReferencedAssetIds(p: Project): Set<AssetId> {
  const refs = new Set<AssetId>();
  for (const sc of Object.values(p.scenes)) {
    if (sc.previewAssetId) refs.add(sc.previewAssetId);
    if (sc.previewThumbAssetId) refs.add(sc.previewThumbAssetId);
    for (const vid of sc.media.videos) refs.add(vid);
    for (const au of sc.media.audios) refs.add(au.assetId);
  }
  for (const au of p.campaignAudios) refs.add(au.assetId);
  for (const m of p.materials ?? []) refs.add(m.assetId);
  for (const n of p.npcs ?? []) refs.add(n.avatarAssetId);
  return refs;
}

/**
 * Удаляет с диска файлы снятых материалов и записи в `assets`, на которые больше нет ссылок.
 */
export async function reconcileAssetFiles(prev: Project, next: Project, cacheDir: string): Promise<Project> {
  const prevIds = new Set(Object.keys(prev.assets) as AssetId[]);
  const nextIds = new Set(Object.keys(next.assets) as AssetId[]);

  for (const id of prevIds) {
    if (nextIds.has(id)) continue;
    const a = prev.assets[id];
    if (a) {
      const abs = path.join(cacheDir, a.relPath);
      await fs.unlink(abs).catch(() => undefined);
    }
  }

  const refs = collectReferencedAssetIds(next);
  const assets = next.assets;
  const kept: Record<AssetId, MediaAsset> = {} as Record<AssetId, MediaAsset>;
  let droppedOrphans = false;
  for (const id of Object.keys(assets) as AssetId[]) {
    const a = assets[id];
    if (!a) continue;
    if (refs.has(id)) {
      kept[id] = a;
    } else {
      droppedOrphans = true;
      const abs = path.join(cacheDir, a.relPath);
      await fs.unlink(abs).catch(() => undefined);
    }
  }

  return droppedOrphans ? { ...next, assets: kept } : next;
}
