'use strict'

const fs = require('fs')
const klawSync = require('klaw-sync')
const glob = require('glob')
const path = require('path')
const LessCache = require('less-cache')

const CONFIG = require('../config')
const LESS_CACHE_VERSION = require('less-cache/package.json').version
const FALLBACK_VARIABLE_IMPORTS = '@import "variables/ui-variables";\n@import "variables/syntax-variables";\n'

module.exports = function () {
  const cacheDirPath = path.join(CONFIG.intermediateAppPath, 'less-compile-cache')
  console.log(`Generating pre-built less cache in ${cacheDirPath}`)

  // Group bundled packages into UI themes, syntax themes, and non-theme packages
  const uiThemes = []
  const syntaxThemes = []
  const nonThemePackages = []
  for (let packageName in CONFIG.appMetadata.packageDependencies) {
    const packageMetadata = require(path.join(CONFIG.intermediateAppPath, 'node_modules', packageName, 'package.json'))
    if (packageMetadata.theme === 'ui') {
      uiThemes.push(packageName)
    } else if (packageMetadata.theme === 'syntax') {
      syntaxThemes.push(packageName)
    } else {
      nonThemePackages.push(packageName)
    }
  }

  CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath = {}
  function saveIntoSnapshotAuxiliaryData (absoluteFilePath, content) {
    const relativeFilePath = path.relative(CONFIG.intermediateAppPath, absoluteFilePath)
    if (!CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath.hasOwnProperty(relativeFilePath)) {
      CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath[relativeFilePath] = {
        content: content,
        digest: LessCache.digestForContent(content)
      }
    }
  }

  CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath = {}
  // Warm cache for every combination of the default UI and syntax themes,
  // because themes assign variables which may be used in any style sheet.
  for (let uiTheme of uiThemes) {
    for (let syntaxTheme of syntaxThemes) {
      // Build a LessCache instance with import paths based on the current theme combination
      const lessCache = new LessCache({
        cacheDir: cacheDirPath,
        fallbackDir: path.join(CONFIG.atomHomeDirPath, 'compile-cache', 'prebuild-less', LESS_CACHE_VERSION),
        syncCaches: true,
        resourcePath: CONFIG.intermediateAppPath,
        importPaths: [
          path.join(CONFIG.intermediateAppPath, 'node_modules', syntaxTheme, 'styles'),
          path.join(CONFIG.intermediateAppPath, 'node_modules', uiTheme, 'styles'),
          path.join(CONFIG.intermediateAppPath, 'static', 'variables'),
          path.join(CONFIG.intermediateAppPath, 'static')
        ]
      })

      // Store file paths located at the import paths so that we can avoid scanning them at runtime.
      for (const absoluteImportPath of lessCache.getImportPaths()) {
        const relativeImportPath = path.relative(CONFIG.intermediateAppPath, absoluteImportPath)
        if (!CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath.hasOwnProperty(relativeImportPath)) {
          CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath[relativeImportPath] = []
          for (const importedFile of klawSync(absoluteImportPath, {nodir: true})) {
            CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath[relativeImportPath].push(
              path.relative(CONFIG.intermediateAppPath, importedFile.path)
            )
          }
        }
      }

      // Cache all styles in static; don't append variable imports
      for (let lessFilePath of glob.sync(path.join(CONFIG.intermediateAppPath, 'static', '**', '*.less'))) {
        cacheCompiledCSS(lessCache, lessFilePath, false)
      }

      // Cache styles for all bundled non-theme packages
      for (let nonThemePackage of nonThemePackages) {
        for (let lessFilePath of glob.sync(path.join(CONFIG.intermediateAppPath, 'node_modules', nonThemePackage, '**', '*.less'))) {
          cacheCompiledCSS(lessCache, lessFilePath, true)
        }
      }

      // Cache styles for this UI theme
      const uiThemeMainPath = path.join(CONFIG.intermediateAppPath, 'node_modules', uiTheme, 'index.less')
      cacheCompiledCSS(lessCache, uiThemeMainPath, true)
      for (let lessFilePath of glob.sync(path.join(CONFIG.intermediateAppPath, 'node_modules', uiTheme, '**', '*.less'))) {
        if (lessFilePath !== uiThemeMainPath) {
          saveIntoSnapshotAuxiliaryData(lessFilePath, fs.readFileSync(lessFilePath, 'utf8'))
        }
      }

      // Cache styles for this syntax theme
      const syntaxThemeMainPath = path.join(CONFIG.intermediateAppPath, 'node_modules', syntaxTheme, 'index.less')
      cacheCompiledCSS(lessCache, syntaxThemeMainPath, true)
      for (let lessFilePath of glob.sync(path.join(CONFIG.intermediateAppPath, 'node_modules', syntaxTheme, '**', '*.less'))) {
        if (lessFilePath !== syntaxThemeMainPath) {
          saveIntoSnapshotAuxiliaryData(lessFilePath, fs.readFileSync(lessFilePath, 'utf8'))
        }
      }
    }
  }

  for (let lessFilePath of glob.sync(path.join(CONFIG.intermediateAppPath, 'node_modules', 'atom-ui', '**', '*.less'))) {
    saveIntoSnapshotAuxiliaryData(lessFilePath, fs.readFileSync(lessFilePath, 'utf8'))
  }

  function cacheCompiledCSS (lessCache, lessFilePath, importFallbackVariables) {
    let lessSource = fs.readFileSync(lessFilePath, 'utf8')
    if (importFallbackVariables) {
      lessSource = FALLBACK_VARIABLE_IMPORTS + lessSource
    }
    lessCache.cssForFile(lessFilePath, lessSource)
    saveIntoSnapshotAuxiliaryData(lessFilePath, lessSource)
  }
}
