# frozen_string_literal: true

require "digest"
require "serbea/pipeline"
require "streamlined/helpers"
require "streamlined/renderable"

module Bridgetown
  module Streamlined
    include ::Streamlined::Renderable
    include Serbea::Pipeline::Helper
    include ERBCapture

    def helper(name, &helper_block)
      self.class.define_method(name) do |*args, **kwargs, &block|
        helper_block.call(*args, **kwargs, &block)
      end
    end
    alias_method :macro, :helper
  end

  class RubyTemplateView
    require "bridgetown-core/helpers"

    using Bridgetown::Refinements
    include Bridgetown::Streamlined

    attr_reader :layout, :resource, :paginator, :site, :content
    alias_method :page, :resource

    def initialize(convertible)
      if convertible.is_a?(Layout)
        @layout = convertible
        @resource = layout.current_document
        @content = layout.current_document_output
      else
        @layout = convertible.site.layouts[convertible.data["layout"]]
        @resource = convertible
      end
      @paginator = resource.paginator if resource.respond_to?(:paginator)
      @site = resource.site
      @support_data_as_view_methods = @site.config.support_data_as_view_methods
    end

    def data = resource.data

    def collections = site.collections

    def site_drop = site.site_payload.site

    def partial(_partial_name = nil, **_options) = raise("Must be implemented in a subclass")

    def render(item, **options, &)
      if item.respond_to?(:render_in)
        result = item.render_in(self, &)
        result&.html_safe
      else
        partial(item, **options, &)&.html_safe
      end
    end

    def liquid_render(component, **options, &block)
      options[:_block_content] = capture(&block) if block && respond_to?(:capture)
      render_statement = _render_statement(component, options)

      template = site.liquid_renderer.file(
        "#{resource.path}.#{Digest::SHA2.hexdigest(render_statement)}"
      ).parse(render_statement)
      template.warnings.each do |e|
        Bridgetown.logger.warn "Liquid Warning:",
                               LiquidRenderer.format_error(e, path || document.relative_path)
      end
      template.render!(options.as_dots, _liquid_context).html_safe
    end

    def helpers
      @helpers ||= Helpers.new(self, site)
    end

    def data_key?(key, *args, **kwargs)
      return false unless @support_data_as_view_methods

      args.empty? && kwargs.empty? && !block_given? && data.key?(key)
    end

    def method_missing(method_name, ...)
      if helpers.respond_to?(method_name.to_sym)
        helpers.send(method_name.to_sym, ...)
      elsif data_key?(method_name, ...)
        data[method_name]
      else
        super
      end
    end

    def respond_to_missing?(method_name, include_private = false)
      helpers.respond_to?(method_name.to_sym, include_private) || super
    end

    def inspect = "#<#{self.class} layout=#{layout&.label} resource=#{resource.relative_path}>"

    private

    def _render_statement(component, options)
      render_statement = options[:_block_content] ?
                           ["{% rendercontent \"#{component}\""] :
                           ["{% render \"#{component}\""]
      unless options.empty?
        render_statement << ", #{options.keys.map { |k| "#{k}: #{k}" }.join(", ")}"
      end
      render_statement << " %}"
      if options[:_block_content]
        render_statement << options[:_block_content]
        render_statement << "{% endrendercontent %}"
      end
      render_statement.join
    end

    def _liquid_context
      {
        registers: {
          site:,
          page: resource.to_liquid,
          cached_partials: Bridgetown::Converters::LiquidTemplates.cached_partials,
        },
        strict_filters: site.config["liquid"]["strict_filters"],
        strict_variables: site.config["liquid"]["strict_variables"],
      }
    end

    def _partial_path(partial_name, ext)
      partial_name = partial_name.split("/").tap { _1.last.prepend("_") }.join("/")

      site.in_source_dir(site.config[:partials_dir], "#{partial_name}.#{ext}")
    end
  end
end
