# frozen_string_literal: true

module Kramdown
  # A `Kramdown::Document`` subclass meant to optimize memory usage from initializing
  # a kramdown document for parsing.
  #
  # The optimization is by using the same options Hash (and its derivatives) for
  # converting all Markdown documents in a Bridgetown site.
  class BridgetownDocument < Document
    class << self
      attr_reader :options, :parser

      # The implementation is basically the core logic in +Kramdown::Document#initialize+
      def setup(options)
        @cache ||= {}

        # reset variables on a subsequent set up with a different options Hash
        unless @cache[:id] == options.hash
          @options = @parser = nil
          @cache[:id] = options.hash
        end

        @options ||= Options.merge(options).freeze
        @parser  ||= begin
          parser_name = (@options[:input] || "kramdown").to_s
          parser_name = parser_name[0..0].upcase + parser_name[1..]
          try_require("parser", parser_name)

          if Parser.const_defined?(parser_name)
            Parser.const_get(parser_name)
          else
            raise Kramdown::Error, "kramdown has no parser to handle the specified " \
                                   "input format: #{@options[:input]}"
          end
        end
      end

      private

      def try_require(type, name)
        require "kramdown/#{type}/#{Utils.snake_case(name)}"
      rescue LoadError
        false
      end
    end

    def initialize(source, options = {}) # rubocop:disable Lint/MissingSuper
      # NOTE: keep `options = {}` instead of the newer `**options` for Kramdown compatibility

      BridgetownDocument.setup(options)

      @options = BridgetownDocument.options
      @root, @warnings = BridgetownDocument.parser.parse(source, @options)
    end

    # Use Kramdown::Converter::Html class to convert this document into HTML.
    #
    # The implementation is basically an optimized version of core logic in
    # +Kramdown::Document#method_missing+ from kramdown-2.1.0.
    def to_html
      output, warnings = Kramdown::Converter::Html.convert(@root, @options)
      @warnings.concat(warnings)
      output
    end
  end
end

#

module Bridgetown
  module Converters
    class Markdown
      class KramdownParser
        attr_reader :extractions

        def initialize(config)
          @config = config["kramdown"] || {}
          @config["syntax_highlighter"] ||= config["highlighter"] || "rouge"
          @config["syntax_highlighter_opts"] ||= {}
          @config["syntax_highlighter_opts"]["guess_lang"] = @config["guess_lang"]
          require_relative "../../kramdown/parser/gfm" if @config["input"] == "GFM"
        end

        def convert(content)
          document = Kramdown::BridgetownDocument.new(content, @config)
          html_output = document.to_html
          if @config["show_warnings"]
            document.warnings.each do |warning|
              Bridgetown.logger.warn "Kramdown warning:", warning
            end
          end
          @extractions = document.root.options[:extractions] # could be nil
          html_output
        end
      end
    end
  end
end
