# frozen_string_literal: true

require 'fileutils'

module Tasks
  module Gitlab
    module Assets
      FOSS_ASSET_FOLDERS = %w[app/assets fixtures/emojis vendor/assets/javascripts].freeze
      EE_ASSET_FOLDERS = %w[ee/app/assets].freeze
      JH_ASSET_FOLDERS = %w[jh/app/assets].freeze
      JS_ASSET_PATTERNS = %w[*.js config/**/*.js].freeze
      JS_ASSET_FILES = %w[package.json yarn.lock].freeze
      MASTER_SHA256_HASH_FILE = 'master-assets-hash.txt'
      HEAD_SHA256_HASH_FILE = 'assets-hash.txt'
      PUBLIC_ASSETS_WEBPACK_DIR = 'public/assets/webpack'

      def self.sha256_of_assets_impacting_webpack_compilation
        start_time = Time.now
        asset_files = assets_impacting_webpack_compilation
        puts "Generating the SHA256 hash for #{assets_impacting_webpack_compilation.size} Webpack-related assets..."

        asset_file_sha256s = asset_files.map do |asset_file|
          Digest::SHA256.file(asset_file).hexdigest
        end

        Digest::SHA256.hexdigest(asset_file_sha256s.join).tap { |sha256| puts "=> SHA256 generated in #{Time.now - start_time}: #{sha256}" }
      end

      def self.assets_impacting_webpack_compilation
        assets_folders = FOSS_ASSET_FOLDERS
        assets_folders += EE_ASSET_FOLDERS if ::Gitlab.ee?
        assets_folders += JH_ASSET_FOLDERS if ::Gitlab.jh?

        asset_files = Dir.glob(JS_ASSET_PATTERNS)
        asset_files += JS_ASSET_FILES

        assets_folders.each do |folder|
          asset_files.concat(Dir.glob(["#{folder}/**/*.*"]))
        end

        asset_files
      end
      private_class_method :assets_impacting_webpack_compilation
    end
  end
end

namespace :gitlab do
  namespace :assets do
    desc 'GitLab | Assets | Compile all frontend assets'
    task :compile do
      require_dependency 'gitlab/task_helpers'

      %w[
        yarn:check
        gettext:po_to_json
        rake:assets:precompile
        gitlab:assets:compile_webpack_if_needed
        gitlab:assets:fix_urls
        gitlab:assets:check_page_bundle_mixins_css_for_sideeffects
      ].each(&::Gitlab::TaskHelpers.method(:invoke_and_time_task))
    end

    desc 'GitLab | Assets | Compile all Webpack assets'
    task :compile_webpack_if_needed do
      FileUtils.mv(Tasks::Gitlab::Assets::HEAD_SHA256_HASH_FILE, Tasks::Gitlab::Assets::MASTER_SHA256_HASH_FILE, force: true)

      master_assets_sha256 =
        if File.exist?(Tasks::Gitlab::Assets::MASTER_SHA256_HASH_FILE)
          File.read(Tasks::Gitlab::Assets::MASTER_SHA256_HASH_FILE)
        else
          'missing!'
        end

      head_assets_sha256 = Tasks::Gitlab::Assets.sha256_of_assets_impacting_webpack_compilation.tap do |sha256|
        File.write(Tasks::Gitlab::Assets::HEAD_SHA256_HASH_FILE, sha256)
      end

      puts "Webpack assets SHA256 for `master`: #{master_assets_sha256}"
      puts "Webpack assets SHA256 for `HEAD`: #{head_assets_sha256}"

      public_assets_webpack_dir_exists = Dir.exist?(Tasks::Gitlab::Assets::PUBLIC_ASSETS_WEBPACK_DIR)

      if head_assets_sha256 != master_assets_sha256 || !public_assets_webpack_dir_exists
        FileUtils.rm_r(Tasks::Gitlab::Assets::PUBLIC_ASSETS_WEBPACK_DIR) if public_assets_webpack_dir_exists

        unless system('yarn webpack')
          abort 'Error: Unable to compile webpack production bundle.'.color(:red)
        end
      end
    end

    desc 'GitLab | Assets | Clean up old compiled frontend assets'
    task clean: ['rake:assets:clean']

    desc 'GitLab | Assets | Remove all compiled frontend assets'
    task purge: ['rake:assets:clobber']

    desc 'GitLab | Assets | Uninstall frontend dependencies'
    task purge_modules: ['yarn:clobber']

    desc 'GitLab | Assets | Fix all absolute url references in CSS'
    task :fix_urls do
      css_files = Dir['public/assets/*.css']
      css_files.each do |file|
        # replace url(/assets/*) with url(./*)
        puts "Fixing #{file}"
        system "sed", "-i", "-e", 's/url(\([\"\']\?\)\/assets\//url(\1.\//g', file

        # rewrite the corresponding gzip file (if it exists)
        gzip = "#{file}.gz"
        if File.exist?(gzip)
          puts "Fixing #{gzip}"

          FileUtils.rm(gzip)
          mtime = File.stat(file).mtime

          File.open(gzip, 'wb+') do |f|
            gz = Zlib::GzipWriter.new(f, Zlib::BEST_COMPRESSION)
            gz.mtime = mtime
            gz.write IO.binread(file)
            gz.close

            File.utime(mtime, mtime, f.path)
          end
        end
      end
    end

    desc 'GitLab | Assets | Compile vendor assets'
    task :vendor do
      unless system('yarn webpack-vendor')
        abort 'Error: Unable to compile webpack DLL.'.color(:red)
      end
    end

    desc 'GitLab | Assets | Check that scss mixins do not introduce any sideffects'
    task :check_page_bundle_mixins_css_for_sideeffects do
      system('./scripts/frontend/check_page_bundle_mixins_css_for_sideeffects.js')
    end
  end
end