require_relative '../rails_helper'

RSpec.describe PetState do
	fixtures :colors, :species, :zones

	let(:blue) { colors(:blue) }
	let(:green) { colors(:green) }
	let(:red) { colors(:red) }
	let(:acara) { species(:acara) }

	describe ".next_unlabeled_appearance" do
		before { PetType.destroy_all }

		def create_sa
			swf_asset = SwfAsset.create!(
				type: "biology", remote_id: (SwfAsset.maximum(:remote_id) || 0) + 1,
				url: "https://images.neopets.example/hello.swf",
				zone: zones(:body), zones_restrict: [], body_id: 0)
		end

		def create_pt(color, species, created_at = nil)
			PetType.create! color:, species:, created_at:,
				body_id: (PetType.maximum(:body_id) || 0) + 1
		end

		def create_ps(pet_type, pose, created_at = nil, **options)
			# HACK: PetStates without any assets don't save correctly.
			# https://github.com/rails/rails/issues/52340
			swf_assets = [create_sa]
			PetState.create! pet_type:, pose:, created_at:, swf_assets:,
				swf_asset_ids: swf_assets.map(&:id), **options
		end

		it "returns nil where there are no pet states" do
			expect(PetState.next_unlabeled_appearance).to be_nil
		end

		it "returns nil where there are only labeled pet states" do
			pt = PetType.create! color: blue, species: acara, body_id: 1
			ps = create_ps(pt, "HAPPY_MASC").tap(&:save!)
			expect(PetState.next_unlabeled_appearance).to be_nil
		end

		it "returns the only pet state when it is unlabeled" do
			pt = PetType.create! color: blue, species: acara, body_id: 1
			ps = create_ps(pt, "UNKNOWN").tap(&:save!)
			expect(PetState.next_unlabeled_appearance).to eq ps
		end

		describe "with multiple unlabeled pet states" do
			before do
				# Create three pet types, with ascending order of creation date.
				@pt1 = create_pt blue,  acara, Date.new(2000)
				@pt2 = create_pt green, acara, Date.new(2005)
				@pt3 = create_pt red,   acara, Date.new(2010)

				# Give each a pet state, but created in a different order.
				@ps1 = create_ps @pt1, "UNKNOWN", Date.new(2020)
				@ps2 = create_ps @pt2, "UNKNOWN", Date.new(2025)
				@ps3 = create_ps @pt3, "UNKNOWN", Date.new(2015)
			end

			it "returns the latest pet type's pet state" do
				expect(PetState.next_unlabeled_appearance).to eq @ps3
			end

			it "excludes fully-labeled pet types" do
				# Label the latest pet state, then see it move to the next.
				@ps3.update!(pose: "HAPPY_FEM")
				expect(PetState.next_unlabeled_appearance).to eq @ps2
			end

			it "excludes labeled pet states" do
				# Create an older pet state on the latest pet type, than label the
				# latest pet state, and see it move back to the older one.
				ps3_a = create_ps @pt3, "UNKNOWN", Date.new(2014)
				@ps3.update!(pose: "HAPPY_FEM")
				expect(PetState.next_unlabeled_appearance).to eq ps3_a
			end

			it "sorts pet states within the latest pet type by newest" do
				# Create a few pet types on the latest pet type, and see that we get
				# the latest back.
				ps3_a = create_ps @pt3, "UNKNOWN", Date.new(2016)
				ps3_b = create_ps @pt3, "UNKNOWN", Date.new(2017)
				ps3_c = create_ps @pt3, "UNKNOWN", Date.new(2018)
				ps3_d = create_ps @pt3, "UNKNOWN", Date.new(2019)
				expect(PetState.next_unlabeled_appearance).to eq ps3_d
			end

			it "can find the next after the latest pet state" do
				expect(PetState.next_unlabeled_appearance(after_id: @ps3.id)).to eq @ps2
			end

			it "can find the next after any given pet state" do
				expect(PetState.next_unlabeled_appearance(after_id: @ps2.id)).to eq @ps1
			end

			it "can find the next after the latest pet state, even within the same pet type" do
				ps3_a = create_ps @pt3, "UNKNOWN", Date.new(2014)
				expect(PetState.next_unlabeled_appearance(after_id: @ps3.id)).to eq ps3_a
			end
		end
	end
end