require 'digest'
require 'rails_helper'

RSpec.describe Wiki::Commit do
  let(:user) { create_member_user! }

  def create_page title:, body: 'initial body'
    described_class.create_content!(
        tag_name: TagName.create!(name: title),
        body:,
        created_by_user: user,
        message: 'init')
  end

  describe '.create_content!' do
    it 'creates page, revision, and version with normalised body' do
      expect {
        described_class.create_content!(
            tag_name: TagName.create!(name: 'commit_integrity_create'),
            body: "a\r\nb\r\n\r\n",
            created_by_user: user,
            message: 'init')
      }
          .to change(WikiPage, :count).by(1)
          .and change(WikiRevision, :count).by(1)
          .and change(WikiVersion, :count).by(1)

      page = WikiPage.joins(:tag_name).find_by!(tag_names: { name: 'commit_integrity_create' })
      revision = page.current_revision
      version = page.wiki_versions.order(:version_no).last

      expect(page.body).to eq("a\nb")
      expect(revision.lines_count).to eq(2)
      expect(version.body).to eq("a\nb")
      expect(version.reason).to eq('init')
    end

    it 'rejects body that becomes blank after normalisation' do
      tag_name = TagName.create!(name: 'commit_integrity_blank')

      expect {
        described_class.create_content!(
            tag_name:,
            body: "\r\n\r\n",
            created_by_user: user,
            message: 'blank')
      }
          .to raise_error(ActiveRecord::RecordInvalid)

      expect(WikiPage.where(tag_name:)).not_to exist
    end
  end

  describe '.content!' do
    it 'updates page body, revision, and version' do
      page = create_page(title: 'commit_integrity_update', body: 'before')
      current_id = page.current_revision.id

      expect {
        described_class.content!(
            page:,
            body: 'after',
            created_user: user,
            message: 'edit',
            base_revision_id: current_id)
      }
          .to change(WikiRevision, :count).by(1)
          .and change(WikiVersion, :count).by(1)

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

      expect(page.body).to eq('after')
      expect(version.body).to eq('after')
      expect(version.reason).to eq('edit')
    end

    it 'does not record tag_version on body-only wiki update' do
      tag_name = TagName.create!(name: 'commit_integrity_linked_tag')
      tag = Tag.create!(tag_name:, category: :general)

      page =
          described_class.create_content!(
              tag_name:,
              body: 'before',
              created_by_user: user,
              message: 'init')

      TagVersionRecorder.record!(
          tag:,
          event_type: :create,
          created_by_user: user)

      before_count = tag.reload.tag_versions.count

      described_class.content!(
          page:,
          body: 'after',
          created_user: user,
          message: 'edit',
          base_revision_id: page.current_revision.id)

      expect(tag.reload.tag_versions.count).to eq(before_count)
    end

    it 'raises conflict and leaves page, revision, and version unchanged' do
      page = create_page(title: 'commit_integrity_conflict', body: 'first')
      stale_id = page.current_revision.id

      described_class.content!(
          page:,
          body: 'second',
          created_user: user,
          message: 'second',
          base_revision_id: stale_id)

      page.reload
      before_body = page.body
      before_revision_count = page.wiki_revisions.count
      before_version_count = page.wiki_versions.count

      expect {
        described_class.content!(
            page:,
            body: 'third',
            created_user: user,
            message: 'stale',
            base_revision_id: stale_id)
      }
          .to raise_error(Wiki::Commit::Conflict)

      page.reload
      expect(page.body).to eq(before_body)
      expect(page.wiki_revisions.count).to eq(before_revision_count)
      expect(page.wiki_versions.count).to eq(before_version_count)
    end

    it 'deduplicates duplicated missing wiki lines' do
      page = create_page(title: 'commit_integrity_dedup', body: 'before')
      duplicated = 'commit_integrity_duplicate_line'

      described_class.content!(
          page:,
          body: "#{ duplicated }\n#{ duplicated }",
          created_user: user,
          message: 'dedup',
          base_revision_id: page.current_revision.id)

      revision = page.reload.current_revision

      expect(WikiLine.where(body: duplicated).count).to eq(1)
      expect(revision.wiki_revision_lines.count).to eq(2)
      expect(revision.wiki_revision_lines.pluck(:wiki_line_id).uniq.size).to eq(1)
    end
  end

  describe '.redirect!' do
    it 'raises because redirect revisions are deprecated' do
      page = create_page(title: 'commit_integrity_redirect_source', body: 'source')
      target = create_page(title: 'commit_integrity_redirect_target', body: 'target')

      expect {
        described_class.redirect!(
            page:,
            redirect_page: target,
            created_user: user,
            message: 'redirect',
            base_revision_id: page.current_revision.id)
      }
          .to raise_error(RuntimeError, '廃止しました．')
    end
  end
end
