<script lang="ts">
	import { applicationStore, type Application } from '$lib/ApplicationsStore.svelte';
	import * as d3 from 'd3';
	import { goto } from '$app/navigation';
	import type { LineGraphData } from './LineGraph.svelte';
	import LineGraph from './LineGraph.svelte';

	let payRangeDiv: HTMLDivElement | undefined = $state(undefined);

	let sort: 'asc' | 'desc' = $state('desc');

	let width = $state(300);

	let job_level = $state('all');

	const base_payrange = $derived(
		applicationStore.all
			.filter((a) => a.payrange.match(/\d/))
			.map((a) => {
				const payrange = a.payrange
					.replace(/[kK]/g, '000')
					// The first is a - the other is unicode 8211
					.replace(/\.\d+/g, '')
					.replace(/[^\d\-–]/g, '')
					.replace(/–/g, '-')
					.split('-')
					.map((a) => Number(a));

				if (Number.isNaN(payrange[0])) {
					payrange[0] = 0;
				}
				if (Number.isNaN(payrange[1])) {
					payrange[1] = 0;
				}

				return { ...a, payrange };
			})
	);

	const payrangeGraphs: LineGraphData = $derived.by(() => {
		const pre_overtime = base_payrange.reduce(
			(
				acc: Record<number, [number, number]>,
				a: Omit<Application, 'payrange'> & { payrange: number[] }
			) => {
				const c = new Date(a.create_time);
				c.setHours(0);
				c.setMinutes(0);
				c.setSeconds(0);
				c.setMilliseconds(0);
				const ct = c.getTime();

				if (acc[ct]) {
					acc[ct] = [
						acc[ct][0] + 1,
						acc[ct][1] + a.payrange[0] + (a.payrange[1] ?? a.payrange[0])
					];
				} else {
					acc[ct] = [1, a.payrange[0] + (a.payrange[1] ?? a.payrange[0])];
				}
				return acc;
			},
			{} as Record<number | string, [number, number]>
		);

		const keys = Object.keys(pre_overtime);

		keys.sort((a, b) => Number(a) - Number(b));

		return [
			{
				name: 'Payrange avg',
				color: 'green',
				xs: keys.map((a) => Number(a)),
				ys: keys.map((a) => pre_overtime[a][1] / (pre_overtime[a][0] * 2))
			}
		];
	});

	const payranges = $derived.by(() => {
		const obj = base_payrange.reduce(
			(acc, a) => {
				if (job_level !== 'all' && a.job_level !== job_level) {
					return acc;
				}
				if (!acc[a.company]) {
					acc[a.company] = [a];
				} else {
					acc[a.company].push(a);
				}
				return acc;
			},
			{} as Record<string, (Omit<Application, 'payrange'> & { payrange: number[] })[]>
		);

		return Object.keys(obj)
			.reduce(
				(acc, a) => {
					acc.push([a, obj[a]]);
					return acc;
				},
				[] as [string, (Omit<Application, 'payrange'> & { payrange: number[] })[]][]
			)
			.toSorted((a, b) => {
				const rangesA = a[1].reduce(max_and_min_reducer, [
					Number.POSITIVE_INFINITY,
					Number.NEGATIVE_INFINITY
				]);
				const rangesB = b[1].reduce(max_and_min_reducer, [
					Number.POSITIVE_INFINITY,
					Number.NEGATIVE_INFINITY
				]);

				const va = (rangesA[1] + rangesA[0]) / 2;
				const vb = (rangesB[1] + rangesB[0]) / 2;

				if (sort === 'asc') {
					return va - vb;
				} else if (sort === 'desc') {
					return vb - va;
				}
				return 0;
			});
	});

	function max_and_min_reducer(
		acc: [number, number],
		a: Omit<Application, 'payrange'> & { payrange: number[] }
	): [number, number] {
		/*if (a.payrange[0] > 1000000 || a.payrange[1] > 1000000) {
			console.log(a);
		}*/
		return [
			Math.min(acc[0], a.payrange[0]),
			Math.max(acc[1], a.payrange[1] ?? 0, a.payrange[0])
		];
	}

	const scale = $derived.by(() => {
		if (!payRangeDiv) return;

		const max_and_min = Object.values(payranges).reduce(
			(acc, a) => {
				return a[1].reduce(
					(acc2, e) => max_and_min_reducer(acc2, e),
					acc as [number, number]
				);
			},
			[Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY] as [number, number]
		);

		const box = payRangeDiv.getBoundingClientRect();

		const scale = d3
			.scaleLinear()
			.domain([max_and_min[0], max_and_min[1]])
			.range([0, box.width - 40]);

		return scale;
	});

	const context = (() => {
		const canvas = document.createElement('canvas');
		const context = canvas.getContext('2d');
		if (!context) return null;
		context.font = '12px Open sans';
		return context;
	})();

	let open = $state<string | undefined>(undefined);
	let searchPayranges = $state('');
	let expandPayRanges = $state(false);
	let searchPayRangeMode: 'limit' | 'goto' = $state('goto');
	let indexPayRanges: HTMLDivElement[] = $state([]);
	let gotoIndex: number | undefined = $state(undefined);

	//
	// Searching
	//
	$effect(() => {
		if (searchPayRangeMode !== 'goto' || !searchPayranges) {
			gotoIndex = undefined;
			return;
		}
		let i = 0;
		for (let [company] of payranges) {
			if (company.match(new RegExp(searchPayranges, 'i'))) {
				indexPayRanges[i].scrollIntoView({
					behavior: 'smooth',
					inline: 'center',
					block: 'center'
				});
				gotoIndex = i;
				return;
			}
			i += 1;
		}
		gotoIndex = undefined;
	});
</script>

<h1 class="text-black">
	Pay range
	<button
		title="Expand/Contract"
		onclick={() => {
			expandPayRanges = !expandPayRanges;
		}}
	>
		<span class={!expandPayRanges ? 'bi bi-arrows-angle-expand' : 'bi bi-arrows-angle-contract'}
		></span>
	</button>
	<button
		title="Sorting"
		onclick={() => {
			if (sort === 'asc') {
				sort = 'desc';
			} else if (sort === 'desc') {
				sort = 'asc';
			}
		}}
	>
		<span class={sort === 'asc' ? 'bi bi-arrow-down' : 'bi bi-arrow-up'}></span>
	</button>
	<button
		title="Filter mode"
		onclick={() => {
			if (searchPayRangeMode === 'limit') {
				searchPayRangeMode = 'goto';
			} else if (searchPayRangeMode === 'goto') {
				searchPayRangeMode = 'limit';
			}
		}}
	>
		<span class={searchPayRangeMode === 'limit' ? 'bi bi-funnel' : 'bi bi-sort-alpha-down'}
		></span>
	</button>
</h1>
<div class="bg-white {expandPayRanges ? '' : 'min-h-[500px] max-h-[500px]'} overflow-y-auto">
	<div class="sticky top-0 py-2 px-2 bg-white w-full z-50 flex">
		<input class="w-full z-20 flex-grow" bind:value={searchPayranges} placeholder="search" />
		<select
			class="finput flex-shrink"
			style="width: unset;"
			id="job_level"
			bind:value={job_level}
		>
			<option value="all"> All </option>
			<option value="intern"> Intern </option>
			<option value="entry"> Entry </option>
			<option value="junior"> Junior </option>
			<option value="mid"> Mid </option>
			<option value="senior"> Senior </option>
			<option value="staff"> Staff </option>
			<option value="lead"> Lead </option>
		</select>
	</div>
	<div bind:this={payRangeDiv}>
		{#if scale && context}
			{#each searchPayranges && searchPayRangeMode === 'limit' ? payranges.filter( (a) => a[0].match(new RegExp(searchPayranges, 'i')) ) : payranges as v, index}
				{@const company = v[0]}
				{@const values = v[1]}
				{@const ranges = values.reduce(max_and_min_reducer, [
					Number.POSITIVE_INFINITY,
					Number.NEGATIVE_INFINITY
				])}
				{@const nameCompany = company === '' ? 'No Company' : company}
				{#if open !== company}
					<div
						class="relative h-[40px] pointer-cursor {gotoIndex === index
							? 'bg-purple-200/50'
							: index % 2 === 0
								? 'bg-slate-50'
								: ''}"
						role="button"
						onclick={() => (open = company)}
						onkeydown={() => (open = company)}
						bind:this={indexPayRanges[index]}
						tabindex={1}
					>
						<div
							class="bg-blue-500 w-[20px] h-[10px] rounded-full absolute"
							style="left: {10 +
								scale(ranges[0]) +
								10}px; top: 50%; transform: translateY(-50%); width: {scale(
								ranges[1]
							) - scale(ranges[0])}px;"
						></div>
						<div
							class="bg-blue-500 w-[20px] h-[20px] rounded-full absolute"
							title={`pay: ${ranges[0].toLocaleString('en-GB', {
								notation: 'compact',
								currency: 'GBP',
								style: 'currency'
							})}`}
							style="left: {10 +
								scale(ranges[0])}px; top: 50%; transform: translateY(-50%);"
						></div>
						<div
							class="bg-blue-500 w-[20px] h-[20px] rounded-full absolute"
							title={`pay: ${ranges[1].toLocaleString('en-GB', {
								notation: 'compact',
								currency: 'GBP',
								style: 'currency'
							})}`}
							style="left: {10 +
								scale(ranges[1])}px; top: 50%; transform: translateY(-50%);"
						></div>
						{#if context.measureText(nameCompany).width < scale(ranges[1]) - scale(ranges[0]) - 40}
							<div
								class="absolute text-center text-white font-bold pb-1"
								style="left: {10 + scale(ranges[0]) + 10}px;  width: {scale(
									ranges[1]
								) - scale(ranges[0])}px;
                                        top: 50%; transform: translateY(-50%); font-size: 10px; "
							>
								{nameCompany}
							</div>
						{:else}
							<div
								class="absolute text-center font-bold pb-1"
								style="left: {10 + scale(ranges[1] ?? ranges[0]) + 30}px;
                                                top: 50%; transform: translateY(-50%); font-size: 10px; "
							>
								{nameCompany}
							</div>
						{/if}
					</div>
				{:else}
					<div
						class=" p-[10px] inset-2
                                        {gotoIndex === index
							? 'bg-purple-200/50'
							: 'bg-slate-200/50'}

                                        "
						bind:this={indexPayRanges[index]}
					>
						<h2 class="font-bold">
							{nameCompany} (Avg: {((ranges[0] + ranges[1]) / 2).toLocaleString(
								'en-GB',
								{
									notation: 'compact',
									currency: 'GBP',
									style: 'currency'
								}
							)}; Min: {ranges[0].toLocaleString('en-GB', {
								notation: 'compact',
								currency: 'GBP',
								style: 'currency'
							})}; Max: {ranges[1].toLocaleString('en-GB', {
								notation: 'compact',
								currency: 'GBP',
								style: 'currency'
							})})
						</h2>
						{#each values as app}
							<div
								class="relative -mx-[10px] h-[40px]"
								role="button"
								tabindex={1}
								onclick={() => {
									applicationStore.loadItem = app as any;
									goto('/');
								}}
								onkeydown={() => {
									applicationStore.loadItem = app as any;
								}}
							>
								{#if app.payrange[1]}
									<div
										class="bg-blue-500 w-[20px] h-[10px] rounded-full absolute"
										style="left: {10 +
											scale(app.payrange[0]) +
											10}px; top: 50%; transform: translateY(-50%); width: {scale(
											app.payrange[1]
										) - scale(app.payrange[0])}px;"
									></div>
								{/if}
								<div
									class="bg-blue-500 w-[20px] h-[20px] rounded-full absolute"
									title={`pay: ${app.payrange[0].toLocaleString('en-GB', {
										notation: 'compact',
										currency: 'GBP',
										style: 'currency'
									})}`}
									style="left: {10 +
										scale(
											app.payrange[0]
										)}px; top: 50%; transform: translateY(-50%);"
								></div>
								{#if app.payrange[1]}
									<div
										class="bg-blue-500 w-[20px] h-[20px] rounded-full absolute"
										title={`pay: ${app.payrange[1].toLocaleString('en-GB', {
											notation: 'compact',
											currency: 'GBP',
											style: 'currency'
										})}`}
										style="left: {10 +
											scale(
												app.payrange[1]
											)}px; top: 50%; transform: translateY(-50%);"
									></div>
								{/if}
								{#if context.measureText(app.title).width < scale(app.payrange[1]) - scale(app.payrange[0]) - 40}
									<div
										class="absolute text-center text-white font-bold pb-1"
										style="left: {10 +
											scale(app.payrange[0]) +
											10}px;  width: {scale(app.payrange[1]) -
											scale(app.payrange[0])}px;
                                                            top: 50%; transform: translateY(-50%); font-size: 10px; "
									>
										{app.title}
									</div>
								{:else}
									<div
										class="absolute text-center font-bold pb-1"
										style="left: {10 +
											scale(app.payrange[1] ?? app.payrange[0]) +
											30}px;
                                                            top: 50%; transform: translateY(-50%); font-size: 10px; "
									>
										{app.title}
									</div>
								{/if}
							</div>
						{/each}
					</div>
				{/if}
			{/each}
		{/if}
	</div>
</div>
<div bind:clientWidth={width}>
	<LineGraph data={payrangeGraphs} xIsDates width={width - 50} height={300} />
</div>
