# frozen_string_literal: true

module Handlebarsjs
  # Wraps MiniRacer snapshot with specific emphasis on
  # loading Handlebars helpers onto the MiniRacer context
  # in the correct order. So that new contexts are preloaded
  # with the handlebars library and the configured helpers.
  class HandlebarsSnapshot
    include KLog::Logging

    attr_reader :scripts
    attr_reader :helpers

    def initialize
      @scripts = []
      @helpers = []
    end

    def add_library(name, script: nil, path: nil)
      add_script(name, 'library', script: script, path: path)
    end

    def add_snippet(name, script: nil, path: nil)
      add_script(name, 'snippet', script: script, path: path)
    end

    def add_helper(name, helper)
      add_helper_entry(
        name: name,
        helper: helper,
        callback: helper.to_proc,
        safe: helper.safe,
        parameters: helper.parameter_names
      )
    end

    def add_callback(name, callback, safe, parameters)
      add_helper_entry(
        name: name,
        helper: nil,
        callback: callback,
        safe: safe,
        parameters: parameters
      )
    end

    def register_helper(name)
      add_script(name, 'helper', script: "Handlebars.registerHelper('#{name}', ruby_#{name})")
    end

    def script
      scripts.map { |script| "// #{script[:type]} - #{script[:name]}\n#{script[:script]}" }.join("\n\n")
    end

    def snapshot
      @snapshot ||= MiniRacer::Snapshot.new(script)
    end

    def new_context
      context = MiniRacer::Context.new(snapshot: snapshot)

      helpers.each do |helper_entry|
        attach_ruby(context, helper_entry)
        eval_register_helper(context, helper_entry)
      end

      context
    end

    # not currently used
    def dirty?
      @snapshot.nil?
    end

    def debug
      data = { scripts: scripts, helpers: helpers }
      log.structure(data)
    end

    def debug_register_scripts
      register_scripts = helpers.map do |helper|
        # Handlebarsjs::Handlebars.register_safe_string_helper_script(
        #   helper[:name]
        # )
        # helper[:helper].build_register_helper_script(
        #   helper[:name],
        #   safe: helper[:safe]
        # )
      end
      puts register_scripts.join("\n")
    end

    private

    def add_script(name, type, script: nil, path: nil)
      raise Handlebarsjs::Error, 'script or path is required' if script.nil? && path.nil?
      raise Handlebarsjs::Error, 'script and path are mutually exclusive' if script && path

      script ||= File.read(path)
      add_script_item(name, type, script, path)
    end

    def add_helper_entry(**args)
      entry = {
        name: args[:name],
        helper: args[:helper],
        callback: args[:callback],
        safe: args[:safe],
        parameters: args[:parameters]
      }

      @helpers << entry

      entry
    end

    def add_script_item(name, type, script, path = nil)
      @snapshot = nil
      scripts << {
        name: name,
        type: type,
        script: script,
        path: path
      }
    end

    # This context should be on handlebars, not snapshot (I THINK)
    def attach_ruby(context, helper_entry)
      name = helper_entry[:name]
      callback = helper_entry[:callback]

      context.attach("ruby_#{name}", callback)
    end

    def eval_register_helper(context, helper_entry)
      script = build_register_helper_script(helper_entry)

      context.eval(script)
    end

    def build_register_helper_script(helper_entry)
      return Handlebarsjs::Handlebars.register_safe_string_helper_script(helper_entry[:name], helper_entry[:parameters]) if helper_entry[:safe]

      Handlebarsjs::Handlebars.register_helper_script(helper_entry[:name])
    end
  end
end