import {
  projectDirectoryPath,
  projectFilePath,
  type ProjectFilePath,
  type ProjectStorageProvider,
  type LearnitiveProjectConfig,
} from '@learnitive/project-storage';

export type ProjectImageFormat = 'png' | 'jpg' | 'jpeg' | 'svg' | 'eps' | 'pdf';

export type ProjectImageAsset = {
  path: ProjectFilePath;
  originalName: string;
  fileName: string;
  format: ProjectImageFormat;
  size: number;
};

export type ImportProjectImageInput = {
  fileName: string;
  data: Uint8Array;
  signal?: AbortSignal;
};

const SUPPORTED_IMAGE_FORMATS = new Set<ProjectImageFormat>([
  'png',
  'jpg',
  'jpeg',
  'svg',
  'eps',
  'pdf',
]);

export function getProjectImageFormat(
  fileName: string,
): ProjectImageFormat | undefined {
  const extension = fileName.split('.').at(-1)?.toLowerCase();
  return extension &&
    SUPPORTED_IMAGE_FORMATS.has(extension as ProjectImageFormat)
    ? (extension as ProjectImageFormat)
    : undefined;
}

export function normalizeProjectImageFileName(fileName: string): string {
  const leafName = fileName.split(/[\\/]/).at(-1)?.trim() ?? '';
  const format = getProjectImageFormat(leafName);
  if (!format) {
    throw new Error(`Unsupported project image format: ${fileName}`);
  }

  const extensionIndex = leafName.lastIndexOf('.');
  const rawBase = leafName.slice(0, extensionIndex);
  const normalizedBase = rawBase
    .normalize('NFKD')
    .replace(/[\u0300-\u036f]/g, '')
    .replace(/[^A-Za-z0-9._-]+/g, '-')
    .replace(/-+/g, '-')
    .replace(/^[._-]+|[._-]+$/g, '')
    .slice(0, 120);

  return `${normalizedBase || 'image'}.${format}`;
}

export async function importProjectImage(
  storage: ProjectStorageProvider,
  config: LearnitiveProjectConfig,
  input: ImportProjectImageInput,
): Promise<ProjectImageAsset> {
  input.signal?.throwIfAborted();
  const format = getProjectImageFormat(input.fileName);
  if (!format) {
    throw new Error(`Unsupported project image format: ${input.fileName}`);
  }

  const directory = projectDirectoryPath(config.assets.imageDirectory);
  await storage.createDirectory(directory, input.signal);

  const normalizedName = normalizeProjectImageFileName(input.fileName);
  const extensionIndex = normalizedName.lastIndexOf('.');
  const base = normalizedName.slice(0, extensionIndex);
  const extension = normalizedName.slice(extensionIndex);

  let candidateName = normalizedName;
  let candidate = projectFilePath(`${directory}${candidateName}`);
  for (
    let suffix = 2;
    await storage.readFile(candidate, input.signal);
    suffix += 1
  ) {
    input.signal?.throwIfAborted();
    candidateName = `${base}-${suffix}${extension}`;
    candidate = projectFilePath(`${directory}${candidateName}`);
  }

  await storage.writeFile(candidate, input.data, input.signal);

  return {
    path: candidate,
    originalName: input.fileName,
    fileName: candidateName,
    format,
    size: input.data.byteLength,
  };
}
