# frozen_string_literal: true

class SolidusAdmin::UI::Table::Component < SolidusAdmin::BaseComponent
  Column = Struct.new(:header, :data, :col, :wrap, keyword_init: true)
  Sortable = Struct.new(:url, :param, :animation, :handle, keyword_init: true)
  Scope = Struct.new(:label, :name, :default, keyword_init: true)
  Filter = Struct.new(:label, :combinator, :attribute, :predicate, :options, keyword_init: true)
  BatchAction = Struct.new(:label, :icon, :action, :require_confirmation, :method, keyword_init: true) # rubocop:disable Lint/StructNewOverride
  private_constant :BatchAction, :Column, :Filter, :Scope, :Sortable

  class Data < Struct.new(:rows, :class, :url, :prev, :next, :columns, :fade, :batch_actions, keyword_init: true) # rubocop:disable Lint/StructNewOverride,Style/StructInheritance
    def initialize(**args)
      super

      self.columns = columns.map do |column|
        column.is_a?(Symbol) ? Column.new(wrap: false, header: column, data: column) : Column.new(wrap: false, **column)
      end
      self.batch_actions = batch_actions.to_a.map { |action| BatchAction.new(**action) }
    end

    def singular_name
      self[:class].model_name.human if self[:class]
    end

    def plural_name
      self[:class].model_name.human.pluralize if self[:class]
    end
  end

  class Search < Struct.new(:name, :value, :url, :searchbar_key, :scopes, :filters, keyword_init: true) # rubocop:disable Style/StructInheritance
    def initialize(**args)
      super

      self.filters = filters.to_a.map { |filter| Filter.new(**filter) }
      self.scopes = scopes.to_a.map { |scope| Scope.new(**scope) }
    end

    def current_scope
      scopes.find { |scope| scope.name.to_s == value[:scope].presence } || default_scope
    end

    def default_scope
      scopes.find(&:default)
    end

    def on_default_scope?
      current_scope == default_scope
    end

    def scope_param_name
      "#{name}[scope]"
    end

    def searchbar_param_name
      "#{name}[#{searchbar_key}]"
    end

    def value
      super || {}
    end
  end

  def initialize(id:, data:, search: nil, sortable: nil)
    @id = id
    @data = Data.new(**data)
    @data.columns.unshift selectable_column if @data.batch_actions.present? && @data.rows.present?
    @search = Search.new(**search) if search
    @sortable = Sortable.new(**sortable) if sortable
  end

  def selectable_column
    @selectable_column ||= Column.new(
      header: -> {
        component("ui/forms/checkbox").new(
          form: batch_actions_form_id,
          "data-action": "#{stimulus_id}#selectAllRows",
          "data-#{stimulus_id}-target": "headerCheckbox",
          "aria-label": t('.select_all'),
        )
      },
      data: ->(data) {
        component("ui/forms/checkbox").new(
          name: "id[]",
          form: batch_actions_form_id,
          value: data.id,
          "data-action": "#{stimulus_id}#selectRow",
          "data-#{stimulus_id}-target": "checkbox",
          "aria-label": t('.select_row'),
        )
      },
      col: { class: 'w-[52px]' },
    )
  end

  def batch_actions_form_id
    @batch_actions_form_id ||= "#{stimulus_id}--batch-actions-#{@id}"
  end

  def search_form_id
    @search_form_id ||= "#{stimulus_id}--search-form-#{@id}"
  end

  def render_batch_action_button(batch_action)
    params = {
      name: request_forgery_protection_token,
      value: form_authenticity_token(form_options: {
        action: batch_action.action,
        method: batch_action.method,
      }),
      formaction: batch_action.action,
      formmethod: batch_action.method,
      form: batch_actions_form_id,
      type: :submit,
      icon: batch_action.icon,
      text: batch_action.label,
      scheme: :secondary,
    }

    if batch_action.require_confirmation
      params["data-action"] = "click->#{stimulus_id}#confirmAction"
      params["data-#{stimulus_id}-message-param"] = t(
        ".action_confirmation",
        action: batch_action.label.downcase
      )
      params["data-#{stimulus_id}-resource-singular-param"] = @data.singular_name.downcase
      params["data-#{stimulus_id}-resource-plural-param"] = @data.plural_name.downcase
    end

    render component("ui/button").new(**params)
  end

  def render_ransack_filter_dropdown(filter, index)
    render component("ui/table/ransack_filter").new(
      presentation: filter.label,
      search_param: @search.name,
      combinator: filter.combinator,
      attribute: filter.attribute,
      predicate: filter.predicate,
      options: filter.options,
      form: search_form_id,
      index:,
    )
  end

  def render_header_cell(cell, **attrs)
    cell = cell.call if cell.respond_to?(:call)
    cell = @data[:class].human_attribute_name(cell) if cell.is_a?(Symbol)
    cell = cell.render_in(self) if cell.respond_to?(:render_in)

    content_tag(:th, cell, class: %{
      border-b
      border-gray-100
      px-4
      h-9
      font-semibold
      vertical-align-middle
      leading-none
    }, **attrs)
  end

  def render_data_cell(column, data)
    cell = column.data
    cell = cell.call(data) if cell.respond_to?(:call)
    cell = data.public_send(cell) if cell.is_a?(Symbol)
    cell = cell.render_in(self) if cell.respond_to?(:render_in)
    cell = tag.div(cell, class: "flex items-center gap-1.5 justify-start overflow-x-hidden") if column.wrap

    tag.td(cell, class: "
      py-2 px-4 h-10 vertical-align-middle leading-none
      [tr:last-child_&:first-child]:rounded-bl-lg [tr:last-child_&:last-child]:rounded-br-lg
    ")
  end

  def current_scope_name
    @search.current_scope.name
  end

  def initial_mode
    @initial_mode ||=
      if @search && (@search.value[@search.searchbar_key] || @search.scopes.none?)
        "search"
      else
        "scopes"
      end
  end

  def should_enable_sortable?
    @sortable && @search&.on_default_scope?
  end
end
