require 'cgi'
require 'rails_helper'

RSpec.describe 'Wiki history integrity', type: :request do
  let!(:user) { create_member_user! }

  def auth_headers user
    { 'X-Transfer-Code' => user.inheritance_code }
  end

  def create_wiki_page title:, body: 'body', message: 'init', user: self.user
    Wiki::Commit.create_content!(
        tag_name: TagName.create!(name: title),
        body:,
        created_by_user: user,
        message:)
  end

  describe 'POST /wiki' do
    it 'creates wiki_page, wiki_revision, and wiki_version atomically' do
      expect {
        post '/wiki',
             params: {
               title: 'wiki_history_create_atomic',
               body: "a\nb\nc",
               message: 'initial commit',
             },
             headers: auth_headers(user)
      }
          .to change(WikiPage, :count).by(1)
          .and change(WikiRevision, :count).by(1)
          .and change(WikiVersion, :count).by(1)

      expect(response).to have_http_status(:created)

      page = WikiPage.find(json.fetch('id'))
      revision = page.current_revision
      version = page.wiki_versions.order(:version_no).last

      expect(page.title).to eq('wiki_history_create_atomic')
      expect(page.body).to eq("a\nb\nc")

      expect(revision).to be_content
      expect(revision.message).to eq('initial commit')
      expect(revision.lines_count).to eq(3)

      expect(version).to have_attributes(
        version_no: 1,
        event_type: 'create',
        title: 'wiki_history_create_atomic',
        body: "a\nb\nc",
        reason: 'initial commit',
        created_by_user_id: user.id
      )
    end

    it 'returns 422 and creates nothing when normalised body is blank' do
      expect {
        post '/wiki',
             params: {
               title: 'wiki_history_blank_body',
               body: "\r\n\r\n",
               message: 'blank',
             },
             headers: auth_headers(user)
      }
          .not_to change(WikiPage, :count)

      expect(response).to have_http_status(:unprocessable_entity)
      expect(WikiPage.joins(:tag_name).where(tag_names: { name: 'wiki_history_blank_body' })).not_to exist
    end

    it 'returns 422 and creates no partial page when title already exists' do
      create_wiki_page(title: 'wiki_history_duplicate_title', body: 'first')

      expect {
        post '/wiki',
             params: {
               title: 'wiki_history_duplicate_title',
               body: 'second',
               message: 'duplicate',
             },
             headers: auth_headers(user)
      }
          .not_to change(WikiPage, :count)

      expect(response).to have_http_status(:unprocessable_entity)
      expect(WikiPage.joins(:tag_name).where(tag_names: { name: 'wiki_history_duplicate_title' }).count).to eq(1)
    end
  end

  describe 'PUT /wiki/:id' do
    it 'updates body and records wiki_revision and wiki_version' do
      page = create_wiki_page(title: 'wiki_history_update_body', body: 'before')
      current_id = page.current_revision.id

      expect {
        put "/wiki/#{ page.id }",
            params: {
              title: 'wiki_history_update_body',
              body: 'after',
              message: 'edit body',
              base_revision_id: current_id,
            },
            headers: auth_headers(user)
      }
          .to change(WikiRevision, :count).by(1)
          .and change(WikiVersion, :count).by(1)

      expect(response).to have_http_status(:ok)

      page.reload
      version = page.wiki_versions.order(:version_no).last

      expect(page.title).to eq('wiki_history_update_body')
      expect(page.body).to eq('after')

      expect(version).to have_attributes(
        event_type: 'update',
        title: 'wiki_history_update_body',
        body: 'after',
        reason: 'edit body',
        created_by_user_id: user.id
      )
    end

    it 'renames title and records wiki_version with new title' do
      page = create_wiki_page(title: 'wiki_history_rename_before', body: 'before')
      current_id = page.current_revision.id

      expect {
        put "/wiki/#{ page.id }",
            params: {
              title: 'wiki_history_rename_after',
              body: 'after',
              message: 'rename',
              base_revision_id: current_id,
            },
            headers: auth_headers(user)
      }
          .to change(WikiRevision, :count).by(1)
          .and change(WikiVersion, :count).by(1)

      expect(response).to have_http_status(:ok)

      page.reload
      version = page.wiki_versions.order(:version_no).last

      expect(page.title).to eq('wiki_history_rename_after')
      expect(page.body).to eq('after')

      expect(version).to have_attributes(
        event_type: 'update',
        title: 'wiki_history_rename_after',
        body: 'after',
        reason: 'rename',
        created_by_user_id: user.id
      )
    end

    it 'does not change title, body, revision, or version on stale base_revision_id' do
      page = create_wiki_page(title: 'wiki_history_conflict_page', body: 'first')
      stale_id = page.current_revision.id

      Wiki::Commit.content!(
          page:,
          body: 'second',
          created_user: user,
          message: 'other edit',
          base_revision_id: stale_id)

      page.reload
      current_title = page.title
      current_body = page.body
      revision_count = page.wiki_revisions.count
      version_count = page.wiki_versions.count

      put "/wiki/#{ page.id }",
          params: {
            title: 'wiki_history_conflict_renamed',
            body: 'third',
            message: 'stale edit',
            base_revision_id: stale_id,
          },
          headers: auth_headers(user)

      expect(response).to have_http_status(:conflict)

      page.reload
      expect(page.title).to eq(current_title)
      expect(page.body).to eq(current_body)
      expect(page.wiki_revisions.count).to eq(revision_count)
      expect(page.wiki_versions.count).to eq(version_count)
    end
  end
end
