// Favicon generation task
// uses the RealFavicon API: https://realfavicongenerator.net/
import gulp from 'gulp';
import fs from 'fs';
import replace from 'gulp-replace';
import notify from 'gulp-notify';
import { imageSizeFromFile } from 'image-size/fromFile'
import realFavicon from 'gulp-real-favicon';
import path from 'node:path';
import { config } from '../config.mjs';

const masterPictureSVG = config.favicon.master.SVG;
const masterPictureSVGMobile = config.favicon.master.SVGMobile;
const masterPicturePNG = config.favicon.master.PNG;
const masterPicturePNGMobile = config.favicon.master.PNGMobile;
const themeName = path.basename(process.cwd());
const colorPrimary = '#10bfff'; // Example color, should come from config ideally.
const colorWhite = '#ffffff';
// File where the favicon markups are stored.
const FAVICON_DATA_FILE = config.favicon.datafile;

let masterPicture = null;
let masterPictureMobile = null;

const replaceVarsTwig = () => {
  notify().write(`filling in Safari colors based on Sass main colors: ${colorPrimary}`);
  return gulp.src(config.htmlTwig)
    .pipe(replace(/fav_var_color/g, colorPrimary))
    .pipe(gulp.dest(config.html));
};

const moveFavicon = () => {
  notify().write('Move the ico file to the root of the theme');
  return gulp.src(config.favicon.srcico)
    .pipe(gulp.dest('./'));
};

const generate = (callback) => {
  notify().write('generate the icons');
  const themeSpaced = themeName.replace(/_/g, ' ').replace(/-/g, ' ');
  const themeShort = themeSpaced.replace(/theme/g, '').replace(/8/g, '');
  const themeFull = themeShort.replace(/(?:^|\s)\S/g, (a) => a.toUpperCase());

  const generateFaviconCallback = (err) => {
    if (err) {
      return callback(new Error(err)); // Pass error to gulp's done
    }
    replaceVarsTwig();
    return moveFavicon();
  };

  const baseConfig = {
    masterPicture: masterPicture,
    dest: config.favicon.dest,
    iconsPath: `/themes/custom/${themeName}/favicons/generated/`,
    design: {
      ios: {
        pictureAspect: 'backgroundAndMargin',
        backgroundColor: colorWhite,
        margin: '18%',
        assets: {
          ios6AndPriorIcons: false,
          ios7AndLaterIcons: true,
          precomposedIcons: false,
          declareOnlyDefaultIcon: true,
        },
      },
      desktopBrowser: {},
      androidChrome: {
        pictureAspect: 'backgroundAndMargin',
        margin: '18%',
        themeColor: colorPrimary,
        backgroundColor: colorWhite,
        manifest: {
          name: themeFull,
          display: 'browser',
          orientation: 'notSet',
          onConflict: 'override',
          declared: true,
        },
        assets: {
          legacyIcon: false,
          lowResolutionIcons: false,
        },
      },
    },
    settings: {
      scalingAlgorithm: 'Mitchell',
      errorOnImageTooSmall: true, // RealFaviconGenerator can also check size.
      readmeFile: false,
      htmlCodeFile: false,
      usePathAsIs: false,
    },
    markupFile: FAVICON_DATA_FILE,
  };

  if (masterPictureMobile) {
    realFavicon.generateFavicon(
      {
        ...baseConfig,
        design: {
          ...baseConfig.design,
          ios: {
            ...baseConfig.design.ios,
            masterPicture: masterPictureMobile,
          },
          androidChrome: {
            ...baseConfig.design.androidChrome,
            masterPicture: masterPictureMobile,
          },
        },
      },
      generateFaviconCallback
    );
  } else {
    realFavicon.generateFavicon(baseConfig, generateFaviconCallback);
  }

  return;
};

const mobileCheck = (callback) => {
  // Check if a mobile icon exists.
  fs.access(masterPictureSVGMobile, (err) => {
    if (!err) {
      notify().write(`Found a separate favicon for mobile (SVG).`);
      masterPictureMobile = masterPictureSVGMobile;
      return;
    } else {
      // check for PNG version.
      fs.access(masterPicturePNGMobile, (err) => {
        if (!err) {
          console.log(`Found a separate favicon for mobile (PNG).`);
          masterPictureMobile = masterPicturePNGMobile;
          generate(callback);
        } else {
          // generation without mobile icon.
          console.log(`No separate mobile favicon found, using main icon.`);
          generate(callback);
        }
      });
    }
  });
};

// Make the favicon.
export const favicon = (callback) => { // Export as a Gulp 5 task
  // Check for updates on RealFaviconGenerator.
  // Check if FAVICON_DATA_FILE exists before reading.
  if (!fs.existsSync(FAVICON_DATA_FILE)) {
    console.log('Favicon data file not found, starting initial generation.');
    // Proceed directly to checking for master picture if data file doesn't exist
    return checkMasterPicture(callback);
  }

  const currentVersion = JSON.parse(fs.readFileSync(FAVICON_DATA_FILE)).version;
  realFavicon.checkForUpdates(currentVersion, (err) => {
    if (err) {
      // An error checking for updates is not necessarily fatal, log and proceed?
      // Or maybe treat as fatal if R_F_G is required. Let's proceed for now.
      console.log('Error checking for RealFaviconGenerator updates, proceeding...');
    } else {
      console.log('RealFaviconGenerator update check complete.');
    }
    // Always proceed to checking the master picture regardless of update check result.
    return checkMasterPicture(callback);
  });
};


// Helper function to encapsulate the logic for checking master pictures
const checkMasterPicture = (callback) => {
  // Check if an SVG exists.
  fs.access(masterPictureSVG, (err) => {
    if (!err) {
      // The file exists, check its dimensions asynchronously
      imageSizeFromFile(masterPictureSVG)
        .then(dimensions => {
          if ( dimensions?.width === dimensions?.height) {
            masterPicture = masterPictureSVG;
            return mobileCheck(callback);
          } else {
            // The dimensions are incorrect.
            callback(new Error(`The dimensions of the SVG source image are incorrect. Must be square but they are: ${dimensions?.width ?? 'unknown'} and ${dimensions.height ?? 'unknown'})`));
          }
        })
        .catch(sizeErr => {
          callback(new Error(`Error getting dimensions for SVG master picture: ${sizeErr}`));
        });
    } else {
      // SVG not found, fall back to PNG.
      notify().write('SVG master picture not found, checking for PNG...');
      fs.access(masterPicturePNG, (err) => {
        if (!err) {
          // The file exists, check its dimensions asynchronously
          imageSizeFromFile(masterPicturePNG)
            .then(dimensions => {
              if (dimensions?.width === dimensions?.height && dimensions.height > 511) {
                notify().write(`PNG master picture found and is square (>511px).`);
                masterPicture = masterPicturePNG;
                mobileCheck(callback);
              } else {
                // The dimensions are incorrect.
                callback(new Error(`The dimensions of the PNG source image are incorrect. Must be square and bigger than 511px but they are: ${dimensions?.width ?? 'unknown'} and ${dimensions?.height ?? 'unknown'})`));
              }
            })
            .catch(sizeErr => {
              callback(new Error(`Error getting dimensions for PNG master picture: ${sizeErr}`));
            });
        } else {
          // Neither SVG nor PNG found
          callback(new Error(
            `Please add the favicon to source folder + fill in correct extension and filename in gulp/config.js (Tried: ${masterPictureSVG} and ${masterPicturePNG})`
          ));
        }
      });
    }
  });

  callback();
}

