# frozen_string_literal: true

#
#  A class that handles interaction between a plugin and the Discourse App.
#
class DiscoursePluginRegistry

  class << self
    attr_writer :javascripts
    attr_writer :auth_providers
    attr_writer :service_workers
    attr_writer :admin_javascripts
    attr_writer :stylesheets
    attr_writer :mobile_stylesheets
    attr_writer :desktop_stylesheets
    attr_writer :sass_variables
    attr_writer :handlebars
    attr_writer :serialized_current_user_fields
    attr_writer :seed_data
    attr_writer :svg_icons
    attr_writer :locales
    attr_accessor :custom_html

    def plugins
      @plugins ||= []
    end

    # Default accessor values
    def javascripts
      @javascripts ||= Set.new
    end

    def auth_providers
      @auth_providers ||= Set.new
    end

    def service_workers
      @service_workers ||= Set.new
    end

    def asset_globs
      @asset_globs ||= Set.new
    end

    def admin_javascripts
      @admin_javascripts ||= Set.new
    end

    def stylesheets
      @stylesheets ||= Hash.new
    end

    def mobile_stylesheets
      @mobile_stylesheets ||= Hash.new
    end

    def desktop_stylesheets
      @desktop_stylesheets ||= Hash.new
    end

    def sass_variables
      @sass_variables ||= Set.new
    end

    def svg_icons
      @svg_icons ||= []
    end

    def handlebars
      @handlebars ||= Set.new
    end

    def serialized_current_user_fields
      @serialized_current_user_fields ||= Set.new
    end

    def seed_data
      @seed_data ||= HashWithIndifferentAccess.new({})
    end

    def locales
      @locales ||= HashWithIndifferentAccess.new({})
    end

    def html_builders
      @html_builders ||= {}
    end

    def seed_path_builders
      @seed_path_builders ||= Set.new
    end

    def vendored_pretty_text
      @vendored_pretty_text ||= Set.new
    end

    def vendored_core_pretty_text
      @vendored_core_pretty_text ||= Set.new
    end
  end

  def self.register_auth_provider(auth_provider)
    self.auth_providers << auth_provider
  end

  def register_js(filename, options = {})
    # If we have a server side option, add that too.
    self.class.javascripts << filename
  end

  def self.register_service_worker(filename, options = {})
    self.service_workers << filename
  end

  def self.register_svg_icon(icon)
    self.svg_icons << icon
  end

  def register_css(filename, plugin_directory_name)
    self.class.stylesheets[plugin_directory_name] ||= Set.new
    self.class.stylesheets[plugin_directory_name] << filename
  end

  def self.register_locale(locale, options = {})
    self.locales[locale] = options
  end

  def register_archetype(name, options = {})
    Archetype.register(name, options)
  end

  def self.register_glob(root, extension, options = nil)
    self.asset_globs << [root, extension, options || {}]
  end

  def self.each_globbed_asset(each_options = nil)
    each_options ||= {}

    self.asset_globs.each do |g|
      root, ext, options = *g

      if options[:admin]
        next unless each_options[:admin]
      else
        next if each_options[:admin]
      end

      Dir.glob("#{root}/**/*") do |f|
        yield f, ext
      end
    end
  end

  JS_REGEX = /\.js$|\.js\.erb$|\.js\.es6$/
  HANDLEBARS_REGEX = /\.(hb[rs]|js\.handlebars)$/

  def self.register_asset(asset, opts = nil, plugin_directory_name = nil)
    if asset =~ JS_REGEX
      if opts == :admin
        self.admin_javascripts << asset
      elsif opts == :vendored_pretty_text
        self.vendored_pretty_text << asset
      elsif opts == :vendored_core_pretty_text
        self.vendored_core_pretty_text << asset
      else
        self.javascripts << asset
      end
    elsif asset =~ /\.css$|\.scss$/
      if opts == :mobile
        self.mobile_stylesheets[plugin_directory_name] ||= Set.new
        self.mobile_stylesheets[plugin_directory_name] << asset
      elsif opts == :desktop
        self.desktop_stylesheets[plugin_directory_name] ||= Set.new
        self.desktop_stylesheets[plugin_directory_name] << asset
      elsif opts == :variables
        self.sass_variables << asset
      else
        self.stylesheets[plugin_directory_name] ||= Set.new
        self.stylesheets[plugin_directory_name] << asset
      end
    elsif asset =~ HANDLEBARS_REGEX
      self.handlebars << asset
    end
  end

  def self.stylesheets_exists?(plugin_directory_name, target = nil)
    case target
    when :desktop
      self.desktop_stylesheets[plugin_directory_name].present?
    when :mobile
      self.mobile_stylesheets[plugin_directory_name].present?
    else
      self.stylesheets[plugin_directory_name].present?
    end
  end

  def self.register_seed_data(key, value)
    self.seed_data[key] = value
  end

  def self.register_seed_path_builder(&block)
    seed_path_builders << block
  end

  def self.register_html_builder(name, &block)
    html_builders[name] ||= []
    html_builders[name] << block
  end

  def self.build_html(name, ctx = nil)
    builders = html_builders[name] || []
    builders.map { |b| b.call(ctx) }.join("\n").html_safe
  end

  def self.seed_paths
    result = SeedFu.fixture_paths.dup
    unless Rails.env.test? && ENV['LOAD_PLUGINS'] != "1"
      seed_path_builders.each { |b| result += b.call }
    end
    result.uniq
  end

  VENDORED_CORE_PRETTY_TEXT_MAP = {
    "moment.js" => "vendor/assets/javascripts/moment.js",
    "moment-timezone.js" => "vendor/assets/javascripts/moment-timezone-with-data.js"
  }
  def self.core_asset_for_name(name)
    asset = VENDORED_CORE_PRETTY_TEXT_MAP[name]
    raise KeyError, "Asset #{name} not found in #{VENDORED_CORE_PRETTY_TEXT_MAP}" unless asset
    asset
  end

  def locales
    self.class.locales
  end

  def javascripts
    self.class.javascripts
  end

  def auth_providers
    self.class.auth_providers
  end

  def service_workers
    self.class.service_workers
  end

  def stylesheets
    self.class.stylesheets
  end

  def mobile_stylesheets
    self.class.mobile_stylesheets
  end

  def desktop_stylesheets
    self.class.desktop_stylesheets
  end

  def sass_variables
    self.class.sass_variables
  end

  def handlebars
    self.class.handlebars
  end

  def self.clear
    self.javascripts = nil
    self.auth_providers = nil
    self.service_workers = nil
    self.stylesheets = nil
    self.mobile_stylesheets = nil
    self.desktop_stylesheets = nil
    self.sass_variables = nil
    self.handlebars = nil
    self.locales = nil
  end

  def self.reset!
    javascripts.clear
    auth_providers.clear
    service_workers.clear
    admin_javascripts.clear
    stylesheets.clear
    mobile_stylesheets.clear
    desktop_stylesheets.clear
    sass_variables.clear
    serialized_current_user_fields
    asset_globs.clear
    html_builders.clear
    vendored_pretty_text.clear
    vendored_core_pretty_text.clear
    seed_path_builders.clear
    locales.clear
  end

  def self.setup(plugin_class)
    registry = DiscoursePluginRegistry.new
    plugin = plugin_class.new(registry)
    plugin.setup
  end

end