import { Download, Loader2, Upload } from "lucide-react";
import { useId, useRef, useState } from "react";
import { useAdapters } from "../contexts/adapter-context.js";
import { useBestiaryContext } from "../contexts/bestiary-context.js";
import { useRulesEditionContext } from "../contexts/rules-edition-context.js";
import { Button } from "./ui/button.js";
import { Input } from "./ui/input.js";

interface SourceFetchPromptProps {
	sourceCode: string;
	onSourceLoaded: (skippedNames: string[]) => void;
}

export function SourceFetchPrompt({
	sourceCode,
	onSourceLoaded,
}: Readonly<SourceFetchPromptProps>) {
	const { bestiaryIndex, pf2eBestiaryIndex } = useAdapters();
	const { fetchAndCacheSource, uploadAndCacheSource } = useBestiaryContext();
	const { edition } = useRulesEditionContext();
	const indexPort = edition === "pf2e" ? pf2eBestiaryIndex : bestiaryIndex;
	const sourceDisplayName = indexPort.getSourceDisplayName(sourceCode);
	const [url, setUrl] = useState(() =>
		indexPort.getDefaultFetchUrl(sourceCode),
	);
	const [status, setStatus] = useState<"idle" | "fetching" | "error">("idle");
	const [error, setError] = useState<string>("");
	const fileInputRef = useRef<HTMLInputElement>(null);
	const sourceUrlId = useId();

	const handleFetch = async () => {
		setStatus("fetching");
		setError("");
		try {
			const { skippedNames } = await fetchAndCacheSource(sourceCode, url);
			setStatus("idle");
			onSourceLoaded(skippedNames);
		} catch (e) {
			setStatus("error");
			setError(e instanceof Error ? e.message : "Failed to fetch source data");
		}
	};

	const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
		const file = e.target.files?.[0];
		if (!file) return;

		setStatus("fetching");
		setError("");

		try {
			const text = await file.text();
			const json = JSON.parse(text);
			await uploadAndCacheSource(sourceCode, json);
			onSourceLoaded([]);
		} catch (err) {
			setStatus("error");
			setError(
				err instanceof Error ? err.message : "Failed to process uploaded file",
			);
		}

		// Reset file input
		if (fileInputRef.current) {
			fileInputRef.current.value = "";
		}
	};

	return (
		<div className="flex flex-col gap-4">
			<div>
				<h3 className="font-semibold text-foreground text-sm">
					Load {sourceDisplayName}
				</h3>
				<p className="mt-1 text-muted-foreground text-xs">
					Stat block data for this source needs to be loaded. Enter a URL or
					upload a JSON file.
				</p>
			</div>

			<div className="flex flex-col gap-2">
				<label htmlFor={sourceUrlId} className="text-muted-foreground text-xs">
					Source URL
				</label>
				<Input
					id={sourceUrlId}
					type="url"
					value={url}
					onChange={(e) => setUrl(e.target.value)}
					disabled={status === "fetching"}
					className="text-xs"
				/>
			</div>

			<div className="flex items-center gap-2">
				<Button onClick={handleFetch} disabled={status === "fetching" || !url}>
					{status === "fetching" ? (
						<Loader2 className="mr-1 h-3 w-3 animate-spin" />
					) : (
						<Download className="mr-1 h-3 w-3" />
					)}
					{status === "fetching" ? "Loading..." : "Load"}
				</Button>

				<span className="text-muted-foreground text-xs">or</span>

				<Button
					variant="outline"
					onClick={() => fileInputRef.current?.click()}
					disabled={status === "fetching"}
				>
					<Upload className="mr-1 h-3 w-3" />
					Upload file
				</Button>
				<input
					ref={fileInputRef}
					type="file"
					accept=".json"
					className="hidden"
					onChange={handleFileUpload}
				/>
			</div>

			{status === "error" && (
				<div className="rounded-md border border-destructive/50 bg-destructive/10 px-3 py-2 text-destructive text-xs">
					{error}
				</div>
			)}
		</div>
	);
}
