import {
  series, parallel, src, dest,
} from 'gulp';
import { exec } from 'child_process';
import path from 'path';
import through from 'through';
import browserify from 'browserify';
import sass from 'sass';
import vinylBuffer from 'vinyl-buffer';
import vinylSourceStream from 'vinyl-source-stream';
import synonyms from 'synonyms';
import svgoUniqueId from 'svgo-unique-id';

import $autoprefixer from 'gulp-autoprefixer';
import $concat from 'gulp-concat';
import $cssnano from 'gulp-cssnano';
import $filesToJson from 'gulp-files-to-json';
import $replace from 'gulp-replace';
import $sass from 'gulp-sass';
import $stylelint from 'gulp-stylelint';
import $svgo from 'gulp-svgo';
import $uglify from 'gulp-uglify';
import $util from 'gulp-util';

$sass.compiler = sass;

const packageName = 'saagie-ui';
const docsFolder = '../../docs-versions/static';
const sources = {
  root: 'src',
  scss: 'src/scss',
  js: 'src/js',
  react: 'src/react',
  assets: 'src/assets',
};

const execCommand = (command, cb) => {
  const child = exec(command);
  child.stdout.pipe(process.stdout);
  child.stderr.pipe(process.stderr);
  child.on('close', () => cb());
};

// -------------------------
//    Scss
// -------------------------
export const buildScss = () => src(`${sources.scss}/index.scss`)
  .pipe($sass().on('error', $sass.logError))
  .pipe($cssnano({ discardComments: { removeAllButFirst: true }, safe: true }))
  .pipe($autoprefixer())
  .pipe($replace(`~${packageName}/${sources.root}/`, '../'))
  .pipe($concat(`${packageName}.css`))
  .pipe(dest('css'))
  .pipe(dest(`${docsFolder}/css`)); // Export for list of versions in documentation

export const lintScss = () => src(`${sources.scss}/**/*.scss`)
  .pipe($stylelint({
    reporters: [{ formatter: 'string', console: true }],
  }));

// -------------------------
//    Js
// -------------------------
export const buildJs = () => browserify(`${sources.js}/index.js`, {
  debug: true,
  extensions: ['es6'],
  standalone: packageName,
})
  .transform('babelify', { presets: ['@babel/preset-env'] })
  .bundle()
  .pipe(vinylSourceStream(`${packageName}.js`))
  .pipe(vinylBuffer())
  .pipe($uglify())
  .pipe(dest('js'));

export const lintJs = (cb) => execCommand(`yarn eslint ${sources.js}`, cb);

// -------------------------
//    Icons mapping
// -------------------------
const svgoPlugins = [
  { removeViewBox: false },
  { uniqueID: svgoUniqueId },
];

export const mapIconsJson = () => src(`${sources.assets}/icons/**/*.svg`)
  .pipe($svgo({ plugins: svgoPlugins }))
  .pipe($filesToJson('icons.map.json'))
  .pipe(dest(`${sources.assets}/icons`));

export const mapIconsJs = () => src(`${sources.assets}/icons/**/*.svg`)
  .pipe($svgo({ plugins: svgoPlugins }))
  .pipe(mapIconsInfos('icons.map.js')) // eslint-disable-line
  .pipe(dest(`${sources.assets}/icons`));

export const mapIcons = parallel(mapIconsJson, mapIconsJs);

// -------------------------
//    React
// -------------------------
export const buildReact = (cb) => execCommand('yarn rollup -c', cb);

export const lintReact = (cb) => execCommand(`yarn eslint ${sources.react}`, cb);

// -------------------------
//    Assets
// -------------------------
export const buildAssets = () => src(`${sources.assets}/**/*.*`)
  .pipe($svgo({ plugins: svgoPlugins })) // Optimize SVG files
  .pipe(dest('assets'))
  .pipe(dest(`${docsFolder}/assets`)); // Export for list of versions in documentation

// -------------------------
//    Publish to NPM
// -------------------------
export const deployToNpm = (cb) => execCommand('yarn publish', cb);

// -------------------------
//    Global Tasks
// -------------------------
export const lint = parallel(lintScss, lintJs, lintReact);
export const build = series(mapIcons, parallel(lint, buildScss, buildJs, buildReact, buildAssets));
export const deploy = series(build, deployToNpm);
export default build;


// -------------------------
//    Generate Icons map
//    for the documentation
// -------------------------
function mapIconsInfos(filename) {
  const cwd = process.cwd();
  const contents = [];

  function writeFile(file) {
    const splitRelativePath = file.relative.split(path.sep);
    const hasCategory = splitRelativePath.length > 1;
    const categoryName = hasCategory ? splitRelativePath[splitRelativePath.length - 2] : null;

    contents.push(`
      {
        name: '${file.stem}',
        keywords: '${synonyms(file.stem, 'v') ? synonyms(file.stem, 'v') : file.stem}',
        ${categoryName ? `category: '${categoryName}',` : ''}
        svg: '${file.contents.toString()}',
      },
    `);
  }

  function endStream() {
    contents.unshift('export default [');
    contents.push('];');

    const file = new $util.File({
      cwd,
      base: cwd,
      path: path.join(cwd, filename),
      contents: Buffer.from(contents.join('\n')),
    });

    this.emit('data', file);
    this.emit('end');
  }

  return through(writeFile, endStream);
}
