import path from 'path';

import { buildImportPath } from '../builder';
import { BaseUrl } from '../options/baseUrl';
import { Logger } from '../options/logger';
import { SemicolonCharacter } from '../options/noSemicolon';
import { QuoteCharacter } from '../options/quoteCharacter';
import { indentation, nonAlphaNumeric } from '../utilities';
import { Directory } from '../interfaces/directory.interface';
import { FileTreeLocation } from '../interfaces/location.interface';

function stringify(structure: ExportStructure, previousIndentation: string): string {
  const nextIndentation = previousIndentation + indentation;
  let content = '';
  for (const key of Object.keys(structure).sort()) {
    content += `
${nextIndentation}${key}: `;
    const exported = structure[key];
    if (typeof exported === 'string') {
      content += exported;
    } else {
      content += stringify(exported, nextIndentation);
    }
    content += ',';
  }
  return `{${content}
${previousIndentation}}`;
}

interface ExportStructure {
  [directoryName: string]: ExportStructure | string;
}

function buildStructureSubsection(structure: ExportStructure, pathParts: string[], name: string, reference: string) {
  const pathPart = pathParts.shift() as string;
  let subsection: ExportStructure = pathPart === '.' ? structure : (structure[pathPart] as ExportStructure);
  if (!subsection) {
    subsection = {};
    structure[pathPart] = subsection;
  }
  if (pathParts.length === 0) {
    subsection[name] = reference;
  } else {
    buildStructureSubsection(subsection, pathParts, name, reference);
  }
}

interface Import {
  module: FileTreeLocation;
  path: string;
}

// Comparator for alphabetically sorting imports by path.
// Does not need to check for equality, will only be used on distinct paths.
function compareImports(a: Import, b: Import): number {
  return a.path < b.path ? -1 : 1;
}

export function buildFileSystemBarrel(
  directory: Directory,
  modules: FileTreeLocation[],
  quoteCharacter: QuoteCharacter,
  semicolonCharacter: SemicolonCharacter,
  _: Logger, // Not used
  baseUrl: BaseUrl
): string {
  const structure: ExportStructure = {};
  let content = '';
  modules
    .map(
      (module: FileTreeLocation): Import => ({
        module,
        path: buildImportPath(directory, module, baseUrl),
      })
    )
    .sort(compareImports)
    .forEach((imported: Import): void => {
      const relativePath = path.relative(directory.path, imported.module.path);
      const directoryPath = path.dirname(relativePath);
      const parts = directoryPath.split(path.sep);
      const alias = relativePath.replace(nonAlphaNumeric, '');
      content += `import * as ${alias} from ${quoteCharacter}${imported.path}${quoteCharacter}${semicolonCharacter}
`;
      const fileName = path.basename(imported.module.name, '.ts');
      buildStructureSubsection(structure, parts, fileName, alias);
    });
  for (const key of Object.keys(structure).sort()) {
    const exported = structure[key];
    if (typeof exported === 'string') {
      content += `export {${exported} as ${key}}${semicolonCharacter}
`;
    } else {
      content += `export const ${key} = ${stringify(exported, '')}${semicolonCharacter}
`;
    }
  }
  return content;
}
