class WikiPagesController < ApplicationController
  def index
    wiki_pages = WikiPage.all

    render json: wiki_pages
  end

  def show
    render_wiki_page_or_404 WikiPage.find(params[:id])
  end

  def show_by_title
    render_wiki_page_or_404 WikiPage.find_by(title: params[:title])
  end

  def exists
    if WikiPage.exists?(params[:id])
      head :no_content
    else
      head :not_found
    end
  end

  def exists_by_title
    if WikiPage.exists?(title: params[:title])
      head :no_content
    else
      head :not_found
    end
  end

  def diff
    id = params[:id]
    from = params[:from]
    to = params[:to].presence
    return head :bad_request if id.blank? || from.blank?

    wiki_page_from = WikiPage.find(id)
    wiki_page_to = WikiPage.find(id)
    wiki_page_from.sha = from
    wiki_page_to.sha = to

    diffs = Diff::LCS.sdiff(wiki_page_from.body, wiki_page_to.body)
    diff_json = diffs.map { |change|
      case change.action
      when ?=
        { type: 'context', content: change.old_element }
      when ?|
        [{ type: 'removed', content: change.old_element },
         { type: 'added', content: change.new_element }]
      when ?+
        { type: 'added', content: change.new_element }
      when ?-
        { type: 'removed', content: change.old_element }
      end
    }.flatten.compact

    render json: { wiki_page_id:  wiki_page_from.id,
                   title:         wiki_page_from.title,
                   older_sha:     wiki_page_from.sha,
                   newer_sha:     wiki_page_to.sha,
                   diff:          diff_json }
  end

  def create
    return head :unauthorized unless current_user
    return head :forbidden unless ['admin', 'member'].include?(current_user.role)

    wiki_page = WikiPage.new(title: params[:title], created_user: current_user, updated_user: current_user)
    if wiki_page.save
      wiki_page.set_body params[:body], user: current_user
      render json: wiki_page, status: :created
    else
      render json: { errors: wiki_page.errors.full_messages }, status: :unprocessable_entity
    end
  end

  def update
    return head :unauthorized unless current_user
    return head :forbidden unless current_user.member?

    title = params[:title]
    body = params[:body]

    return head :unprocessable_entity if title.blank? || body.blank?

    wiki_page = WikiPage.find(params[:id])
    wiki_page.title = title
    wiki_page.updated_user = current_user
    wiki_page.set_body(body, user: current_user)
    wiki_page.save!

    head :ok
  end

  def search
    title = params[:title]&.strip

    q = WikiPage.all
    q = q.where('title LIKE ?', "%#{ WikiPage.sanitize_sql_like(title) }%") if title.present?

    render json: q.limit(20)
  end

  def changes
    id = params[:id]
    log = if id.present?
            wiki.page("#{ id }.md")&.versions
          else
            wiki.repo.log('main', nil)
          end
    return render json: [] unless log

    render json: log.map { |commit|
      wiki_page = WikiPage.find(commit.message.split(' ')[1].to_i)
      wiki_page.sha = commit.id

      next nil if wiki_page.sha.blank?

      user = User.find(commit.author.name.to_i)

      { sha:          wiki_page.sha,
        pred:         wiki_page.pred,
        succ:         wiki_page.succ,
        wiki_page:    wiki_page && { id: wiki_page.id, title: wiki_page.title },
        user:         user && { id: user.id, name: user.name },
        change_type:  commit.message.split(' ')[0].downcase[0...(-1)],
        timestamp:    commit.authored_date }
    }.compact
  end

  private

  WIKI_PATH = Rails.root.join('wiki').to_s

  def wiki
    @wiki ||= Gollum::Wiki.new(WIKI_PATH)
  end

  def render_wiki_page_or_404 wiki_page
    return head :not_found unless wiki_page

    wiki_page.sha = params[:version].presence

    body = wiki_page.body
    sha = wiki_page.sha
    pred = wiki_page.pred
    succ = wiki_page.succ
    render json: wiki_page.as_json.merge(body:, sha:, pred:, succ:)
  end
end
