class PostVersionsController < ApplicationController
  def index
    post_id = params[:post].presence
    tag_id = params[:tag].presence&.to_i
    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 = PostVersion.joins(<<~SQL.squish)
          LEFT JOIN
            post_versions prev
            ON
              prev.post_id = post_versions.post_id
              AND prev.version_no = post_versions.version_no - 1
        SQL
        .select('post_versions.*',
                'prev.title AS prev_title',
                'prev.url AS prev_url',
                'prev.thumbnail_base AS prev_thumbnail_base',
                'prev.tags_json AS prev_tags_json',
                'prev.video_ms AS prev_video_ms',
                'prev.original_created_from AS prev_original_created_from',
                'prev.original_created_before AS prev_original_created_before')
    q = q.where('post_versions.post_id = ?', post_id) if post_id
    if tag_id
      q = q.where(("JSON_CONTAINS(post_versions.tags_json, JSON_OBJECT('id', :tag_id)) " +
                   "OR JSON_CONTAINS(prev.tags_json, JSON_OBJECT('id', :tag_id))"),
                  tag_id:)
    end

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

    versions = q.order(Arel.sql('post_versions.created_at DESC, post_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_tags =
          normalise_json(row.tags_json)
          .sort_by { [(case _1.fetch('category')
                       when 'deerjikist'
                         0
                       when 'meme'
                         1
                       when 'character'
                         2
                       when 'general'
                         3
                       when 'material'
                         4
                       when 'meta'
                         5
                       else
                         6
                       end),
                      _1.fetch('name').downcase] }
          .map { Post.tag_snapshot_literal(_1) }
      prev_tags =
          (normalise_json(row.attributes['prev_tags_json']) || [])
          .sort_by { [(case _1.fetch('category')
                       when 'deerjikist'
                         0
                       when 'meme'
                         1
                       when 'character'
                         2
                       when 'general'
                         3
                       when 'material'
                         4
                       when 'meta'
                         5
                       else
                         6
                       end),
                      _1.fetch('name').downcase] }
          .map { Post.tag_snapshot_literal(_1) }

      { post_id: row.post_id,
        version_no: row.version_no,
        event_type: row.event_type,
        title: { current: row.title, prev: row.attributes['prev_title'] },
        url: { current: row.url, prev: row.attributes['prev_url'] },
        thumbnail: { current: nil, prev: nil },
        thumbnail_base: { current: row.thumbnail_base,
                          prev: row.attributes['prev_thumbnail_base'] },
        video_ms: { current: row.video_ms, prev: row.attributes['prev_video_ms'] },
        tags: build_version_tags(cur_tags, prev_tags),
        original_created_from: {
            current: row.original_created_from&.iso8601,
            prev: row.attributes['prev_original_created_from']&.iso8601 },
        original_created_before: {
            current: row.original_created_before&.iso8601,
            prev: row.attributes['prev_original_created_before']&.iso8601 },
        created_at: row.created_at.iso8601,
        created_by_user:
            if row.created_by_user_id
              { id: row.created_by_user_id,
                name: users_by_id[row.created_by_user_id] }
            end }
    end
  end

  def build_version_tags(cur_tags, prev_tags)
    (cur_tags | prev_tags).map do |name|
      type =
        if cur_tags.include?(name) && prev_tags.include?(name)
          'context'
        elsif cur_tags.include?(name)
          'added'
        else
          'removed'
        end

      {
        name:,
        type:
      }
    end
  end
end
