import gql from "graphql-tag";

function getVisibleLayers(petAppearance, itemAppearances) {
	if (!petAppearance) {
		return [];
	}

	const validItemAppearances = itemAppearances.filter((a) => a);

	const petLayers = petAppearance.layers.map((l) => ({ ...l, source: "pet" }));

	const itemLayers = validItemAppearances
		.map((a) => a.layers)
		.flat()
		.map((l) => ({ ...l, source: "item" }));

	let allLayers = [...petLayers, ...itemLayers];

	const itemRestrictedZoneIds = new Set(
		validItemAppearances
			.map((a) => a.restrictedZones)
			.flat()
			.map((z) => z.id),
	);
	const petRestrictedZoneIds = new Set(
		petAppearance.restrictedZones.map((z) => z.id),
	);

	const visibleLayers = allLayers.filter((layer) => {
		// When an item restricts a zone, it hides pet layers of the same zone.
		// We use this to e.g. make a hat hide a hair ruff.
		//
		// NOTE: Items' restricted layers also affect what items you can wear at
		//       the same time. We don't enforce anything about that here, and
		//       instead assume that the input by this point is valid!
		if (layer.source === "pet" && itemRestrictedZoneIds.has(layer.zone.id)) {
			return false;
		}

		// When a pet appearance restricts a zone, or when the pet is Unconverted,
		// it makes body-specific items incompatible. We use this to disallow UCs
		// from wearing certain body-specific Biology Effects, Statics, etc, while
		// still allowing non-body-specific items in those zones! (I think this
		// happens for some Invisible pet stuff, too?)
		//
		// TODO: We shouldn't be *hiding* these zones, like we do with items; we
		//       should be doing this way earlier, to prevent the item from even
		//       showing up even in search results!
		//
		// NOTE: This can result in both pet layers and items occupying the same
		//       zone, like Static, so long as the item isn't body-specific! That's
		//       correct, and the item layer should be on top! (Here, we implement
		//       it by placing item layers second in the list, and rely on JS sort
		//       stability, and *then* rely on the UI to respect that ordering when
		//       rendering them by depth. Not great! 😅)
		//
		// NOTE: We used to also include the pet appearance's *occupied* zones in
		//       this condition, not just the restricted zones, as a sensible
		//       defensive default, even though we weren't aware of any relevant
		//       items. But now we know that actually the "Bruce Brucey B Mouth"
		//       occupies the real Mouth zone, and still should be visible and
		//       above pet layers! So, we now only check *restricted* zones.
		//
		// NOTE: UCs used to implement their restrictions by listing specific
		//       zones, but it seems that the logic has changed to just be about
		//       UC-ness and body-specific-ness, and not necessarily involve the
		//       set of restricted zones at all. (This matters because e.g. UCs
		//       shouldn't show _any_ part of the Rainy Day Umbrella, but most UCs
		//       don't restrict Right-Hand Item (Zone 49).) Still, I'm keeping the
		//       zone restriction case running too, because I don't think it
		//       _hurts_ anything, and I'm not confident enough in this conclusion.
		//
		// TODO: Do Invisibles follow this new rule like UCs, too? Or do they still
		//       use zone restrictions?
		if (
			layer.source === "item" &&
			layer.bodyId !== "0" &&
			(petAppearance.pose === "UNCONVERTED" ||
				petRestrictedZoneIds.has(layer.zone.id))
		) {
			return false;
		}

		// A pet appearance can also restrict its own zones. The Wraith Uni is an
		// interesting example: it has a horn, but its zone restrictions hide it!
		if (layer.source === "pet" && petRestrictedZoneIds.has(layer.zone.id)) {
			return false;
		}

		return true;
	});
	visibleLayers.sort((a, b) => a.zone.depth - b.zone.depth);

	return visibleLayers;
}

export const itemAppearanceFragmentForGetVisibleLayers = gql`
	fragment ItemAppearanceForGetVisibleLayers on ItemAppearance {
		id
		layers {
			id
			bodyId
			zone {
				id
				depth
			}
		}
		restrictedZones {
			id
		}
	}
`;

export const petAppearanceFragmentForGetVisibleLayers = gql`
	fragment PetAppearanceForGetVisibleLayers on PetAppearance {
		id
		pose
		layers {
			id
			zone {
				id
				depth
			}
		}
		restrictedZones {
			id
		}
	}
`;

export default getVisibleLayers;