<script lang="ts">
	import type { ApiPisObject, ApiTrainsTrainByHeadcode } from '@owlboard/owlboard-ts';
	import { OwlClient, ApiError, ValidationError } from '$lib/owlClient';
	import { slide } from 'svelte/transition';
	import { quintOut } from 'svelte/easing';
	import { formatUkTime, calculateDelay } from '$lib/utils/time';
	import TocStyle from '$lib/components/ui/TocStyle.svelte';
	import TiplocConverter from '$lib/components/ui/TiplocConverter.svelte';
	import { IconChevronDownFilled } from '@tabler/icons-svelte';
	let { service }: { service: ApiTrainsTrainByHeadcode.TrainByHeadcodeResponse } = $props();
	let isExpanded = $state(false);
	let loadingDetails = $state(false);
	let loadingDetailsError = $state(false);
	let loadingDetailsErrorMsg = $state('');
	let details = $state(null);

	const toggleExpand = async (rid: string) => {
		if (isExpanded) {
			isExpanded = false;
			return;
		}

		loadingDetails = true;
		try {
			const result = await OwlClient.trains.getByRid(service.r);
			details = result.data;
			isExpanded = true;
		} catch (e) {
			console.Error('Failed to load train details');
			loadingDetailsError = true;
			loadingDetailsErrorMsg = e.message;
		} finally {
			loadingDetails = false;
		}
	};

	let OriginDepartureSummary = $derived(formatUkTime(service.od));

	async function loadDetails(rid: string) {
		if (details) return;
		loadingDetails = true;

		const result = await OwlClient.trains.getByRid(service.r);
		details = result.data;
		loadingDetails = false;
	}

	const estClass = (act, est) => (!act && est ? 'est' : 'act');

	const activityMap: Record<string, string> = {
		'-D': 'Vehicles detatched',
		'-T': 'Vehicles attached & detached',
		'-U': 'Vehicles attached',
		AE: 'Assist locomotive attached',
		C: 'Traincrew change only',
		D: 'Set down only',
		E: 'Stops for examination',
		K: 'Passenger count point',
		KC: 'Ticket collection point',
		KE: 'Ticket examination point',
		KF: 'First class ticket examination point',
		KS: 'Selective ticket examination point',
		L: 'Locomotive changed',
		N: 'Unadvertised stop',
		OP: 'Operational stop only',
		OR: 'Locomotive attached on rear',
		R: 'Request stop only',
		RM: 'Train changes direction',
		RR: 'Locomotive run round',
		S: 'Staff only stop',
		TW: 'Stops for token/staff/tablet only',
		U: 'Pick up only',
		W: 'Watering coaches',
		X: 'Passes another train'
	};

	const activityRegex = new RegExp(
		Object.keys(activityMap)
			.sort((a, b) => b.length - a.length) // Longest codes first
			.map((key) => key.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')) // Escape special chars
			.join('|'),
		'g'
	);

	const getRelevantActivities = (act: string) => {
		if (!act) return [];

		// Find all matches in the string
		const matches = act.match(activityRegex) || [];

		// Map to labels and remove duplicates
		return [...new Set(matches)].map((code) => activityMap[code]);
	};
</script>

<div class="train-service">
	<button class="summary" onclick={toggleExpand} type="button" aria-expanded={isExpanded}>
		{#if loadingDetails}
			<div class="loading-state"><div class="loading-spinner"></div></div>
		{/if}
		<div class="operator-summary">
			<TocStyle toc={service.o} />
		</div>
		<div class="main-text-summary">
			<div class="time-summary">
				{OriginDepartureSummary}
			</div>
			<div class="location-summary" class:can-all={service.ct}>
				{service.ot}
			</div>
			<div class="location-summary to-summary" class:can-all={service.ct}>to</div>
			<div class="location-summary" class:can-all={service.ct}>
				{service.dt}
			</div>
			<div class="arrow" class:expanded={isExpanded}>
				<IconChevronDownFilled color={'var(--color-title)'} size={25} />
			</div>
		</div>
	</button>
	{#if isExpanded && details}
		<div class="box-ext" transition:slide={{ duration: 800, easing: quintOut }}>
			<!-- Here goes the data formatting! -->
			<div class="detail-head">
				<!-- Cancellation Section -->
				{#if service.ct}
					<span class="cancel-reason"> Cancelled throughout </span>
				{/if}
				{#if details.header.cr}
					<span class="cancel-reason">
						{details.header.cr}
						{#if details.header.cl}
							{details.header.cn ? ' near ' : ' at '}
							<TiplocConverter code={details.header.cl} />
						{/if}
					</span>
				{/if}

				<!-- Delay Section -->
				{#if details.header.dr}
					<span class="delay-reason">
						{details.header.dr}
						{#if details.header.dl}
							{details.header.dn ? ' near ' : ' at '}
							{details.header.dl}
						{/if}
					</span>
				{/if}
				{#if details.pis}
					<div class="pis-detail">
						{details.pis.code}
					</div>
				{/if}
			</div>
			<div class="color-key">
				<p class="tpl-stop">Times in yellow are estimates</p>
			</div>
			<div class="schedule-table-container">
				<table class="schedule-table">
					<thead>
						<tr>
							<th colspan="2"></th>
							<th colspan="2">Arr</th>
							<th colspan="2">Dep</th>
						</tr>
						<tr>
							<th>Location</th>
							<th>Plat</th>
							<th>Sch</th>
							<th>Act</th>
							<th>Sch</th>
							<th>Act</th>
							<th></th>
						</tr>
					</thead>
					{#each details.locations as loc}
					<tbody class="location-group">
						
							<tr class:pass-loc={loc.r === 'PASS'} class:can-loc={loc.can}>
								<td class="tpl-cell" class:tpl-stop={loc.r != 'PASS'}>
									{loc.t}
								</td>
								<td class="plat-cell cell-divider-right" class:plat-change={loc.pc}>{loc.p}</td>
								{#if loc.r == 'PASS'}
									<td class="time-cell cell-divider-right" colspan="2">Pass</td>
									<td class="time-cell">{formatUkTime(loc.wtp)}</td>
									<td class="time-cell {estClass(loc.atp, loc.etp)}"
										>{formatUkTime(loc.atp || loc.etp || '--')}</td
									>
								{:else}
									<td class="time-cell">{formatUkTime(loc.pta || loc.wta || '--')}</td>
									<td class="time-cell cell-divider-right {estClass(loc.ata, loc.eta)}"
										>{formatUkTime(loc.ata || loc.eta || '--')}</td
									>
									<td class="time-cell">{formatUkTime(loc.ptd || loc.wtd || '--')}</td>
									<td class="time-cell cell-divider-right {estClass(loc.atd, loc.etd)}"
										>{formatUkTime(loc.atd || loc.etd || '--')}</td
									>
								{/if}
								{#if loc}
									{@const delay = calculateDelay(loc)}
									<td class="delay-{delay.type}">{delay.val}</td>
								{/if}
							</tr>
							{#if loc.act && getRelevantActivities(loc.act).length > 0}
							<tr class="activity-row">
								<td colspan="7">
									<div class="activity-container">
										{#each getRelevantActivities(loc.act) as note}
										<span class="activity-tag">{note}</span>
										{/each}
									</div>
								</td>
							</tr>
							{/if}
						
					</tbody>{/each}
				</table>
			</div>
		</div>
	{/if}
</div>

<style>
	/* 
		Main container
	*/
	.train-service {
		background-color: var(--color-accent);
		width: 100%;
		max-width: 460px;
		border-radius: 12px;
		box-shadow: var(--shadow-std);
		overflow: visible;
		font-family: 'URW Gothic', sans-serif;
		transition: 0.2s all;
		filter: brightness(1.1);
		-webkit-tap-highlight-color: transparent;
	}

	.train-service:active {
		filter: brightness(1.2);
	}

	@media (hover: hover) {
		.train-service:hover {
			filter: brightness(1.2);
		}
	}

	/* 
		Summary Header
	*/
	.summary {
		position: relative;
		display: flex;
		align-items: center;
		width: 100%;
		padding: 0 1rem;
		min-height: 48px;
		border: none;
		background: transparent;
		cursor: pointer;
		text-align: left;
		gap: 0.5rem;
	}

	.operator-summary {
		flex-shrink: 0;
	}

	.main-text-summary {
		display: flex;
		flex-grow: 1;
		align-items: center;
		gap: 0.5rem;
	}

	.time-summary {
		font-size: 0.75rem;
		font-weight: 700;
		color: var(--color-brand);
	}

	.location-summary {
		text-transform: uppercase;
		font-weight: 500;
		font-size: 0.75rem;
		letter-spacing: 0.02em;
		color: var(--color-title);
	}

	.to-summary {
		font-size: 0.8rem;
		font-style: oblique;
		text-transform: lowercase;
	}

	.arrow {
		padding: 0;
		margin: 0 0 0 auto;
		height: 25px;
		transition: all 0.9s;
	}

	.expanded {
		transform: rotateX(180deg);
	}

	.can-all {
		color: red;
	}

	/*
		Box Extention
	*/
	.box-ext {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		width: 100%;
	}

	.detail-head {
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: 0.5rem;
		text-align: center;
		width: 100%;
	}

	.cancel-reason,
	.delay-reason {
		display: block;
		padding: 4px 8px;
		width: 95%;
		font-size: 1rem;
		font-weight: 500;
		animation: cancel-pulse 2s ease-in-out infinite;
	}

	.cancel-reason {
		color: var(--cancel-red);
	}

	.delay-reason {
		color: rgb(255, 119, 0);
	}

	/*
		Schedule Table
	*/

	.color-key,
	.color-key p {
		text-align: center;
		width: 100%;
		padding: 0 0 0.2rem 0;
		margin: 0;
	}

	.schedule-table-container {
		display: flex;
		justify-content: center;
		align-items: center;
		width: 100%;
		padding-bottom: 0.1rem;
	}

	.schedule-table {
		width: 95%;
		border-collapse: collapse;
		font-family: 'URW Gothic', 'Inter', sans-serif;
		font-variant-numeric: tabular-nums;
		font-size: 0.8rem;
		letter-spacing: -0.02ch;
		transition: all 0.5s;
		font-weight: 500;
	}

	.location-group:nth-of-type(even) {
		background-color: rgba(255, 255, 255, 0.04);
	}
	.cell-divider-right {
		border-right: 1px solid rgba(255, 255, 255, 0.1);
	}

	th,
	td {
		text-align: center;
		padding: 6px 4px;
	}

	.activity-row td {
		padding: 0 0 10px 0;
		text-align: left;
		font-size: 0.75rem;
		letter-spacing: 0.01em;
	}

	.activity-container {
		display: flex;
		flex-wrap: wrap;
		gap: 6px;
		padding: 0 12px;
	}

	.activity-tag {
		background: rgba(255, 255, 255, 0.1);
        border: 1px solid rgba(255, 255, 255, 0.05);
        padding: 2px 6px;
        border-radius: 4px;
	}

	.tpl-cell {
		color: var(--color-title);
		text-align: left;
	}

	.tpl-stop {
		color: var(--location-yellow);
	}

	.plat-change {
		animation: fast-pulse 2s ease-out infinite;
	}

	.pass-loc td {
		color: var(--color-title);
		font-style: oblique;
		opacity: 0.5;
	}

	.can-loc {
		text-decoration: line-through;
	}

	.est {
		color: var(--location-yellow);
		font-style: oblique;
	}

	td.delay-late {
		color: var(--delay-orange);
		font-weight: 600;
		animation: pulse 2s ease-out infinite;
	}

	td.delay-early {
		color: var(--early-blue);
		font-weight: 600;
		animation: pulse 2s ease-out infinite;
	}

	/* 
		Loading State
	*/
	.loading-state {
		position: absolute;
		top: 0;
		left: 0;
		z-index: 2;
		display: flex;
		align-items: center;
		justify-content: center;
		background-color: rgba(0, 0, 0, 0.2);
		backdrop-filter: blur(5px);
		width: 100%;
		height: 100%;
		font-family: 'URW Gothic', sans-serif;
		font-size: 1rem;
		color: var(--color-title);
	}

	.loading-spinner {
		width: 16px;
		height: 16px;
		border: 2px solid rgba(0, 0, 0, 0.1);
		border-top-color: #fff;
		border-radius: 50%;
		animation: load-spin 0.8s linear infinite;
		z-index: 3;
	}

	/*
		Responsivity
	*/
	@media (min-width: 330px) {
		.time-summary,
		.location-summary {
			font-size: 0.9rem;
		}
		.activity-row td {
			font-size: 0.8rem;
		}
	}

	@media (min-width: 340px) {
		.schedule-table {
			font-size: 0.9rem;
		}
	}

	@media (min-width: 360px) {
		.time-summary {
			font-size: 1.1rem;
		}
		.location-summary {
			font-size: 1rem;
		}
		.schedule-table {
			font-size: 0.99rem;
		}
		.activity-row td {
			font-size: 0.9rem;
		}
	}

	@media (min-width: 420px) {
		.schedule-table {
			font-size: 1.1rem;
		}
	}

	/*
		KEYFRAMES
	*/
	@keyframes load-spin {
		to {
			transform: rotate(360deg);
		}
	}
</style>
