<script lang="ts">
	import { ArrowRight, Ban, FileIcon, Folder, Play, TriangleAlert } from 'lucide-svelte';
	import {
		current_height,
		get_selectable_color_classes,
		get_shifted_color
	} from '$lib/ts/stores/ui_behavior';
	import Button from '$lib/components/Button.svelte';
	import {
		supported_file_type_icon,
		type Inode,
		get_file_primary_key,
		type FileTransferTask,
		is_folder
	} from '$lib/ts/types';

	import {
		is_selected,
		select,
		selected_file_ids
	} from '$lib/ts/stores/select';
	import {
		change_file_path,
		current_file_path,
		get_date_mapping,
		get_folder_elements,
		get_missing_colliding_display_ids
	} from '$lib/ts/stores/files';
	import RefreshPlay from '../svgs/RefreshPlay.svelte';
	import { get_file_size_display_string, get_file_type } from '$lib/ts/utils';
	import { open_file } from '$lib/ts/api_handler';
	import {
		run_on_all_selected_displays,
		selected_online_display_ids
	} from '$lib/ts/stores/displays';
	import { get_thumbnail_url } from '$lib/ts/stores/thumbnails';
	import { liveQuery, type Observable } from 'dexie';
	import { add_sync_recursively, file_transfer_tasks } from '$lib/ts/file_transfer_handler';

	let { file, not_interactable = false }: { file: Inode; not_interactable?: boolean } = $props();

	let file_primary_key = $derived(get_file_primary_key(file));

	let missing_colliding_displays_ids:
		| Observable<{ missing: string[]; colliding: string[] }>
		| undefined = $state();
	$effect(() => {
		const f = file;
		const s = $selected_online_display_ids;
		missing_colliding_displays_ids = liveQuery(() => get_missing_colliding_display_ids(f, s));
	});

	let colliding_warning: boolean = $derived(
		!!$missing_colliding_displays_ids && $missing_colliding_displays_ids.colliding.length !== 0
	);

	let file_size: Observable<number> | undefined = $state();
	$effect(() => {
		const f = file;
		file_size = liveQuery(() => get_size_recursively(f));
	});

	let file_transfer_task_list: FileTransferTask[] | null = $derived(
		Object.hasOwn($file_transfer_tasks, file_primary_key)
			? $file_transfer_tasks[file_primary_key]
			: null
	);

	let loading_finished = $state(false);
	let previous_loading_state = $state(false);
	$effect(() => {
		const ftt = file_transfer_task_list;
		if (previous_loading_state && !ftt) {
			loading_finished = true;
			setTimeout(() => (loading_finished = false), 200);
		}
		previous_loading_state = !!ftt;
	});

	let thumbnail_url = liveQuery(() => get_thumbnail_url(file_primary_key));
	let date_mapping: Observable<Record<string, Date>> = liveQuery(() =>
		get_date_mapping(file_primary_key)
	);

	const file_is_folder = $derived(is_folder(file));

	function get_created_info(date_mapping: Record<string, Date> | undefined, full_string = false) {
		if (!date_mapping) return '';
		const keys = Object.keys(date_mapping);
		if (keys.length === 0) return '';

		if (keys.length === 1) return get_formated_created_string(date_mapping[keys[0]], full_string);

		let out = '';
		let is_different = false;
		const first_formated_created_string = get_formated_created_string(
			date_mapping[keys[0]],
			full_string
		);
		out += `${keys[0]}: ${first_formated_created_string}`;

		for (const key of keys.splice(0, 1)) {
			const current_formated_created_string = get_formated_created_string(
				date_mapping[key],
				full_string
			);
			if (!is_different && current_formated_created_string !== first_formated_created_string)
				is_different = true;
			out += `\n${key}: ${current_formated_created_string}`;
		}

		if (full_string) {
			return is_different ? out : first_formated_created_string;
		} else {
			return is_different ? 'versch.' : first_formated_created_string;
		}
	}

	function get_formated_created_string(date_object: Date, full_string: boolean) {
		if (full_string) {
			return (
				get_formated_date_string(date_object, true) + ' ' + get_formated_time_string(date_object)
			);
		} else if (date_object.toDateString() === new Date().toDateString()) {
			return get_formated_time_string(date_object);
		} else {
			return get_formated_date_string(date_object);
		}
	}

	function get_formated_time_string(date_object: Date) {
		return `${date_object.getHours().toString().padStart(2, '0')}:${date_object.getMinutes().toString().padStart(2, '0')}`;
	}

	function get_formated_date_string(date_object: Date, full_year = false) {
		return `${date_object.getDate().toString().padStart(2, '0')}.${(date_object.getMonth() + 1).toString().padStart(2, '0')}.${date_object
			.getFullYear()
			.toString()
			.slice(full_year ? 0 : 2)}`;
	}

	function get_grayed_out_text_color_strings(is_selected: boolean): string {
		if (not_interactable) return 'text-stone-400';
		if (file_transfer_task_list) return 'text-white/20';
		const color = is_selected ? 'text-stone-600' : 'text-stone-400';
		const factor = is_selected ? -1 : 1;
		return `${color} group-hover:${get_shifted_color(color, factor * 100)} group-active:${get_shifted_color(color, factor * 150)}`;
	}

	function get_grayed_out_border_color_strings(is_selected: boolean): string {
		if (not_interactable) return 'border-stone-550';
		if (file_transfer_task_list) return 'border-white/10';
		const color = is_selected ? 'border-stone-450' : 'border-stone-550';
		const factor = is_selected ? 1 : 1;
		return `${color} group-hover:${get_shifted_color(color, factor * 100)} group-active:${get_shifted_color(color, factor * 150)}`;
	}

	function onclick(e: Event) {
		if (not_interactable || file_transfer_task_list) return;
		select(selected_file_ids, file_primary_key, 'toggle');
		e.stopPropagation();
	}

	async function open() {
		if (file_is_folder) {
			await change_file_path($current_file_path + file.name + '/');
		} else if (
			!!$missing_colliding_displays_ids &&
			$missing_colliding_displays_ids.missing.length !== 0
		) {
			await add_sync_recursively(get_file_primary_key(file), $selected_online_display_ids, true);
		} else {
			const path_to_file = $current_file_path + file.name;
			await run_on_all_selected_displays((d) => open_file(d.ip, path_to_file));
		}
	}

	function get_main_classes(): string {
		let out = '';

		if (loading_finished) {
			out += 'bg-stone-500 text-white/30';
		} else if (file_transfer_task_list) {
			out += 'bg-stone-700 text-white/30';
		} else {
			out += get_selectable_color_classes(
				!not_interactable && is_selected(file_primary_key, $selected_file_ids),
				{
					bg: true,
					hover: !not_interactable,
					active: !not_interactable,
					text: true
				}
			);
		}

		if (not_interactable) {
			out += ' rounded-lg';
		} else if (file_transfer_task_list) {
			out += ' rounded-r-lg';
		} else {
			out += ' rounded-r-lg cursor-pointer';
		}

		return out;
	}

	function get_total_percentage(ftt_list: FileTransferTask[]): number {
		let percentage_sum = 0;
		for (const ftt of ftt_list) {
			percentage_sum += get_percentage(ftt);
		}
		return Math.round(percentage_sum / ftt_list.length);
	}


	function get_percentage(ftt: FileTransferTask): number {
		let total_percentage: number;
		if (ftt.data.type === 'upload') {
			total_percentage = ftt.loading_data.percentage;
		} else {
			const percentage_array = ftt.data.destination_display_data.map(
				(dd) => dd.loading_data.percentage
			);
			total_percentage =
				(ftt.loading_data.percentage + percentage_array.reduce((total, n) => total + n, 0)) /
				(1 + percentage_array.length);
		}

		return Math.min(total_percentage, 100);
	}

	async function get_size_recursively(file: Inode): Promise<number> {
		if (is_folder(file)) {
			const folder_elements = await get_folder_elements(
				file.path + file.name + '/',
				$selected_online_display_ids
			);
			let out: number = 0;
			for (const el of folder_elements) {
				out += await get_size_recursively(el);
			}
			return out;
		} else {
			return file.size;
		}
	}
</script>

<div
	data-testid="inode"
	class="flex flex-row h-{$current_height.file} w-full {loading_finished
		? 'scale-105'
		: ''} transition-[scale] duration-300"
>
	{#if !not_interactable}
		<div class="h-{$current_height.file} aspect-square max-w-15 flex">
			<Button
				disabled={(!file_is_folder && get_file_type(file) === null) || colliding_warning}
				title={!file_is_folder && get_file_type(file) === null
					? 'Dateityp nicht unterstützt'
					: colliding_warning
						? 'Dateien kollidieren auf verschiedenen Bildschirmen'
						: ''}
				className="flex rounded-l-lg rounded-r-none {file_is_folder
					? 'text-stone-450'
					: 'text-stone-800'} w-full"
				div_class="w-full"
				bg={colliding_warning
					? 'bg-red-400'
					: get_selectable_color_classes(
							!file_is_folder && get_file_type(file) !== null,
							{
								bg: true
							},
							-50
						)}
				hover_bg={colliding_warning
					? 'bg-red-500'
					: get_selectable_color_classes(
							!file_is_folder,
							{
								bg: true
							},
							50
						)}
				active_bg={colliding_warning
					? 'bg-red-500'
					: get_selectable_color_classes(
							!file_is_folder,
							{
								bg: true
							},
							100
						)}
				click_function={(e) => {
					open();
					e.stopPropagation();
				}}
			>
				{#if file_is_folder}
					<ArrowRight class="size-full" strokeWidth="3" />
				{:else if get_file_type(file) === null}
					<Ban class="size-full" strokeWidth="3" />
				{:else if colliding_warning}
					<TriangleAlert class="size-full" strokeWidth="3" />
				{:else if !!$missing_colliding_displays_ids && $missing_colliding_displays_ids.missing.length !== 0}
					<RefreshPlay className="size-full" />
				{:else}
					<Play class="size-full" strokeWidth="3" />
				{/if}
			</Button>
		</div>
	{/if}
	<div
		role="button"
		tabindex="0"
		onkeydown={(e) => {
			if (e.key === 'Enter' || e.key === ' ') onclick(e);
		}}
		{onclick}
		class="{get_main_classes()} relative transition-colors duration-200 gap-4 flex flex-row justify-between group w-full min-w-0"
	>
		{#if !!file_transfer_task_list}
			<div
				class="absolute pointer-events-none inset-y-0 left-0 transition-[width] duration-400 bg-stone-600 rounded-r-lg"
				style={`width: ${get_total_percentage(file_transfer_task_list)}%;`}
			></div>
		{/if}
		<div class="flex flex-row gap-2 min-w-0 w-full z-10">
			<div class="aspect-square rounded-md flex justify-center items-center">
				{#if file_is_folder}
					<Folder class="size-full p-2" />
				{:else if $thumbnail_url || null}
					<img
						src={$thumbnail_url || null}
						alt="ERR"
						class="object-contain size-full select-none block p-1 rounded-lg text-center content-center text-red-300"
						draggable="false"
					/>
				{:else if supported_file_type_icon[get_file_type(file)?.display_name || '']}
					{@const Icon = supported_file_type_icon[get_file_type(file)?.display_name || '']}
					<Icon class="size-full p-2" />
				{:else}
					<FileIcon class="size-full p-2" />
				{/if}
			</div>
			<div class="content-center truncate select-none w-full" title={file.name}>
				{file.name.includes('.') && !file_is_folder && get_file_type(file)
					? file.name.slice(0, file.name.lastIndexOf('.'))
					: file.name}
			</div>
		</div>
		<div
			class=" p-1 flex flex-row items-center gap-1 pr-1 z-10 {get_grayed_out_text_color_strings(
				is_selected(file_primary_key, $selected_file_ids)
			)} duration-200 transition-colors"
		>
			<div
				class="w-14 content-center text-center select-none text-xs whitespace-nowrap"
				title={get_created_info($date_mapping, true)}
			>
				{get_created_info($date_mapping)}
			</div>
			<div
				class="h-[70%] border {get_grayed_out_border_color_strings(
					is_selected(file_primary_key, $selected_file_ids)
				)} duration-200 transition-colors my-1"
			></div>
			<div
				class="w-12 content-center text-center select-none text-xs whitespace-nowrap truncate"
				title={file.type}
			>
				{file_is_folder ? 'Ordner' : (get_file_type(file)?.display_name ?? '?')}
			</div>
			<div
				class="h-[70%] border {get_grayed_out_border_color_strings(
					is_selected(file_primary_key, $selected_file_ids)
				)} duration-200 transition-colors"
			></div>
			<div
				class="w-12 content-center text-center select-none text-xs whitespace-nowrap"
				title={get_file_size_display_string($file_size ?? -1, 3)}
			>
				{get_file_size_display_string($file_size ?? -1)}
			</div>
		</div>
	</div>
</div>
