require "rails_helper"

RSpec.describe "TagChildren", type: :request do
  let!(:parent) { create(:tag) }
  let!(:child)  { create(:tag) }

  # ここは君のUser factoryに合わせて調整
  let(:user) { create_member_user! }
  let(:admin) { create_admin_user! }

  # current_user を ApplicationController でスタブ
  def stub_current_user(user_or_nil)
    allow_any_instance_of(ApplicationController)
      .to receive(:current_user)
      .and_return(user_or_nil)
  end

  describe "POST /tag_children" do
    subject(:do_request) do
      post "/tags/#{ parent_id }/children/#{ child_id }"
    end

    context "when not logged in" do
      let(:parent_id) { parent.id }
      let(:child_id) { child.id }

      it "returns 401" do
        stub_current_user(nil)
        do_request
        expect(response).to have_http_status(:unauthorized)
      end
    end

    context "when logged in but not admin" do
      let(:parent_id) { parent.id }
      let(:child_id) { child.id }

      it "returns 403" do
        stub_current_user(user)
        do_request
        expect(response).to have_http_status(:forbidden)
      end
    end

    context "when admin and params are present" do
      before { stub_current_user(admin) }
      let(:parent_id) { parent.id }
      let(:child_id) { child.id }

      it "returns 204 and adds child to parent.children" do
        expect(parent.children).not_to include(child)

        expect { do_request }
          .to change { parent.reload.children.ids.include?(child.id) }
          .from(false).to(true)

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

      it 'returns 422 and does not create relation when the new link makes a cycle' do
        TagImplication.create!(tag: parent, parent_tag: child)

        expect {
          do_request
        }.not_to change(TagImplication, :count)

        expect(response).to have_http_status(:unprocessable_entity)
        expect(TagImplication.where(tag: child, parent_tag: parent)).not_to exist
      end
    end

    context "when Tag.find raises (invalid ids)" do
      before { stub_current_user(admin) }

      let(:parent_id) { -1 }
      let(:child_id) { -1 }

      it "returns 404" do
        do_request
        expect(response).to have_http_status(:not_found)
      end
    end

    context 'when parent is nico' do
      before { stub_current_user(admin) }

      let!(:parent) { create(:tag, :nico, name: 'nico:parent_ng') }
      let(:parent_id) { parent.id }
      let(:child_id) { child.id }

      it 'returns 400 and does not create relation' do
        expect {
          do_request
        }.not_to change(TagImplication, :count)

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

    context 'when child is nico' do
      before { stub_current_user(admin) }

      let!(:child) { create(:tag, :nico, name: 'nico:child_ng') }
      let(:parent_id) { parent.id }
      let(:child_id) { child.id }

      it 'returns 400 and does not create relation' do
        expect {
          do_request
        }.not_to change(TagImplication, :count)

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

  describe "DELETE /tag_children" do
    subject(:do_request) do
      delete "/tags/#{ parent_id }/children/#{ child_id }"
    end

    context "when not logged in" do
      let(:parent_id) { parent.id }
      let(:child_id) { child.id }

      it "returns 401" do
        stub_current_user(nil)
        do_request
        expect(response).to have_http_status(:unauthorized)
      end
    end

    context "when logged in but not admin" do
      let(:parent_id) { parent.id }
      let(:child_id) { child.id }

      it "returns 403" do
        stub_current_user(user)
        do_request
        expect(response).to have_http_status(:forbidden)
      end
    end

    context "when admin and params are present" do
      before do
        stub_current_user(admin)
        parent.children << child
      end

      let(:parent_id) { parent.id }
      let(:child_id) { child.id }

      it "returns 204 and removes child from parent.children" do
        expect(parent.reload.children).to include(child)

        expect { do_request }
          .to change { parent.reload.children.ids.include?(child.id) }
          .from(true).to(false)

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

      it 'records create and update versions for child tag' do
        expect {
          do_request
        }.to change(TagVersion, :count).by(2)

        expect(response).to have_http_status(:no_content)

        versions = child.reload.tag_versions.order(:version_no)
        expect(versions.map(&:event_type)).to eq(['create', 'update'])
        expect(versions.first.parent_tag_ids.split).to include(parent.id.to_s)
        expect(versions.second.parent_tag_ids).to eq('')
        expect(versions.second.created_by_user_id).to eq(admin.id)
      end
    end

    context "when Tag.find raises (invalid ids)" do
      before { stub_current_user(admin) }

      let(:parent_id) { -1 }
      let(:child_id) { -1 }

      it "returns 404" do
        do_request
        expect(response).to have_http_status(:not_found)
      end
    end

    context 'when parent is nico' do
      before { stub_current_user(admin) }

      let!(:parent) { create(:tag, :nico, name: 'nico:parent_ng_delete') }
      let(:parent_id) { parent.id }
      let(:child_id) { child.id }

      it 'returns 400' do
        do_request
        expect(response).to have_http_status(:bad_request)
      end
    end

    context 'when child is nico' do
      before { stub_current_user(admin) }

      let!(:child) { create(:tag, :nico, name: 'nico:child_ng_delete') }
      let(:parent_id) { parent.id }
      let(:child_id) { child.id }

      it 'returns 400' do
        do_request
        expect(response).to have_http_status(:bad_request)
      end
    end
  end
end
