<script lang="ts">
	import * as course from '$lib/course';
	import * as race from '$lib/race';

	let rawStats = $state({
		[race.Stat.Speed]: 1200,
		[race.Stat.Stamina]: 1200,
		[race.Stat.Power]: 1200,
		[race.Stat.Guts]: 1200,
		[race.Stat.Wit]: 1200,
	});
	let style = $state(race.RunningStyle.FrontRunner);
	let surfaceApt = $state(race.AptitudeLevel.A);
	let distanceApt = $state(race.AptitudeLevel.S);
	let styleApt = $state(race.AptitudeLevel.A);
	let mood = $state(race.Mood.Normal);
	let isCareer = $state(false);
	let skillStatBonus = $state({
		[race.Stat.Speed]: 0,
		[race.Stat.Stamina]: 0,
		[race.Stat.Power]: 0,
		[race.Stat.Guts]: 0,
		[race.Stat.Wit]: 0,
	});

	let raceLen = $state(2000);
	let slopesRaw: course.Slope[] = $state([]);
	let surface = $state(race.Surface.Turf);
	let conditions = $state(race.GroundConditions.Firm);
	let thresh1: race.Stat | undefined = $state();
	let thresh2: race.Stat | undefined = $state();

	const rushedOpts = ['No Rushed', 'Rushed Enabled', 'Rushed Forced'] as const;
	let rushedType: (typeof rushedOpts)[number] = $state('No Rushed');
	let spotStruggleEnabled = $state(false);

	const baseStats = $derived({
		[race.Stat.Speed]: race.baseStat(mood, rawStats[race.Stat.Speed]),
		[race.Stat.Stamina]: race.baseStat(mood, rawStats[race.Stat.Stamina]),
		[race.Stat.Power]: race.baseStat(mood, rawStats[race.Stat.Power]),
		[race.Stat.Guts]: race.baseStat(mood, rawStats[race.Stat.Guts]),
		[race.Stat.Wit]: race.baseStat(mood, rawStats[race.Stat.Wit]),
	});

	const thresholdMod = $derived.by(() => {
		if (thresh1 == null) {
			if (thresh2 == null) {
				return 1;
			}
			return race.thresholdMod(baseStats[thresh2]);
		}
		if (thresh2 == null) {
			return race.thresholdMod(baseStats[thresh1]);
		}
		return race.thresholdMod(baseStats[thresh1], baseStats[thresh2]);
	});

	const adjStats = $derived({
		[race.Stat.Speed]: race.adjustedStat(
			race.Stat.Speed,
			baseStats[race.Stat.Speed],
			isCareer,
			surface,
			conditions,
			thresholdMod,
		),
		[race.Stat.Stamina]: race.adjustedStat(race.Stat.Stamina, baseStats[race.Stat.Stamina], isCareer),
		[race.Stat.Power]: race.adjustedStat(race.Stat.Power, baseStats[race.Stat.Power], isCareer, surface, conditions),
		[race.Stat.Guts]: race.adjustedStat(race.Stat.Guts, baseStats[race.Stat.Guts], isCareer),
		[race.Stat.Wit]: race.adjustedStat(race.Stat.Wit, baseStats[race.Stat.Wit], isCareer, styleApt),
	});

	const stats = $derived({
		[race.Stat.Speed]: race.finalStat(adjStats[race.Stat.Speed], skillStatBonus[race.Stat.Speed]),
		[race.Stat.Stamina]: race.finalStat(adjStats[race.Stat.Stamina], skillStatBonus[race.Stat.Stamina]),
		[race.Stat.Power]: race.finalStat(adjStats[race.Stat.Power], skillStatBonus[race.Stat.Power]),
		[race.Stat.Guts]: race.finalStat(adjStats[race.Stat.Guts], skillStatBonus[race.Stat.Guts]),
		[race.Stat.Wit]: race.finalStat(adjStats[race.Stat.Wit], skillStatBonus[race.Stat.Wit]),
	});

	const distType = $derived(race.distance(raceLen));
	const phaseSpeed = $derived({
		[race.Phase.EarlyRace]: race.sectionSpeed(
			raceLen,
			baseStats[race.Stat.Wit],
			stats[race.Stat.Wit],
			style,
			race.Phase.EarlyRace,
		),
		[race.Phase.MidRace]: race.sectionSpeed(raceLen, baseStats[race.Stat.Wit], stats[race.Stat.Wit], style, race.Phase.MidRace),
		[race.Phase.LateRace]: [
			race.spurtSpeed(stats[race.Stat.Speed], stats[race.Stat.Guts], style, distanceApt, raceLen),
			race.spurtSpeed(stats[race.Stat.Speed], stats[race.Stat.Guts], style, distanceApt, raceLen),
		] as [number, number],
	});

	const isFront = $derived(style === race.RunningStyle.FrontRunner || style === race.RunningStyle.Runaway);
	const spotStruggle = $derived(spotStruggleEnabled && isFront);

	const maxHP = $derived(race.maxHP(raceLen, style, stats[race.Stat.Stamina]));

	const sections = $derived.by(() => {
		const r = course.splitSectionSlopes(raceLen, slopesRaw).map((s) => {
			const uphillMod = s.slope > 0 ? race.uphillMod(stats[race.Stat.Power], s.slope) : 0;
			const speed = phaseSpeed[s.phase].map((v) => v + uphillMod);
			const time = [(s.endDist - s.startDist) / speed[1], (s.endDist - s.startDist) / speed[0]];
			const hp = [
				time[0] * race.hpPerSecond(raceLen, surface, conditions, stats[race.Stat.Guts], s.phase, speed[0]),
				time[1] * race.hpPerSecond(raceLen, surface, conditions, stats[race.Stat.Guts], s.phase, speed[1]),
			];
			return { ...s, speed, time, hp, remain: [0, 0] };
		});
		let remain = [maxHP, maxHP];
		for (const v of r) {
			remain = [remain[0] - v.hp[0], remain[1] - v.hp[1]];
			v.remain = [...remain];
		}
		return r;
	});

	const spurtHPRate = $derived(
		race.hpPerSecond(
			raceLen,
			surface,
			conditions,
			stats[race.Stat.Guts],
			race.Phase.LateRace,
			phaseSpeed[race.Phase.LateRace][0],
		),
	);
	const needHP = $derived(race.fullSpurtHP(raceLen, phaseSpeed[race.Phase.LateRace][0], spurtHPRate));
	const spurtStartHP = $derived(sections.findLast((s) => s.phase !== race.Phase.LateRace)?.remain);

	let slopeAddParams: course.Slope = $state({ per: 1, span: [0, 0] });
	function addSlope() {
		// TODO(zeph): validate no overlap with current slopes
		slopesRaw.push(slopeAddParams);
		slopeAddParams = { per: 1, span: [0, 0] };
	}
	function removeSlope(i: number) {
		if (i < 0 || i >= slopesRaw.length) {
			return;
		}
		slopesRaw.splice(i, 1);
	}

	const phaseNames = {
		[race.Phase.EarlyRace]: 'Early',
		[race.Phase.MidRace]: 'Mid',
		[race.Phase.LateRace]: 'Late',
	} as const;
</script>

<h1 class="text-4xl">Stamina Calculator</h1>
<div class="mx-auto mt-8 grid max-w-5xl grid-cols-1 rounded-md text-center shadow-md ring md:grid-cols-6">
	{#each race.StatList as stat (stat)}
		{@const statName = race.Stat[stat]}
		<div class="m-4">
			<label for={statName}>{statName}</label>
			<input type="number" class="w-full" id={statName} required bind:value={rawStats[stat]} />
		</div>
	{/each}
	<div class="m-4">
		<label for="mood">Mood</label>
		<select class="w-full" id="mood" required bind:value={mood}>
			{#each race.MOODS as mood (mood)}
				<option value={mood}>{race.Mood[mood]}</option>
			{/each}
		</select>
	</div>
	<div class="m-4">
		<label for="style">Style</label>
		<select class="w-full" id="style" required bind:value={style}>
			{#each race.RUNNING_STYLES as [name, style] (style)}
				<option value={style}>{name}</option>
			{/each}
		</select>
	</div>
	<div class="m-4">
		<label for="distanceApt">{race.Distance[distType]} Aptitude</label>
		<select class="w-full" id="distanceApt" required bind:value={distanceApt}>
			{#each race.APTITUDE_LEVELS as apt (apt)}
				<option value={apt}>{race.AptitudeLevel[apt]}</option>
			{/each}
		</select>
	</div>
	<div class="m-4">
		<label for="surfaceApt">{race.Surface[surface]} Aptitude</label>
		<select class="w-full" id="surfaceApt" required bind:value={surfaceApt}>
			{#each race.APTITUDE_LEVELS as apt (apt)}
				<option value={apt}>{race.AptitudeLevel[apt]}</option>
			{/each}
		</select>
	</div>
	<div class="m-4">
		<label for="styleApt"
			>{race.RUNNING_STYLES[style != race.RunningStyle.Runaway ? style : race.RunningStyle.FrontRunner][0]}</label
		>
		<select class="w-full" id="styleApt" required bind:value={styleApt}>
			{#each race.APTITUDE_LEVELS as apt (apt)}
				<option value={apt}>{race.AptitudeLevel[apt]}</option>
			{/each}
		</select>
	</div>
	<div class="m-4 self-center">
		<label for="isCareer" class="mr-1 align-middle">In Career</label>
		<input type="checkbox" id="isCareer" role="switch" bind:checked={isCareer} class="min-h-6 min-w-6 align-middle" />
	</div>
	<div class="col-span-full col-start-1 flex">
		<div class="h-px grow place-self-center border-t"></div>
		<span class="mx-4 flex-none">Skill Passives</span>
		<div class="h-px grow place-self-center border-t"></div>
	</div>
	{#each race.StatList as stat (stat)}
		{@const statName = race.Stat[stat]}
		<div class="m-4">
			<label for={`${statName}Bonus`}>{statName}</label>
			<input type="number" class="w-full" id={`${statName}Bonus`} required bind:value={skillStatBonus[stat]} />
		</div>
	{/each}
</div>
<div class="mx-auto mt-8 grid max-w-5xl grid-cols-1 gap-4 rounded-md text-center shadow-md ring md:grid-cols-6">
	<div class="m-4 flex md:col-span-2">
		<input
			class="col-span-2 my-auto w-full justify-self-center-safe"
			type="range"
			id="raceLen"
			min="1000"
			max="3600"
			step="100"
			bind:value={raceLen}
		/>
		<span class="my-auto pl-2 text-sm">{raceLen}m</span>
	</div>
	<select id="surface" class="m-4" bind:value={surface}>
		<option value={race.Surface.Turf}>Turf</option>
		<option value={race.Surface.Dirt}>Dirt</option>
	</select>
	<select id="conditions" class="m-4" bind:value={conditions}>
		{#each race.GROUND_CONDITONS as cond (cond)}
			<option value={cond}>{race.GroundConditions[cond]}</option>
		{/each}
	</select>
	<div class="m-4">
		<label for="thresh1">Threshold 1</label>
		<select id="thresh1" class="w-full" bind:value={thresh1}>
			<option value={undefined}></option>
			{#each race.StatList as stat (stat)}
				<option value={stat}>{race.Stat[stat]}</option>
			{/each}
		</select>
	</div>
	<div class="m-4">
		<label for="thresh2">Threshold 2</label>
		<select id="thresh2" class="w-full" bind:value={thresh2}>
			<option value={undefined}></option>
			{#each race.StatList as stat (stat)}
				<option value={stat}>{race.Stat[stat]}</option>
			{/each}
		</select>
	</div>
</div>
<div class="mx-auto mt-8 grid max-w-lg grid-cols-2 gap-4 rounded-md p-4 text-center shadow-md ring">
	<select id="rushed" class="w-full" bind:value={rushedType}>
		{#each rushedOpts as opt (opt)}
			<option>{opt}</option>
		{/each}
	</select>
	<div class="place-self-center">
		<label for="spotStruggle">Spot Struggle</label>
		<input type="checkbox" class="min-h-6 min-w-6 align-middle" id="rushed" role="switch" bind:checked={spotStruggleEnabled} />
	</div>
</div>
<div class="mx-auto mt-8 max-w-4xl rounded-md p-4 text-center shadow-md ring">
	<!-- TODO(zeph): this doesn't work for mobile -->
	<table class="w-full table-fixed border-spacing-4">
		<caption>Slopes</caption>
		<thead>
			<tr>
				<th scope="col">Slope</th>
				<th scope="col">Start</th>
				<th scope="col">End</th>
				<th scope="col"><!-- empty --></th>
			</tr>
		</thead>
		<tbody>
			{#each slopesRaw as slope, i (slope.span[0])}
				<tr>
					<td>{slope.per}</td>
					<td>{slope.span[0]}</td>
					<td>{slope.span[1]}</td>
					<td>
						<button
							type="button"
							class="w-24 bg-mist-300 p-2 hover:cursor-pointer dark:bg-mist-900"
							onclick={() => removeSlope(i)}
						>
							Remove
						</button>
					</td>
				</tr>
			{/each}
			<tr>
				<td>
					<select id={`slopeAddPer`} bind:value={slopeAddParams.per}>
						<option value={2}>Uphill +2</option>
						<option value={1.5}>Uphill +1.5</option>
						<option value={1}>Uphill +1</option>
						<option value={-1}>Downhill -1</option>
						<option value={-1.5}>Downhill -1.5</option>
						<option value={-2}>Downhill -2</option>
					</select>
				</td>
				<td>
					<input type="number" id="slopeAddStart" bind:value={slopeAddParams.span[0]} />
				</td>
				<td>
					<input type="number" id="slopeAddEnd" bind:value={slopeAddParams.span[1]} />
				</td>
				<td>
					<button type="button" class="w-24 bg-mist-300 p-2 hover:cursor-pointer dark:bg-mist-900" onclick={addSlope}>
						Add
					</button>
				</td>
			</tr>
		</tbody>
	</table>
</div>

<div class="mx-auto mt-8 flex max-w-3xl">
	<div class="m-2 flex-1 rounded-md border text-center shadow-sm transition-shadow hover:shadow-md">
		<span class="block text-lg">Max HP</span>
		<span class="block text-2xl">{Math.floor(maxHP)}</span>
	</div>
	<div class="m-2 flex-1 rounded-md border text-center shadow-sm transition-shadow hover:shadow-md">
		<span class="block text-lg">Spurt Threshold</span>
		<span class="block text-2xl">{Math.ceil(needHP)}</span>
	</div>
	<div class="m-2 flex-1 rounded-md border text-center shadow-sm transition-shadow hover:shadow-md">
		<span class="block text-lg">Late Race Start</span>
		{#if spurtStartHP != null}
			<span class="block text-2xl">{Math.ceil(spurtStartHP[1])} &ndash; {Math.ceil(spurtStartHP[0])}</span>
		{/if}
	</div>
</div>
<table class="mt-8 w-full border text-center">
	<thead>
		<tr>
			<th scope="colgroup" colspan="4" class="border-x">Section Data</th>
			<th scope="colgroup" colspan={spotStruggle ? 2 : 1} class="border-x">Section State</th>
			<th scope="colgroup" colspan="4" class="border-x">Fastest</th>
			<th scope="colgroup" colspan="4" class="border-x">Slowest</th>
		</tr>
		<tr>
			<th scope="col">Section</th>
			<th scope="col">Phase</th>
			<th scope="col" colspan="2">Span</th>
			<th scope="col">Slope</th>
			<th scope="col" class:hidden={!spotStruggle}>Spot Struggle</th>
			<th scope="col">Speed</th>
			<th scope="col">Time</th>
			<th scope="col">HP</th>
			<th scope="col">Remaining</th>
			<th scope="col">Speed</th>
			<th scope="col">Time</th>
			<th scope="col">HP</th>
			<th scope="col">Remaining</th>
		</tr>
	</thead>
	<tbody>
		{#each sections as s (s.startDist)}
			<tr class="even:bg-mist-300 dark:even:bg-mist-900" class:font-bold={s.phase === race.Phase.LateRace}>
				<td>{s.section}</td>
				<td>{phaseNames[s.phase]}</td>
				<td>{s.startDist.toFixed(1)} m</td>
				<td>{s.endDist.toFixed(1)} m</td>
				<td>{s.slope}</td>
				<td class:hidden={!spotStruggle}></td>
				<td>{s.speed[1].toFixed(3)} m/s</td>
				<td>{s.time[1].toFixed(3)} s</td>
				<td>{s.hp[1].toFixed(1)}</td>
				<td>{s.remain[0].toFixed(1)}</td>
				<td>{s.speed[0].toFixed(3)} m/s</td>
				<td>{s.time[0].toFixed(3)} s</td>
				<td>{s.hp[0].toFixed(1)}</td>
				<td>{s.remain[1].toFixed(1)}</td>
			</tr>
		{/each}
	</tbody>
</table>
