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

function uploadPathError(message, code) {
  return Object.assign(new Error(message), { statusCode: 400, code });
}

function isInside(basePath, targetPath) {
  const relative = path.relative(basePath, targetPath);
  return relative !== ''
    && relative !== '..'
    && !relative.startsWith(`..${path.sep}`)
    && !path.isAbsolute(relative);
}

export async function resolveUploadPaths({ uploadsDir, filePaths }) {
  let realUploadsDir;
  try {
    realUploadsDir = await fs.realpath(uploadsDir);
  } catch (err) {
    if (err?.code === 'ENOENT') {
      throw uploadPathError('Upload directory is not available', 'uploads_dir_not_found');
    }
    throw err;
  }

  return Promise.all(filePaths.map(async (filePath) => {
    if (typeof filePath !== 'string' || !filePath) {
      throw uploadPathError('path entries must be non-empty strings', 'invalid_upload_path');
    }
    if (!path.isAbsolute(filePath)) {
      throw uploadPathError('path entries must be absolute paths within the upload directory', 'invalid_upload_path');
    }

    let realFilePath;
    try {
      realFilePath = await fs.realpath(filePath);
    } catch (err) {
      if (err?.code === 'ENOENT') {
        throw uploadPathError(`file not found in upload directory: ${filePath}`, 'file_not_found');
      }
      throw err;
    }

    if (!isInside(realUploadsDir, realFilePath)) {
      throw uploadPathError('path resolves outside the upload directory', 'upload_path_outside_root');
    }

    const stat = await fs.stat(realFilePath);
    if (!stat.isFile()) {
      throw uploadPathError('path must identify a file', 'invalid_upload_path');
    }
    return realFilePath;
  }));
}