require 'rails_helper'

RSpec.describe 'TheatreComments', type: :request do
  def sign_in_as(user)
    allow_any_instance_of(ApplicationController).to receive(:current_user).and_return(user)
  end

  describe 'GET /theatres/:theatre_id/comments' do
    let(:theatre) { create(:theatre) }
    let(:other_theatre) { create(:theatre) }
    let(:alice) { create(:user, name: 'Alice') }
    let(:bob) { create(:user, name: 'Bob') }

    let!(:comment_3) do
      create(
        :theatre_comment,
        theatre: theatre,
        no: 3,
        user: alice,
        content: 'third comment'
      )
    end

    let!(:comment_1) do
      create(
        :theatre_comment,
        theatre: theatre,
        no: 1,
        user: alice,
        content: 'first comment'
      )
    end

    let!(:comment_2) do
      create(
        :theatre_comment,
        theatre: theatre,
        no: 2,
        user: bob,
        content: 'second comment'
      )
    end

    let!(:other_comment) do
      create(
        :theatre_comment,
        theatre: other_theatre,
        no: 1,
        user: bob,
        content: 'other theatre comment'
      )
    end

    it 'theatre_id で絞り込み、no_gt より大きいものを no 降順で返す' do
      get "/theatres/#{theatre.id}/comments", params: { no_gt: 1 }

      expect(response).to have_http_status(:ok)
      expect(response.parsed_body.map { |row| row['no'] }).to eq([3, 2])
      expect(response.parsed_body.map { |row| row['content'] }).to eq([
        'third comment',
        'second comment'
      ])
    end

    it 'user は id と name だけを含む' do
      get "/theatres/#{theatre.id}/comments", params: { no_gt: 1 }

      expect(response).to have_http_status(:ok)

      expect(response.parsed_body.first['user']).to eq({
        'id' => alice.id,
        'name' => 'Alice'
      })
      expect(response.parsed_body.first['user'].keys).to contain_exactly('id', 'name')
    end

    it 'no_gt が負数なら 0 として扱う' do
      get "/theatres/#{theatre.id}/comments", params: { no_gt: -100 }

      expect(response).to have_http_status(:ok)
      expect(response.parsed_body.map { |row| row['no'] }).to eq([3, 2, 1])
    end

    it '削除済みコメントは deleted として返し、本文を隠す' do
      comment_2.discard!

      get "/theatres/#{theatre.id}/comments", params: { no_gt: 1 }

      expect(response).to have_http_status(:ok)

      deleted_comment = response.parsed_body.find { _1['no'] == 2 }
      expect(deleted_comment).to include(
        'deleted' => true,
        'content' => nil
      )

      visible_comment = response.parsed_body.find { _1['no'] == 3 }
      expect(visible_comment).to include(
        'deleted' => false,
        'content' => 'third comment'
      )
    end
  end

  describe 'POST /theatres/:theatre_id/comments' do
    let(:user) { create(:user, name: 'Alice') }
    let(:theatre) { create(:theatre, next_comment_no: 2) }

    before do
      create(
        :theatre_comment,
        theatre: theatre,
        no: 1,
        user: user,
        content: 'existing comment'
      )
    end

    it '未ログインなら 401 を返す' do
      expect {
        post "/theatres/#{theatre.id}/comments", params: { content: 'hello' }
      }.not_to change(TheatreComment, :count)

      expect(response).to have_http_status(:unauthorized)
    end

    it 'content が blank なら 422 を返す' do
      sign_in_as(user)

      expect {
        post "/theatres/#{theatre.id}/comments", params: { content: '   ' }
      }.not_to change(TheatreComment, :count)

      expect(response).to have_http_status(:unprocessable_entity)
    end

    it 'theatre が存在しなければ 404 を返す' do
      sign_in_as(user)

      expect {
        post '/theatres/999999/comments', params: { content: 'hello' }
      }.not_to change(TheatreComment, :count)

      expect(response).to have_http_status(:not_found)
    end

    it 'コメントを作成し、user を紐づけ、next_comment_no を進める' do
      sign_in_as(user)

      expect {
        post "/theatres/#{theatre.id}/comments", params: { content: 'new comment' }
      }.to change(TheatreComment, :count).by(1)

      expect(response).to have_http_status(:created)

      comment = TheatreComment.find_by!(theatre: theatre, no: 2)

      expect(comment.user).to eq(user)
      expect(comment.content).to eq('new comment')
      expect(theatre.reload.next_comment_no).to eq(3)

      expect(response.parsed_body.slice('theatre_id', 'no', 'user_id', 'content')).to eq({
        'theatre_id' => theatre.id,
        'no' => 2,
        'user_id' => user.id,
        'content' => 'new comment'
      })
    end
  end

  describe 'DELETE /theatres/:theatre_id/comments/:id' do
    let(:theatre) { create(:theatre) }
    let(:alice) { create(:user, name: 'Alice') }
    let(:bob) { create(:user, name: 'Bob') }
    let!(:comment) do
      create(
        :theatre_comment,
        theatre: theatre,
        no: 1,
        user: alice,
        content: 'delete target'
      )
    end

    it 'returns 401 when not logged in' do
      delete "/theatres/#{theatre.id}/comments/#{comment.no}"

      expect(response).to have_http_status(:unauthorized)
      expect(comment.reload.discarded?).to eq(false)
    end

    it 'allows the comment owner to delete it' do
      sign_in_as(alice)

      delete "/theatres/#{theatre.id}/comments/#{comment.no}"

      expect(response).to have_http_status(:no_content)
      expect(comment.reload.discarded?).to eq(true)
    end

    it 'returns 403 when another user tries to delete it' do
      sign_in_as(bob)

      delete "/theatres/#{theatre.id}/comments/#{comment.no}"

      expect(response).to have_http_status(:forbidden)
      expect(comment.reload.discarded?).to eq(false)
    end
  end
end
