App.EndWeek.masterSuiteReport = function() {
	let frag = document.createDocumentFragment();
	const slaves = App.Utils.sortedEmployees(App.Entity.facilities.masterSuite);
	const msAvg = App.Utils.masterSuiteAverages();

	// find a potential legendary abolitionist
	const abolitionist = slaves.find((s) => (s.devotion > 95) && (s.prestige === 0) && (
		(s.origin === "You sentenced $him to enslavement as a punishment for attempted theft of a slave.") ||
		(s.origin === "$He is an enslaved Daughter of Liberty.") ||
		(s.origin === "You got $him at the Slave Shelter. $He is an enslaved Daughter of Liberty, caught some weeks after the failed coup. $His previous owner used $him as a punching bag and dart board, then when he was bored of $him tattooed obscenities all over $his body and threw $him away.") ||
		(s.origin === "$He is an enslaved member of an anti-slavery extremist group.") ||
		(s.career === "an antislavery activist")
	));
	V.legendaryAbolitionistID = abolitionist ? abolitionist.ID : 0;

	const pregnantSlaves = V.masterSuiteUpgradePregnancy ? slaves.filter((s) => s.pregKnown > 0).length : 0;

	function concubineText() {
		let r = [];
		const {He, he, his, him, himself} = getPronouns(S.Concubine);

		r.push(`Your concubine <span class='slave-name'>${SlaveFullName(S.Concubine)}</span> is serving you in ${V.masterSuiteName}. More than any other slave, ${his} sexual brilliance and physical appeal are <span class="green">critical</span> to your reputation.`);

		if ((S.Concubine.prestigeDesc === "You bankrupted and enslaved $him in revenge for $his part in the attack on your arcology by the Daughters of Liberty.") && (S.Concubine.newGamePlus === 0)) {
			r.push(`${He} was once your rival, and your relationship is widely thought to be <span class="green">the perfect modern romance.</span>`);
			repX(500, "concubine", S.Concubine);
		} else if ((S.Concubine.prestige > 0)) {
			repX(500*S.Concubine.prestige, "concubine", S.Concubine);
			if (V.verboseDescriptions === 1) {
				r.push(`Keeping such a prestigious slave as your concubine <span class="green">adds to your fame.</span>`);
				if (S.Concubine.prestigeDesc === "$He is a famed Free Cities whore, and commands top prices.") {
					r.push(`When ${he} has a free moment, ${he} refines ${his} flexibility so that ${he} is prepared for any sexual position you can think of, and many more that ${he} researched just for your pleasure.`);
				}
				if (S.Concubine.prestigeDesc === "$He is a famed Free Cities slut, and can please anyone.") {
					r.push(`When ${he} has a free moment, ${he} pretties ${himself} up so that ${he} may be as beautiful as possible to make you look even better.`);
				}
				if (S.Concubine.prestigeDesc === "$He is remembered for winning best in show as a cockmilker.") {
					if (S.Concubine.balls > 10 || S.Concubine.prostate > 1) {
						r.push(`${He} does ${his} best to prevent ${himself} from making a mess when you don't want one. When ${he} has free time, ${he} researches new techniques to make use of ${his} huge loads to entertain you.`);
					} else if (S.Concubine.balls > 0) {
						r.push(`When ${he} has a free moment, ${he} researches ways to increase the size of ${his} loads for you; ${he} dreams of the day ${he} show you just what ${he} was capable of.`);
					} else {
						r.push(`${He} wishes ${he} could blow huge loads for you, but without balls, there is little ${he} can do.`);
					}
				}
				if (S.Concubine.prestigeDesc === "$He is remembered for winning best in show as a dairy cow.") {
					if (S.Concubine.lactation > 1 || S.Concubine.lactationAdaptation > 90) {
						r.push(`${He} does ${his} best to prevent ${himself} from making a mess when you don't want one. When ${he} has free time, ${he} researches new techniques to make use of ${his} excessive milk to entertain you.`);
					} else if (S.Concubine.lactation > 0) {
						r.push(`${He} does ${his} best to prevent ${himself} from making a mess when you don't want one. When ${he} has free time, ${he} researches new techniques to make use of ${his} breast milk to entertain you.`);
					} else {
						r.push(`${He} wishes ${he} could lactate for you, but ${he} has dried up since ${his} glory days.`);
					}
				}
				if (S.Concubine.prestigeDesc === "$He is remembered for winning best in show as a breeder.") {
					if (S.Concubine.bellyPreg >= 5000) {
						r.push(`When ${he} has a free moment, ${he} refines ${his} flexibility so that ${he} can perform any sexual position possible for ${him} in ${his} motherly state. ${He} takes the time to look up new positions to work around ${his} growing pregnancy.`);
					} else if (isFertile(S.Concubine) || S.Concubine.preg > 0) {
						r.push(`When ${he} has a free moment, ${he} refines ${his} flexibility so that ${he} is prepared for any sexual position you can think of, no matter how heavy ${he} becomes with children.`);
					} else {
						r.push(`When ${he} has a free moment, ${he} researches ways to restore ${his} fertility; ${he} dreams of the day ${he} can carry `);
						if (V.PC.dick !== 0) {
							r.push(`your children.`);
						} else {
							r.push(`children for you.`);
						}
					}
				}
			}
		}
		if (S.Concubine.porn.prestige > 2) {
			repX(800, "concubine", S.Concubine);
			r.push(`Having a porn star as your personal bed warmer <span class="green">reflects on your standing.</span> Your citizens can only wonder at what kinky things happen behind closed doors.`);
		} else if (S.Concubine.porn.prestige > 1) {
			repX(300, "concubine", S.Concubine);
			r.push(`Having a rising porn star as your personal bed warmer <span class="green">reflects on your standing.</span>`);
		}
		if (S.Concubine.counter.oral + S.Concubine.counter.anal + S.Concubine.counter.vaginal + S.Concubine.counter.mammary + S.Concubine.counter.penetrative > 1000) {
			if (V.verboseDescriptions === 1) {
				r.push(`Many citizens `);
				if (S.Concubine.counter.publicUse > 10) {
					r.push(`remember having had ${him} themselves, and`);
				}
				r.push(`<span class="green">respectfully</span> envy you ${his} exclusive company.`);
			}
			repX(300, "concubine", S.Concubine);
		}
		/* Perform facility based rule changes - FIXME - dev/trust living condition changes probably should be in saRules; there's no text reporting these changes here */
		if (V.masterSuiteUpgradeLuxury > 0) {
			S.Concubine.rules.living = "luxurious";
			S.Concubine.devotion += 4;
			S.Concubine.trust += 4;
		} else {
			S.Concubine.rules.living = "normal";
			S.Concubine.devotion += 2;
			S.Concubine.trust += 2;
		}
		repX(Beauty(S.Concubine) * 5 + (S.Concubine.skill.vaginal || 0) + (S.Concubine.skill.anal || 0) + (S.Concubine.skill.oral || 0) + (S.Concubine.skill.whoring || 0) + (S.Concubine.skill.entertainment || 0), "concubine", S.Concubine);
		return r.join(' ');
	}

	/** Generate text specific to non-concubine MS slaves
	 * @param {App.Entity.SlaveState} slave
	 * @returns {string}
	 */
	function nonConcubineText(slave) {
		let r = [];
		r.push(`<span class='slave-name'>${SlaveFullName(slave)}</span>`);
		if (slave.choosesOwnAssignment === 2) {
			r.push(`${App.SlaveAssignment.choosesOwnJob(slave)}`);
		} else {
			r.push(`sees to your pleasure in ${V.masterSuiteName}.`);
		}
		/* Perform facility based rule changes - FIXME - dev/trust living condition changes probably should be in saRules; there's no text reporting these changes here */
		if (V.masterSuiteUpgradeLuxury === 1) {
			slave.rules.living = "luxurious";
			if (slave.devotion <= 95) {
				slave.devotion += 2;
			}
			if (slave.trust < 60) {
				slave.trust++;
			}
		} else if (V.masterSuiteUpgradeLuxury === 2) {
			slave.rules.living = "luxurious";
			if (slave.energy > 90) {
				if (slave.devotion <= 95) {
					slave.devotion += 2;
				}
				if (slave.trust <= 95) {
					slave.trust += 2;
				}
			} else {
				if (slave.devotion <= 60) {
					slave.devotion++;
				}
				if (slave.trust < 60) {
					slave.trust++;
				}
			}
		} else {
			slave.rules.living = "spare";
			if ((slave.devotion <= 20) && (slave.trust >= -20)) {
				slave.devotion -= 2;
				slave.trust -= 5;
			} else if ((slave.devotion <= 60)) {
				slave.devotion += 2;
			} else if ((slave.devotion > 60)) {
				slave.devotion -= 2;
			}
			if (slave.trust < 60) {
				slave.trust++;
			}
		}
		// decoration dev bonus (reported at end)
		if (V.masterSuiteDecoration !== "standard") {
			slave.devotion++;
		}
		return r.join(' ');
	}

	/** Generate text shared by concubine and non-concubine MS slaves
	 * @param {App.Entity.SlaveState} slave
	 * @returns {DocumentFragment}
	 */
	function commonText(slave) {
		const smallFrag = document.createDocumentFragment();
		let r = [];
		const {He, he, His, his, him, himself} = getPronouns(slave);

		if (V.masterSuiteUpgradeLuxury === 2 && msAvg.energy > random(40, 90)) {
			const energy = (Math.ceil(slave.energy / 5) * 7);
			SimpleSexAct.Slave(slave, energy);
			if (canPenetrate(slave)) {
				actX(slave, "penetrative", random(1, 3) * energy);
			}
			if (slave.nipples === "fuckable" && msAvg.dick > 2) {
				actX(slave, "mammary", random(1, 3) * energy);
			}
			if ((slave.trust >= -20) && (slave.devotion > -10) && (slave.fetishStrength <= 95) && slave.fetish !== "mindbroken") {
				const fetishChange = fetishChangeChance(slave);
				if ((msAvg.milk > 2000) && (fetishChange > random(0, 50))) {
					if (slave.fetish === "boobs") {
						if (V.verboseDescriptions === 1) {
							r.push(`${His}`);
						} else {
							r.push(`${slave.slaveName}'s`);
						}
						r.push(`<span class="lightcoral">boob fetish is strengthened</span> by the constant availability of milky nipples for ${him} to play with.`);
						slave.fetishStrength += 4;
					} else {
						if (V.verboseDescriptions === 1) {
							r.push(`${He}`);
						} else {
							r.push(slave.slaveName);
						}
						r.push(`<span class="lightcoral">acquires a boob fetish</span> after spending a lot of time in the fuckpit drinking from ${his} fellow fucktoys' tits.`);
						slave.fetish = "boobs";
						slave.fetishStrength = 65;
					}
				} else if ((msAvg.dick > 3) && (slave.anus > 0) && (fetishChange > random(0, 50))) {
					r.push(`${He}`);
					if (slave.fetish === "buttslut") {
						r.push(`<span class="lightcoral">sinks farther into anal pleasure,</span> since ${he} spends ${his} time in the fuckpit with at least one of the many available cocks up ${his} butt.`);
						slave.fetishStrength += 4;
					} else {
						r.push(`<span class="lightcoral">acquires an anal fetish</span> after helplessly orgasming at the mercy of your many fucktoys eager to shove their big stiff penises up ${his} ass.`);
						slave.fetish = "buttslut";
						slave.fetishStrength = 65;
					}
				} else if ((msAvg.cum > 3) && (fetishChange > random(0, 50))) {
					r.push(`${He}`);
					if (slave.fetish === "cumslut") {
						r.push(`<span class="lightcoral">sinks farther into cum addiction,</span> since ${he} spends ${his} time in the fuckpit eagerly sucking down ejaculate, straight from the many sources.`);
						slave.fetishStrength += 4;
					} else {
						r.push(`<span class="lightcoral">acquires an oral fixation</span> after orally servicing your many fucktoys eager to blow their loads down ${his} throat.`);
						slave.fetish = "cumslut";
						slave.fetishStrength = 65;
					}
				} else if ((msAvg.preg > 10) && (fetishChange > random(0, 50))) {
					r.push(`${He}`);
					if (slave.fetish === "pregnancy") {
						r.push(`<span class="lightcoral">sinks farther into pregnancy obsession,</span> since ${he} never wants for a pregnant girl to make love to.`);
						slave.fetishStrength += 4;
					} else {
						r.push(`<span class="lightcoral">acquires a pregnancy fetish,</span> since many of ${his} sexual partners in the fuckpit are heavily pregnant.`);
						slave.fetish = "pregnancy";
						slave.fetishStrength = 65;
					}
				} else if ((msAvg.dom < -25) && (fetishChange > random(0, 50))) {
					r.push(`${He}`);
					if (slave.fetish === "dom") {
						r.push(`<span class="lightcoral">becomes more dominant,</span> since there are so many subs in the fuckpit who beg ${him} to fuck them hard.`);
						slave.fetishStrength += 4;
					} else {
						r.push(`<span class="lightcoral">becomes sexually dominant</span> after having fun satisfying the many submissive fucktoys in the fuckpit who beg ${him} to top them.`);
						slave.fetish = "dom";
						slave.fetishStrength = 65;
					}
				} else if ((msAvg.dom > 25) && (fetishChange > random(0, 50))) {
					r.push(`${He}`);
					if (slave.fetish === "submissive") {
						r.push(`<span class="lightcoral">becomes even more submissive,</span> since there are so many doms in the fuckpit that ${he}'s often used by more than one at once.`);
						slave.fetishStrength += 4;
					} else {
						r.push(`<span class="lightcoral">becomes sexually submissive</span> after the many dominant slaves in the fuckpit hold ${him} down and use ${him} for their pleasure.`);
						slave.fetish = "submissive";
						slave.fetishStrength = 65;
					}
				} else if ((msAvg.sadism < -25) && (fetishChange > random(0, 50))) {
					r.push(`${He}`);
					if (slave.fetish === "sadist") {
						r.push(`<span class="lightcoral">becomes more sadistic,</span> since there are so many sluts in the fuckpit who will do anything for ${him} if ${he}'ll only spank them.`);
						slave.fetishStrength += 4;
					} else {
						r.push(`<span class="lightcoral">becomes a bit of a sadist</span> after ${he} notices that so many of ${his} fellow fucktoys cum harder if ${he} gives them a good slap.`);
						slave.fetish = "sadist";
						slave.fetishStrength = 65;
					}
				} else if ((msAvg.sadism > 25) && (fetishChange > random(0, 50))) {
					if (slave.fetish === "masochist") {
						if (V.verboseDescriptions === 1) {
							r.push(`${His}`);
						} else {
							r.push(`${slave.slaveName}'s`);
						}
						r.push(`<span class="lightcoral">pain addiction deepens,</span> since the fuckpit is full of ladies happy to fuck ${him} while ${he} screams.`);
						slave.fetishStrength += 4;
					} else {
						if (V.verboseDescriptions === 1) {
							r.push(`${He}`);
						} else {
							r.push(slave.slaveName);
						}
						r.push(`<span class="lightcoral">learns masochism</span> after experiencing many orgasms under the exquisite sexual torture of the sadists in the fuckpit.`);
						slave.fetish = "masochist";
						slave.fetishStrength = 65;
					}
				}
			}
			// FIXME: fuckpit means servant milkers get no milk at all??
			if (slave.lactation > 0) {
				slave.lactationDuration = 2;
				slave.boobs -= slave.boobsMilk;
				slave.boobsMilk = 0;
			}
			if (slave.energy > 40 && slave.energy < 95) {
				r.push(`Being a constant part of the fuckpit orgy <span class="green">greatly heightens ${his} libido.</span>`);
				slave.energy += 2;
			}
			slave.need -= 50;
		}

		if (V.masterSuiteUpgradePregnancy === 1) {
			/* If they're not on fertility drugs and the toggle is active, stick them on (if they can take them). Otherwise take them off. */
			if ((V.masterSuitePregnancyFertilityDrugs === 1) && (slave.drugs !== "fertility drugs") && canGetPregnant(slave)) {
				slave.drugs = "fertility drugs";
			} else if ((V.masterSuiteHyperPregnancy === 1) && (slave.drugs !== "super fertility drugs") && canGetPregnant(slave)) {
				slave.drugs = "super fertility drugs";
			} else if ((((V.masterSuitePregnancyFertilityDrugs === 0) && (slave.drugs === "fertility drugs")) || ((V.masterSuiteHyperPregnancy === 0) && (slave.drugs === "super fertility drugs"))) || (!canGetPregnant(slave) && ["fertility drugs", "super fertility drugs"].includes(slave.drugs))) {
				slave.drugs = "no drugs";
			}
			/* We don't know they're pregnant for a month or so by game logic */
			if (slave.pregKnown === 1) {
				/* FIXME: should these effects have associated text? */
				/* If they're preggo and in the upgraded suite, give them extra devotion. More if they're being given lighter duties. */
				if (slave.devotion <= 100) {
					if (V.masterSuitePregnancySlaveLuxuries === 0) {
						slave.devotion += 2;
					} else {
						slave.devotion += 5;
					}
				}
				/* If they're preggo and in the upgraded suite, give them extra trust. More if they're being given lighter duties. */
				if (slave.trust <= 100) {
					if (V.masterSuitePregnancySlaveLuxuries === 0) {
						slave.trust += 2;
					} else {
						slave.trust += 5;
					}
				}
				/* If they're preggo and in the upgraded suite, give them extra health. More if they're being given lighter duties. */
				if (slave.health.condition < 100) {
					if (V.masterSuitePregnancySlaveLuxuries === 0) {
						improveCondition(slave, 15);
					} else {
						improveCondition(slave, 25);
					}
				}
			}
		}

		$(smallFrag).append(r.join(' '));

		if (V.verboseDescriptions === 1) {
			const msContent = App.UI.DOM.appendNewElement("div", smallFrag, '', "indent");
			$(msContent).append(`${He} ${App.SlaveAssignment.pleaseYou(slave)}`);

			if (V.servantMilkers === 1 && slave.lactation > 0 && slave.fuckdoll === 0 && slave.fetish !== "mindbroken" && canMove(slave) && slave.intelligence+slave.intelligenceImplant >= -90) {
				const milkingResults = App.SlaveAssignment.getMilked(slave, 0.25);
				const milkDiv = App.UI.DOM.appendNewElement("div", smallFrag, `When ${his} breasts begin to feel full and you aren't around, ${he} avails ${himself} to the penthouse milkers and gives ${milkingResults.milk} liters of milk over the week, which is sold for `, "indent");
				App.UI.DOM.appendNewElement("span", milkDiv, `${cashFormat(milkingResults.milkSale)}.`, ["cash", "inc"]);
			}
			const commonContent = App.UI.DOM.appendNewElement("div", smallFrag, '', "indent");
			commonContent.append(App.SlaveAssignment.standardSlaveReport(slave, false));
		} else {
			// discard return values silently
			App.SlaveAssignment.choosesOwnJob(slave);
			App.SlaveAssignment.pleaseYou(slave);
			if (V.servantMilkers === 1 && slave.lactation > 0 && slave.fuckdoll === 0 && slave.fetish !== "mindbroken" && canMove(slave) && slave.intelligence+slave.intelligenceImplant >= -90) {
				App.SlaveAssignment.getMilked(slave, 0.25);
			}
			App.SlaveAssignment.standardSlaveReport(slave, true);
		}
		if (slave.health.condition < 80) {
			if (V.masterSuiteUpgradeLuxury === 1) {
				improveCondition(slave, 20);
			} else {
				improveCondition(slave, 10);
			}
		}

		return smallFrag;
	}

	if (slaves.length > 0) {
		const intro = App.UI.DOM.appendNewElement("p", frag, '', "indent");
		let r = [];
		if (slaves.length > 1) {
			r.push(`<strong>There are ${slaves.length} slaves`);
		} else {
			r.push(`<strong>There is one slave`);
		}
		r.push(`seeing to your pleasure in the master suite.</strong> Such sexual opulence <span class="green">improves</span> your reputation.`);

		if ((V.arcologies[0].FSEgyptianRevivalist > 0) && (slaves.length >= 5)) {
			r.push(`Society <span class="green">approves</span> of your keeping a large number of women. This advances the Egyptian revivalist ideal of multiple concubinage.`);
			FutureSocieties.Change("EgyptianRevivalist", 2);
		}
		if (pregnantSlaves >= 1) {
			r.push(`The suite is supporting the pregnancies of the slaves`);
			if (V.masterSuitePregnancyFertilityDrugs === 1 || V.masterSuiteHyperPregnancy === 1) {
				r.push(`within, and is providing them with fertility drugs`);
				if (V.masterSuitePregnancyFertilitySupplements === 1) {
					r.push(`and supplements to encourage impregnation.`);
				} else {
					r.push(`to encourage impregnation.`);
				}
			} else {
				r.push(`within.`);
			}
			if (V.arcologies[0].FSHedonisticDecadence > 0) {
				r.push(`Society <span class="green">approves</span> of the pampering your pregnant harem receives. This advances the ideal that everyone's desires should be fulfilled.`);
				FutureSocieties.Change("Hedonistic", 1);
			}
		}
		if (slaves.length > 1) {
			r.push(`The level of sexual energy in the suite is`);
			if (V.masterSuiteUpgradeLuxury === 1) {
				if (msAvg.energy > 90) {
					r.push(`intense. When you enter your luxurious retreat, you can set off pent-up orgasms with a touch.`);
				} else if (msAvg.energy > 60) {
					r.push(`high; there is strong competition for your favors whenever you enter your retreat.`);
				} else {
					r.push(`relatively normal; the slaves serve your pleasure before their own.`);
				}
			} else if (V.masterSuiteUpgradeLuxury === 2) {
				if (msAvg.energy > 90) {
					r.push(`intense. Your slaves spend almost all their time in the fuckpit, having sex in a big pile.`);
				} else if (msAvg.energy > 60) {
					r.push(`high; your slaves spend much of their time in the fuckpit, having sex with each other.`);
				} else {
					r.push(`relatively normal; the slaves lounging in the fuckpit get each other off when they feel like it.`);
				}
				if (msAvg.cum > 4) {
					r.push(`Unless it's right after an automated cleaning, everything in the fuckpit is spattered with cum.`);
				}
				if (msAvg.milk > 4000) {
					r.push(`There's so much lactation going on that the lowest level of the fuckpit is a pool of milk.`);
				}
			} else {
				r.push(`relatively normal.`);
			}
		}
		$(intro).append(r.join(' '));
	}

	if (S.Concubine) {
		tired(S.Concubine);
		const slaveEntry = App.UI.DOM.appendNewElement("div", frag, '', "slave-report");
		App.SlaveAssignment.appendSlaveArt(slaveEntry, S.Concubine);
		slaveEntry.append(App.EndWeek.favoriteIcon(S.Concubine), " ");
		$(slaveEntry).append(concubineText(), commonText(S.Concubine));
	}

	for (const slave of slaves) {
		const slaveEntry = App.UI.DOM.appendNewElement("div", frag, '', "slave-report");
		App.SlaveAssignment.appendSlaveArt(slaveEntry, slave);
		slaveEntry.append(App.EndWeek.favoriteIcon(slave), " ");
		$(slaveEntry).append(nonConcubineText(slave), commonText(slave));
	}

	if (pregnantSlaves > 0 && V.arcologies[0].FSRestart !== "unset" && V.propOutcome !== 1 && V.eugenicsFullControl !== 1) {
		const eliteEffects = App.UI.DOM.appendNewElement("p", frag, '', "indent");
		$(eliteEffects).append(`The Societal Elite know what you are doing with your bedslaves. <span class="red">They do not approve.</span>`);
		V.failedElite += (5 * pregnantSlaves);
	}

	if (V.masterSuiteDecoration !== "standard") {
		const decorationEffects = App.UI.DOM.appendNewElement("p", frag, '', "indent");
		$(decorationEffects).append(`${capFirstChar(V.masterSuiteName)}'s ${V.masterSuiteDecoration} atmosphere <span class="hotpink">has a minor impact on your fucktoys.</span>`);
	}

	return frag;
};