module Symphonia
  module ApplicationHelper

    include FormHelper

    def bootstrap_class_for(flash_type)
      case flash_type.to_sym
      when :notice
        'alert-success'
      when :error, :alert
        'alert-danger'
      when :warning
        'alert-warning'
      when :info
        'alert-info'
      else
        flash_type.to_s
      end
    end

    # Renders flash messages
    def render_flash_messages(flash_messages = nil)
      s = ''
      Array(flash_messages || flash).each do |type, message|
        s << tag.div(class: "d-print-none alert #{bootstrap_class_for(type)}") do
          tag.button('', class: 'fa fa-circle-xmark close', data: { dismiss: 'alert' }) + Array.wrap(message).collect { |m| icon(type, text: m) }.join("<br>").html_safe
        end
      end

      s.html_safe
    end

    def render_menu(menu, options = {})
      s = ''
      Symphonia::MenuManager.menu(menu).each do |name, item|
        s << render_menu_node(name, item).to_s
      end
      options[:container_class] ||= 'mr-auto'

      tag.ul(s.html_safe, itemscope: '', itemtype: 'http://schema.org/BreadcrumbList', class: "navbar-nav #{options[:container_class]}", id: menu.to_s)
    end

    def render_menu_node(menu, item, options = {})
      condition = item[:if] ? item[:if].call : true
      return nil unless condition

      selected = @menu_item.to_sym == menu
      label = case item[:label].class.name
              when 'NilClass'
                '&nbsp;'.html_safe
              when 'String'
                item[:label]
              when 'Symbol'
                t(item[:label])
              when 'Proc'
                item[:label].call(controller)
              else
                raise "MenuManager error: Label is unknown type: #{item[:label].class}"
              end
      if item[:children].blank?
        tag.li(render_menu_link(item, label, options), class: "nav-item #{menu} #{'active' if selected} #{options[:class]}", id: item[:id])
      else
        children = ''
        item[:children].each do |child, subitem|
          children << render_menu_node(menu, subitem, class: 'dropdown-item').to_s
        end
        if children.present?
          tag.li(class: "nav-item dropdown #{menu}") do
            concat render_menu_link(item.merge({ class: 'dropdown-toggle', data: { toggle: 'dropdown' } }), label, { is_submenu: true })
            concat tag.ul(children.html_safe, class: 'dropdown-menu')
          end
        end
      end
    end

    def render_menu_link(item, label, _options = {})
      url = case item[:url].class.name
            when 'Symbol'
              if item[:url].to_s.include?('.')
                endpoint, path = item[:url].to_s.split('.')
                send(endpoint).send(path)
              else
                main_app.send item[:url]
              end
            when 'Proc'
              item[:url].call(self)
            else
              item[:url]
            end
      link_to(
        (tag.i('', class: "#{item[:icon]}") + "\n" + tag.span(label, itemprop: 'title')).html_safe,
        url,
        class: "nav-link #{item[:class]}",
        data: item[:data],
        method: item[:method],
        itemprop: 'url'
      )
    end

    def html_title(*args)
      if args.empty?
        title = @html_title || []
        title << t(:meta_title)
        title.reject(&:blank?).join(' — ')
      else
        @html_title ||= []
        @html_title += args
      end
    end

    def html_description(*args)
      if args.empty?
        desc = @html_description
        desc ||= t(:meta_description)
        desc.to_s
      else
        @html_description ||= []
        @html_description += args
      end
    end

    def title(*args, &block)
      options = args.extract_options!
      header = args.shift
      small = args.shift || ''
      header_text = if header.is_a?(Symbol)
                      t(header, default: header.to_s.humanize)
                    else
                      header.to_s.dup
                    end
      if @symphonia_modal_dialog
        ActiveSupport::Deprecation.warn "@symphonia_modal_dialog is no used anymore !"
        @symphonia_modal_dialog.title ||= header_text
        ''
      else
        html_title(header_text.dup)
        header_text << ("\n" << tag.small(small, class: 'text-muted')) if small.present?
        s = ''
        if options[:back] && !request.xhr?
          back_url = options[:back] unless options[:back].is_a? TrueClass
          s << link_to_back(back_url)
        end
        s << capture(&block).to_s if block_given?
        header_class = (s.present? && "col-6") || nil
        header_tag = content_tag(((request.xhr? && :h5) || :h1), id: 'page_header', class: header_class) do
          header_text.html_safe
        end
        return header_tag if s.blank?

        tag.div(class: "row") do
          header_tag + tag.div(s.html_safe, class: "col-6 text-right")
        end
      end
    end

    alias_method :page_header, :title

    def render_no_data(message = nil)
      tag.div(icon("circle-info", text: message || t(:text_no_data)), class: 'alert alert-info text-center nodata')
    end

    def content_for(name, content = nil, &block)
      @has_content ||= {}
      @has_content[name] = true
      super(name, content, &block)
    end

    def has_content?(name)
      !!(@has_content && @has_content[name])
    end

    def format_text(text, _options = {})
      return '' if text.nil?

      markdown = RDiscount.new(text, :smart, :filter_html)
      markdown.to_html.html_safe
    end

    def format_html(text)
      tag.div((defined?(Ckeditor) ? text.html_safe : format_text(text)), class: 'formatted-text')
    end

    def format_price(value, options = {})
      number_to_currency(value, { precision: 1, strip_insignificant_zeros: true }.merge(options))
    end

    def multiselect_toggler(id = nil)
      link_to(icon('plus'), 'javascript:void(0);', onclick: "toggleMultiSelect(#{id || 'this'});return false", class: 'btn fa fa-border')
    end

    def link_to_back(url = nil)
      link_to(icon(:back, t(:button_back)), (params[:back_url] || url || :back), class: 'btn btn-link back')
    end

    def link_to_new_entity(options = {})
      anchor = options.has_key?(:anchor) ? options.delete(:anchor) : 'page_header'
      label = options.delete(:label) || t("label_#{controller_name.singularize}_new")
      model = controller.try(:model) || controller_name.singularize
      url = options.delete(:url) || new_polymorphic_path(model, anchor: anchor)

      link_to(icon('square-plus', text: label), url, { class: 'btn btn-primary' }.merge(options))
    end

    # change the default link renderer for will_paginate
    def will_paginate(collection_or_options = nil, options = {})
      if collection_or_options.is_a? Hash
        options, collection_or_options = collection_or_options, nil
      end
      unless options[:renderer]
        options = options.merge renderer: Symphonia::BootstrapLinkRender
      end
      options[:query] ||= @query if @query
      super *[collection_or_options, options].compact
    end

    def ckeditor_for(field_id, options = {})
      return '' unless !!defined?(Ckeditor)

      inline = options.delete(:inline)
      opts = options.inject({}) do |mem, var|
        key = var[0].to_s.camelcase(:lower)
        key[0].downcase!
        mem[key] = var[1]

        mem
      end
      opts['toolbar'] ||= 'Basic'
      # opts['customConfig'] ||= 'Basic'
      js = if inline
             "CKEDITOR.inline('#{field_id}', {toolbar: 'Basic'});"
           else
             "
        var ta_editor = CKEDITOR.instances['#{field_id}'];
        if (ta_editor) {CKEDITOR.remove(ta_editor);}
        CKEDITOR.replace('#{field_id}', #{opts.to_json.html_safe});
      "
           end
      javascript_tag("$(document).ready(function() {#{js.html_safe}})".html_safe)
    end

    # prepend FontAwesome::Sass::Rails::ViewHelpers
    def icon(icon, text = nil, html_options = {})
      if text.is_a?(Hash)
        html_options = text
        text = nil
      end

      text_content = if text
                       tag.span(text, class: 'd-none d-sm-inline')
                     elsif html_options[:text]
                       html_options.delete(:text)
                     end
      html_options[:title] ||= text
      html_options[:class] = "fa-solid fa-#{icon}"
      html_options['aria-hidden'] ||= true

      html = tag.i(nil, **html_options)
      html << ' ' << text_content.to_s if text_content.present?
      html
    end

    def fa_icon(fa, options = {})
      ActiveSupport::Deprecation.warn "use `icon` instead"
      icon(fa, options.delete(:text), options)
    end

    # Render original template from engine
    #   Useful for override part of engine view
    #
    # @example render_super "login/new"
    # @param [String] template_name
    # @param [Class<Symphonia::Engine>] engine
    # @param [String] format
    def render_super(template_name, engine: Symphonia::Engine, format: "html")
      resolver = lookup_context.view_paths.paths.find do |resolver|
        resolver.path == engine.root.join("app", "views").to_s
      end
      template = resolver.find_all(template_name, engine.engine_name, false, { locale: ["."], formats: [format], variants: [], handlers: [:erb] }, nil, {}).first
      return "" unless template

      render template: template
    end

    def render_symphonia_dialog(*args, &block)
      ActiveSupport::Deprecation.warn "Use `render_modal` instead"
      options = args.extract_options!
      title = options[:title] || args.shift # first arg possible `title`
      body = args.shift
      options[:form_disabled] = true
      size = options.delete(:size)
      size ||= '90%' if options.delete(:large)
      if size.to_s.match(%r(^\d+$))
        size = size.to_s + '%'
      end
      @symphonia_modal_dialog = SymphoniaModalDialog.new(self, options)
      @symphonia_modal_dialog.size = size
      if block_given?
        yield @symphonia_modal_dialog
      else
        raise ArgumentError if body.nil?
      end
      @symphonia_modal_dialog.title ||= title
      if @symphonia_modal_dialog.body.blank?
        if body.is_a?(Hash)
          body.merge!(formats: [:html])
        else
          body = { partial: body, formats: [:html] }
        end
        @symphonia_modal_dialog.body = render(body)
      end
      html = @symphonia_modal_dialog.to_html
      if options[:render_only]
        html
      else
        js = %Q(
        $('##{@symphonia_modal_dialog.modal_id}').modal('hide').remove();
        $('body').append('#{j html}');
        showModal('##{@symphonia_modal_dialog.modal_id}');
        $("##{@symphonia_modal_dialog.modal_id} .modal-dialog input:text").first().focus();
        $("##{@symphonia_modal_dialog.modal_id} .modal-dialog .modal-body div.buttons").remove();
      )
        js.concat("$('##{@symphonia_modal_dialog.modal_id} .modal-dialog').css({width: '#{size}'});") if size.present?
        js.html_safe
      end
    end

    # def render_modal_dialog(show = true, options = {}, &block)
    #   tags = SymphoniaModalDialog.new(self, options)
    #   yield tags if block_given?
    #   html = tags.to_html
    #   if show
    #     "$('##{tags.modal_id}').remove();$('body').append('#{j html}'); showModal('##{tags.modal_id}');".html_safe
    #   else
    #     html
    #   end
    # end

    class SymphoniaModalDialog

      attr_writer :title, :body, :footer
      attr_reader :modal_id, :title

      attr_accessor :size

      def initialize(controller, options = {})
        @c = controller
        @title = options.delete(:title)
        @modal_id = options.delete(:id) || 'modal-dialog'
        @form_options = options.delete(:form_options) || {}
        @options = options
      end

      alias_attribute :id, :modal_id

      def to_html
        html = "<div id='#{@modal_id}' style='' class='modal fade' role='dialog'><div class='modal-dialog #{'modal-lg' if size.present?}'><div class='modal-content'>"
        html << @c.tag.div(class: 'modal-header') do
          @c.tag.button('', class: 'close fa fa-times', data: { dismiss: 'modal' }, 'aria-hidden' => true) + @c.tag.h4(@title, class: 'modal-title') + @header.to_s
        end
        content = @c.tag.div(@c.tag.div(body.html_safe, class: 'modal-content-inner-container container-fluid'), class: 'modal-body')
        content << @c.tag.div(footer.html_safe, class: 'modal-footer')

        html << content.html_safe
        html << '</div></div></div>'
        html.html_safe
      end

      def header(&block)
        if block_given?
          @header = @c.capture(&block)
        else
          @header = (@header.is_a?(Proc) ? @header.call.to_s : @header.to_s)
        end
      end

      def body(&block)
        if block_given?
          @body = @c.capture(&block)
        else
          @body = (@body.is_a?(Proc) ? @body.call.to_s : @body.to_s)
        end
      end

      def footer(&block)
        if block_given?
          @footer = @c.capture(&block)
        else
          @footer = (@footer.is_a?(Proc) ? @footer.call.to_s : @footer.to_s)
        end
      end

      def submit(name = nil, options = {})
        name ||= @c.t(:button_save)
        @footer = footer.to_s + @c.link_to(name, 'javascript:void(0)', { onclick: "$('##{@modal_id}').find('form').submit()", class: 'btn btn-primary' }.merge(options)).html_safe
      end

    end
  end
end