// Compile Our Sass
import { dest, series, src } from 'gulp';
import * as dartSass from 'sass';
import gulpSass from 'gulp-sass';
const sass = gulpSass(dartSass);
import rename from 'gulp-rename';
import replace from 'gulp-replace';
import flatten from 'gulp-flatten';
import sourcemaps from 'gulp-sourcemaps';
import autoprefixer from 'gulp-autoprefixer';
import sassGlob from 'gulp-sass-glob';
import bulkSass from 'gulp-sass-glob-use-forward';
import cleanCSS from 'gulp-clean-css';
import notify from 'gulp-notify';
import fs from 'fs';
import crypto from 'crypto';

import { config } from '../config.mjs';
import { errorNotification } from './setup.mjs';

/**
 * Functions for compiling css per part.
 */
function buildCSSProd(fileSrc, fileDest = config.css.dest) {
  return src(fileSrc)
    // Use globbing so we don't need to manually add the imports.
    .pipe(bulkSass())
    .pipe(sassGlob())
    .pipe(sass({ outputStyle: 'expanded' }))
    .pipe(autoprefixer())
    // Cache busting the font files.
    .pipe(replace(/\[\[:FONT:([^:]+):FONT:\]\]/g, (match, p1) => {
      try {
        const buffer = fs.readFileSync(`${fileDest}/${p1}`);
        const hash = crypto.createHash('md5').update(buffer).digest('hex');
        return `${p1}?${hash.substring(0, 6)}`;
      } catch (error) {
        console.error(`Error reading font file: ${dest}/${p1}`, error);
        return p1; // Return original if file not found.
      }
    }))
    // Replace relative paths for files.
    .pipe(flatten())
    .pipe(dest(fileDest));
}

function buildCSSDev(fileSrc, fileDest = config.css.dest) {
  return src(fileSrc)
    // Use globbing so we don't need to manually add the imports.
    .pipe(bulkSass().on('error', (err) => {
      errorNotification(this, err);
    }))
    .pipe(sassGlob().on('error', (err) => {
      errorNotification(this, err);
    }))
    // Add sourcemaps.
    .pipe(sourcemaps.init())
    // An identity sourcemap will be generated at this step.
    .pipe(sourcemaps.identityMap())
    .pipe(sass({
      outputStyle: 'expanded' // Don't minify here because errors with maps.
    }).on('error', (err) => {
      errorNotification(this, err, false);
    }))
    .pipe(autoprefixer().on('error', (err) => {
      errorNotification(this, err, false);
    }))
    .pipe(replace(/\[\[:FONT:([^:]+):FONT:\]\]/g, (match, p1) => {
      try {
        const buffer = fs.readFileSync(`${fileDest}/${p1}`);
        const hash = crypto.createHash('md5').update(buffer).digest('hex');
        return `${p1}?${hash.substring(0, 6)}`;
      } catch (error) {
        console.error(`Error reading font file: ${dest}/${p1}`, error);
        return p1; // Return original if file not found.
      }
    }))
    .pipe(
      sourcemaps.write('./' + dest, {
        sourceMappingURL: (file) => {
          return file.relative + '.map';
        }
      }).on('error', (err) => {
        errorNotification(this, err.false);
      })
    )

    // Replace relative paths for files.
    .pipe(flatten())
    // Cache busting the font files.
    .pipe(dest(fileDest));
}

function buildCssComponents(fileSrc) {
  return src( fileSrc, { base: './'} )
    // Use globbing so we don't need to manually add the imports.
    .pipe(bulkSass().on('error', (err) => {
      errorNotification(this, err);
    }))
    .pipe(sassGlob().on('error', (err) => {
      errorNotification(this, err);
    }))
    // Add sourcemaps.
    .pipe(sourcemaps.init())
    // An identity sourcemap will be generated at this step.
    .pipe(sourcemaps.identityMap())
    .pipe(sass({
      outputStyle: 'expanded' // Don't minify here because errors with maps.
    }).on('error',  (err) => {
      errorNotification(this, err);
    }))
    .pipe(autoprefixer().on('error', (err) => {
      errorNotification(this, err);
    }))
    // Cache busting the font files.
    .pipe(dest('./'));
}

/**
 * Separate tasks so we can run them async.
 */
export const cssThemeDev = async () => buildCSSDev(config.css.components.theme.src);
export const cssContentblocksDev = series(async () => buildCSSDev(config.css.components.contentblocks.src));
export const cssFeaturesDev = series(async () => buildCSSDev(config.css.components.features.src));
export const cssSDCDev = series(async () => buildCssComponents(config.css.components.sdc.src));

export const cssMessageDev = (done) => {
  notify(
    `
    ===========================================================================
    Don't commit development CSS. Use 'gulp cssProd' instead.
    Sourcemap files won't work on environments and lead to errors & overhead.
    ===========================================================================`
  );
  done();
};

// -- For production.
export const cssThemeProd = async () => buildCSSProd(config.css.components.theme.src);
export const cssContentblocksProd = async () => buildCSSProd(config.css.components.contentblocks.src);
export const cssFeaturesProd = async () => buildCSSProd(config.css.components.features.src);
export const cssSDCProd = async () => buildCssComponents(config.css.components.sdc.src);

/**
 * Copy & rename the css-files needed for mail and editor
 * the 'watch' task should also listen to changes in that src,
 * in order to properly update.
 */
const cssMail = () => {
  return src(config.css.mail.src)
    .pipe(rename('mail.css'))
    .pipe(cleanCSS({ compatibility: '*' }))
    .pipe(dest(config.paths.base));
};

const cssEditor = () => {
  // @todo: check if this is still needed, this just makes a copy.
  return src(config.css.editor.src)
    .pipe(rename('editor.css'))
    .pipe(cleanCSS({ compatibility: '*' }))
    .pipe(dest(config.css.dest));
};

export const cssMailTask = async () => cssMail();
export const cssEditorTask = async () => cssEditor();

/**
 * Minify fonts css because it will be injected in header.
 */

const cssFonts = () => {
  return src(config.css.dest + '/style.fonts.css')
    .pipe(cleanCSS({ compatibility: '*' }))
    .pipe(dest(config.css.dest));
};

export const cssFontsTask = async () => cssFonts();

/**
 * Bundle the css tasks into 1 big task.
 */
export const cssDev = series(
  cssMessageDev,
  cssThemeDev,
  cssContentblocksDev,
  cssFeaturesDev,
  cssFontsTask,
  cssMailTask,
  cssEditorTask,
  cssSDCDev,
);
export const cssProd = series(
  cssThemeProd,
  cssContentblocksProd,
  cssFeaturesProd,
  cssFontsTask,
  cssMailTask,
  cssEditorTask,
  cssSDCProd,
);

export {
  cssMail,
  cssEditor
};
