<script lang="ts">
	import Modal from "../Modal.svelte";
	import { base } from "$app/paths";
	import { tick } from "svelte";

	interface Props {
		open?: boolean;
		acceptMimeTypes?: string[]; // optional client-side validation
		onclose?: () => void;
		onfiles?: (files: File[]) => void;
	}

	let { open = $bindable(false), acceptMimeTypes = [], onclose, onfiles }: Props = $props();

	let urlValue = $state("");
	let loading = $state(false);
	let errorMsg = $state("");
	let inputEl: HTMLInputElement | undefined = $state();

	async function focusInputSoon() {
		// Wait for modal and content to mount, then focus and select
		await tick();
		await tick();
		setTimeout(() => {
			inputEl?.focus();
			inputEl?.select();
		}, 0);
	}

	$effect(() => {
		if (open) {
			// reset state when opening
			urlValue = "";
			errorMsg = "";
			void focusInputSoon();
		}
	});

	function isHttpsUrl(url: string) {
		try {
			const u = new URL(url);
			return u.protocol === "https:";
		} catch {
			return false;
		}
	}

	function matchesAllowed(contentType: string, allowed: string[]): boolean {
		const ct = contentType.split(";")[0]?.trim().toLowerCase();
		if (!ct) return false;
		const [ctType, ctSubtype] = ct.split("/");
		for (const a of allowed) {
			const [aType, aSubtype] = a.toLowerCase().split("/");
			const typeOk = aType === "*" || aType === ctType;
			const subOk = aSubtype === "*" || aSubtype === ctSubtype;
			if (typeOk && subOk) return true;
		}
		return false;
	}

	function close() {
		open = false;
		onclose?.();
	}

	async function handleSubmit() {
		errorMsg = "";
		const trimmed = urlValue.trim();
		if (!isHttpsUrl(trimmed)) {
			errorMsg = "Enter a valid HTTPS URL.";
			return;
		}
		loading = true;
		try {
			// Use server proxy directly for one URL to validate size/types before creating File
			const params = new URLSearchParams({ url: trimmed });
			if (acceptMimeTypes.length > 0) params.set("accept", acceptMimeTypes.join(","));
			const proxyUrl = `${base}/api/fetch-url?${params}`;
			const res = await fetch(proxyUrl);
			if (!res.ok) {
				const txt = await res.text();
				throw new Error(txt || `Failed to fetch (${res.status})`);
			}
			const blob = await res.blob();
			// Optional client-side mime filter (same wildcard semantics as dropzone)
			if (acceptMimeTypes.length > 0 && blob.type && !matchesAllowed(blob.type, acceptMimeTypes)) {
				throw new Error("File type not allowed.");
			}
			const disp = res.headers.get("content-disposition");
			let filename = "attachment";
			const match = disp?.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
			if (match && match[1]) filename = match[1].replace(/['"]/g, "");
			else {
				try {
					const u = new URL(trimmed);
					const last = u.pathname.split("/").pop() || "attachment";
					filename = decodeURIComponent(last);
				} catch {}
			}
			const file = new File([blob], filename, { type: blob.type || "application/octet-stream" });
			onfiles?.([file]);
			close();
		} catch (e) {
			errorMsg = e instanceof Error ? e.message : "Failed to fetch URL";
		} finally {
			loading = false;
		}
	}
</script>

{#if open}
	<Modal onclose={close} width="w-[90dvh] md:w-[480px]">
		{#snippet children()}
			<form
				class="flex w-full flex-col gap-5 p-6"
				onsubmit={(e) => {
					e.preventDefault();
					handleSubmit();
				}}
			>
				<div class="flex items-start justify-between">
					<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">Add from URL</h2>
					<button type="button" class="group" onclick={close} aria-label="Close">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							viewBox="0 0 32 32"
							class="size-5 text-gray-700 group-hover:text-gray-500 dark:text-gray-300 dark:group-hover:text-gray-400"
						>
							<path
								d="M24 9.41 22.59 8 16 14.59 9.41 8 8 9.41 14.59 16 8 22.59 9.41 24 16 17.41 22.59 24 24 22.59 17.41 16 24 9.41z"
								fill="currentColor"
							/>
						</svg>
					</button>
				</div>

				<div class="flex flex-col gap-2">
					<label class="text-sm text-gray-600 dark:text-gray-400" for="fetch-url-input"
						>Enter URL</label
					>
					<input
						id="fetch-url-input"
						bind:this={inputEl}
						bind:value={urlValue}
						type="url"
						placeholder="https://example.com/file.txt"
						class="w-full rounded-xl border border-gray-200 bg-white px-3 py-2 text-[15px] text-gray-800 outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-gray-200 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder:text-gray-500 dark:focus:ring-gray-700"
						aria-invalid={errorMsg ? "true" : "false"}
						onkeydown={(e) => {
							if (e.key === "Enter") {
								e.preventDefault();
								handleSubmit();
							}
						}}
					/>
				</div>

				{#if errorMsg}
					<p class="-mt-1 text-sm text-red-600 dark:text-red-400">{errorMsg}</p>
				{/if}
				<p class="-mt-2 text-xs text-gray-500 dark:text-gray-400">Only HTTPS. Max 10MB.</p>

				<div class="flex items-center justify-end gap-2">
					<button
						type="button"
						class="inline-flex items-center rounded-xl border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-900 shadow hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-700 dark:text-gray-100 dark:hover:bg-gray-600"
						onclick={close}
					>
						Cancel
					</button>
					<button
						type="submit"
						class="inline-flex items-center rounded-xl border border-gray-900 bg-gray-900 px-3 py-1.5 text-sm font-semibold text-white hover:bg-black disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-100 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-white"
						disabled={loading || urlValue.trim() === ""}
					>
						{#if loading}Fetching…{:else}Add{/if}
					</button>
				</div>
			</form>
		{/snippet}
	</Modal>
{/if}

<style lang="postcss">
	:global(input) {
		font-family: inherit;
	}
	/* Uses app-level colors and rounded/blur styles via utility classes */
	/* The Modal itself provides consistent container + scrollbar-custom styling */
</style>
