require 'rails_helper'

RSpec.describe PhantomPlayer, type: :model do
  describe 'associations' do
    it { should belong_to(:crew) }
    it { should belong_to(:claimed_by).class_name('User').optional }
    it { should belong_to(:claimed_from_membership).class_name('CrewMembership').optional }
    it { should have_many(:gw_individual_scores) }
  end

  describe 'validations' do
    it { should validate_presence_of(:name) }
    it { should validate_length_of(:name).is_at_most(100) }
    it { should validate_length_of(:granblue_id).is_at_most(20) }

    describe 'granblue_id uniqueness within crew' do
      let(:crew) { create(:crew) }
      let!(:existing) { create(:phantom_player, crew: crew, granblue_id: '12345678') }

      it 'rejects duplicate granblue_id in same crew' do
        phantom = build(:phantom_player, crew: crew, granblue_id: '12345678')
        expect(phantom).not_to be_valid
        expect(phantom.errors[:granblue_id]).to be_present
      end

      it 'allows same granblue_id in different crew' do
        other_crew = create(:crew)
        phantom = build(:phantom_player, crew: other_crew, granblue_id: '12345678')
        expect(phantom).to be_valid
      end

      it 'allows multiple nil granblue_ids in same crew' do
        phantom = build(:phantom_player, crew: crew, granblue_id: nil)
        expect(phantom).to be_valid
      end
    end

    describe 'claimed_by must be crew member' do
      let(:crew) { create(:crew) }
      let(:phantom) { build(:phantom_player, crew: crew) }

      it 'accepts claimed_by who is a crew member' do
        member = create(:user)
        create(:crew_membership, crew: crew, user: member, role: :member)
        phantom.claimed_by = member
        expect(phantom).to be_valid
      end

      it 'rejects claimed_by who is not a crew member' do
        non_member = create(:user)
        phantom.claimed_by = non_member
        expect(phantom).not_to be_valid
        expect(phantom.errors[:claimed_by]).to include('must be a member of this crew')
      end
    end

    describe 'claim_confirmed requires claimed_by' do
      let(:phantom) { build(:phantom_player, claim_confirmed: true, claimed_by: nil) }

      it 'is invalid' do
        expect(phantom).not_to be_valid
        expect(phantom.errors[:claim_confirmed]).to include('requires a claimed_by user')
      end
    end
  end

  describe 'scopes' do
    let(:crew) { create(:crew) }
    let(:member) { create(:user) }
    let!(:membership) { create(:crew_membership, crew: crew, user: member, role: :member) }
    let!(:unclaimed) { create(:phantom_player, crew: crew) }
    let!(:claimed) { create(:phantom_player, crew: crew, claimed_by: member) }
    let!(:confirmed) { create(:phantom_player, crew: crew, claimed_by: member, claim_confirmed: true) }

    describe '.unclaimed' do
      it 'returns only unclaimed phantoms' do
        expect(PhantomPlayer.unclaimed).to contain_exactly(unclaimed)
      end
    end

    describe '.claimed' do
      it 'returns claimed phantoms (confirmed or not)' do
        expect(PhantomPlayer.claimed).to contain_exactly(claimed, confirmed)
      end
    end

    describe '.pending_confirmation' do
      it 'returns claimed but unconfirmed phantoms' do
        expect(PhantomPlayer.pending_confirmation).to contain_exactly(claimed)
      end
    end
  end

  describe '#assign_to' do
    let(:crew) { create(:crew) }
    let(:phantom) { create(:phantom_player, crew: crew) }
    let(:member) { create(:user) }
    let!(:membership) { create(:crew_membership, crew: crew, user: member, role: :member) }

    it 'assigns the phantom to the user' do
      phantom.assign_to(member)
      expect(phantom.claimed_by).to eq(member)
      expect(phantom.claim_confirmed).to be false
    end

    it 'raises error for non-crew member' do
      non_member = create(:user)
      expect { phantom.assign_to(non_member) }.to raise_error(CrewErrors::MemberNotFoundError)
    end
  end

  describe '#confirm_claim!' do
    let(:crew) { create(:crew) }
    let(:member) { create(:user) }
    let!(:membership) { create(:crew_membership, crew: crew, user: member, role: :member) }
    let(:phantom) { create(:phantom_player, crew: crew, claimed_by: member) }

    it 'confirms the claim' do
      phantom.confirm_claim!(member)
      expect(phantom.claim_confirmed).to be true
    end

    it 'raises error for wrong user' do
      other_user = create(:user)
      create(:crew_membership, crew: crew, user: other_user, role: :member)
      expect { phantom.confirm_claim!(other_user) }.to raise_error(CrewErrors::NotClaimedByUserError)
    end

    context 'with individual scores' do
      let(:gw_event) { create(:gw_event) }
      let(:participation) { create(:crew_gw_participation, crew: crew, gw_event: gw_event) }
      let!(:phantom_score) do
        create(:gw_individual_score,
               crew_gw_participation: participation,
               phantom_player: phantom,
               crew_membership: nil,
               score: 1_000_000)
      end

      it 'transfers scores to the membership' do
        phantom.confirm_claim!(member)
        phantom_score.reload

        expect(phantom_score.crew_membership).to eq(membership)
        expect(phantom_score.phantom_player).to be_nil
      end
    end
  end

  describe '#unassign!' do
    let(:crew) { create(:crew) }
    let(:member) { create(:user) }
    let!(:membership) { create(:crew_membership, crew: crew, user: member, role: :member) }
    let(:phantom) { create(:phantom_player, crew: crew, claimed_by: member) }

    it 'removes the assignment' do
      phantom.unassign!
      expect(phantom.claimed_by).to be_nil
      expect(phantom.claim_confirmed).to be false
    end
  end
end
