class TagVersionsController < ApplicationController
  def index
    tag_id = params[:id].presence
    page = (params[:page].presence || 1).to_i
    limit = (params[:limit].presence || 20).to_i

    page = 1 if page < 1
    limit = 1 if limit < 1

    offset = (page - 1) * limit

    q = TagVersion.joins(<<~SQL.squish)
          LEFT JOIN
            tag_versions prev
            ON
              prev.tag_id = tag_versions.tag_id
              AND prev.version_no = tag_versions.version_no - 1
        SQL
      .select('tag_versions.*', 'prev.name AS prev_name', 'prev.category AS prev_category',
              'prev.deprecated_at AS prev_deprecated_at',
              'prev.aliases AS prev_aliases', 'prev.parent_tag_ids AS prev_parent_tag_ids')
    q = q.where('tag_versions.tag_id = ?', tag_id) if tag_id

    count = q.except(:select, :order, :limit, :offset).count

    versions = q.order(Arel.sql('tag_versions.created_at DESC, tag_versions.id DESC'))
                .limit(limit)
                .offset(offset)

    render json: { versions: serialise_versions(versions), count: }
  end

  private

  def serialise_versions rows
    user_ids = rows.map(&:created_by_user_id).compact.uniq
    users_by_id = User.where(id: user_ids).pluck(:id, :name).to_h

    rows.map do |row|
      cur_aliases = split_values(row.aliases)
      prev_aliases = split_values(row.attributes['prev_aliases'])

      cur_parent_tag_ids = split_parent_tag_ids(row.parent_tag_ids)
      prev_parent_tag_ids = split_parent_tag_ids(row.attributes['prev_parent_tag_ids'])

      all_parent_tag_ids = (cur_parent_tag_ids | prev_parent_tag_ids)

      tags_by_id =
          Tag
          .includes(:tag_name, :materials, { tag_name: :wiki_page })
          .where(id: all_parent_tag_ids)
          .index_by(&:id)

      parent_tags =
          build_version_values(cur_parent_tag_ids, prev_parent_tag_ids, key: :tag_id)
          .map do |h|
            { tag: TagRepr.base(tags_by_id[h[:tag_id]]),
              type: h[:type] }
          end

      { tag_id: row.tag_id,
        version_no: row.version_no,
        event_type: row.event_type,
        name: { current: row.name, prev: row.attributes['prev_name'] },
        category: { current: row.category, prev: row.attributes['prev_category'] },
        deprecated_at: { current: row.deprecated_at&.iso8601,
                         prev: row.attributes['prev_deprecated_at']&.iso8601 },
        aliases: build_version_values(cur_aliases, prev_aliases, key: :name),
        parent_tags:,
        created_at: row.created_at.iso8601,
        created_by_user: row.created_by_user_id &&
                         { id: row.created_by_user_id,
                           name: users_by_id[row.created_by_user_id] } }
    end
  end

  def build_version_values cur_values, prev_values, key:
    (cur_values | prev_values).map do |value|
      type =
          if cur_values.include?(value) && prev_values.include?(value)
            'context'
          elsif cur_values.include?(value)
            'added'
          else
            'removed'
          end

      { key => value, type: }
    end
  end

  def split_values(values) = values.to_s.split(/\s+/).reject(&:blank?)

  def split_parent_tag_ids(values) = split_values(values).map(&:to_i)
end
