import axios from "axios";
import { FileUp, XCircle } from "lucide-react";
import type React from "react";
import { useState } from "react";
import { toast } from "sonner";
import { useRequestPresignedImport } from "@/api/generated/content/content.ts";
import { Button } from "@/components/ui/button.tsx";
import {
	Dialog,
	DialogContent,
	DialogDescription,
	DialogFooter,
	DialogHeader,
	DialogTitle,
} from "@/components/ui/dialog.tsx";
import { Input } from "@/components/ui/input.tsx";
import { Progress } from "@/components/ui/progress.tsx";

interface MangaManualImportDialogProps {
	fileImportDialogOpen: boolean;
	onFileImportDialogOpenChange: (open: boolean) => void;
}

export const MangaManualImportDialog = ({
	fileImportDialogOpen,
	onFileImportDialogOpenChange,
}: MangaManualImportDialogProps) => {
	const [malId, setMalId] = useState("");
	const [aniListId, setAniListId] = useState("");
	const [dragActive, setDragActive] = useState(false);
	const [files, setFiles] = useState<File[] | null>(null);
	const [isUploading, setIsUploading] = useState(false);
	const [uploadProgress, setUploadProgress] = useState<Record<string, number>>(
		{},
	);
	const [uploadErrors, setUploadErrors] = useState<Record<string, boolean>>({});

	const { mutateAsync: requestPresignedUrl } = useRequestPresignedImport();

	const handleFileImport = async () => {
		if (!files || files.length === 0) {
			toast.error("Please select one or more files to upload");
			return;
		}

		if (!malId.trim() && !aniListId.trim()) {
			toast.error("Please enter either an AniList or a MyAnimeList ID");
			return;
		}

		setIsUploading(true);
		setUploadProgress({});
		setUploadErrors({});
		let hasError = false;

		for (const file of files) {
			try {
				const response = await requestPresignedUrl({
					data: {
						malId: malId ? Number(malId) : undefined,
						aniListId: aniListId ? Number(aniListId) : undefined,
						originalFilename: file.name,
					},
				});

				const presignedUrl = response?.data?.presignedUrl;
				if (!presignedUrl) {
					throw new Error("Failed to get presigned URL");
				}

				await axios.put(presignedUrl, file, {
					headers: {
						"Content-Type": "",
					},
					onUploadProgress: (progressEvent) => {
						if (progressEvent.total) {
							const percentCompleted = Math.round(
								(progressEvent.loaded * 100) / progressEvent.total,
							);
							setUploadProgress((prev) => ({
								...prev,
								[file.name]: percentCompleted,
							}));
						}
					},
				});
			} catch (error) {
				console.error(`Error uploading ${file.name}:`, error);
				hasError = true;
				setUploadErrors((prev) => ({ ...prev, [file.name]: true }));
				toast.error(`Failed to import ${file.name}`);
			}
		}

		setIsUploading(false);

		if (!hasError) {
			setFiles(null);
			setMalId("");
			setAniListId("");
			setUploadProgress({});
			setUploadErrors({});
			onFileImportDialogOpenChange(false);
			toast.success("Manga imported successfully! Backend will process it.");
		}
	};

	const handleDrag = (e: React.DragEvent) => {
		e.preventDefault();
		e.stopPropagation();
		if (e.type === "dragenter" || e.type === "dragover") {
			setDragActive(true);
		} else if (e.type === "dragleave") {
			setDragActive(false);
		}
	};

	const handleDrop = (e: React.DragEvent) => {
		e.preventDefault();
		e.stopPropagation();
		setDragActive(false);

		if (e.dataTransfer.files) {
			setFiles(Array.from(e.dataTransfer.files));
		}
	};

	const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
		if (e.target.files) {
			setFiles(Array.from(e.target.files));
		}
	};

	return (
		<Dialog
			open={fileImportDialogOpen}
			onOpenChange={onFileImportDialogOpenChange}
		>
			<DialogContent>
				<DialogHeader>
					<DialogTitle>Import from File</DialogTitle>
					<DialogDescription>
						Upload one or more files and provide the MyAnimeList manga URL (or
						ID) to import manga data.
					</DialogDescription>
				</DialogHeader>
				<div className="space-y-4">
					<div>
						<label className="text-sm font-medium">AniList ID</label>
						<Input
							placeholder="17"
							value={aniListId}
							onChange={(e) => setAniListId(e.target.value)}
							className="mt-2"
							disabled={isUploading}
						/>
					</div>
					<div>
						<label className="text-sm font-medium">MyAnimeList ID</label>
						<Input
							placeholder="20"
							value={malId}
							onChange={(e) => setMalId(e.target.value)}
							className="mt-2"
							disabled={isUploading}
						/>
					</div>
					{!isUploading && (
						<div>
							<label className="text-sm font-medium">Upload File</label>
							<div
								onDragEnter={handleDrag}
								onDragLeave={handleDrag}
								onDragOver={handleDrag}
								onDrop={handleDrop}
								className={`mt-2 rounded-lg border-2 border-dashed p-6 text-center transition-colors ${
									dragActive
										? "border-primary bg-primary/5"
										: "border-muted-foreground/25"
								}`}
							>
								<input
									type="file"
									id="file-input"
									onChange={handleFileSelect}
									className="hidden"
									multiple
									accept=".cbz"
								/>
								<label htmlFor="file-input" className="cursor-pointer">
									<div className="flex flex-col items-center gap-2">
										<FileUp className="h-8 w-8 text-muted-foreground" />
										<div>
											<p className="text-sm font-medium">
												{files
													? files.map((file) => file.name).join(", ")
													: "Drag and drop your files here"}
											</p>
											<p className="text-xs text-muted-foreground">
												or click to select (.CBZ, .CBR)
											</p>
										</div>
									</div>
								</label>
							</div>
						</div>
					)}
					{isUploading && files && files.length > 0 && (
						<div className="space-y-4 rounded-lg bg-muted p-4">
							<div className="space-y-1">
								<div className="flex justify-between text-sm font-medium">
									<span>Overall Progress</span>
									<span>
										{Math.round(
											files.reduce(
												(acc, file) => acc + (uploadProgress[file.name] || 0),
												0,
											) / files.length,
										)}
										%
									</span>
								</div>
								<Progress
									value={
										files.reduce(
											(acc, file) => acc + (uploadProgress[file.name] || 0),
											0,
										) / files.length
									}
									indicatorClassName={
										Math.round(
											files.reduce(
												(acc, file) => acc + (uploadProgress[file.name] || 0),
												0,
											) / files.length,
										) === 100
											? "bg-green-500"
											: ""
									}
								/>
							</div>
							<div className="space-y-3 max-h-60 overflow-y-auto pr-2">
								{files.map((file) => {
									const isError = uploadErrors[file.name];
									const progress = uploadProgress[file.name] || 0;
									return (
										<div key={file.name} className="space-y-1">
											<div className="flex justify-between text-xs text-muted-foreground">
												<span
													className="truncate pr-4 max-w-[200px]"
													title={file.name}
												>
													{file.name}
												</span>
												{isError ? (
													<span className="flex items-center gap-1 font-medium text-destructive">
														<XCircle className="h-3 w-3" /> Failed
													</span>
												) : (
													<span>{progress}%</span>
												)}
											</div>
											<Progress
												value={isError ? 100 : progress}
												indicatorClassName={
													isError
														? "bg-destructive"
														: progress === 100
															? "bg-green-500"
															: ""
												}
											/>
										</div>
									);
								})}
							</div>
						</div>
					)}
				</div>
				<DialogFooter>
					<Button
						disabled={isUploading}
						variant="outline"
						onClick={() => {
							setFiles(null);
							setUploadProgress({});
							setUploadErrors({});
							onFileImportDialogOpenChange(false);
						}}
					>
						Cancel
					</Button>
					<Button disabled={isUploading} onClick={handleFileImport}>
						Import
					</Button>
				</DialogFooter>
			</DialogContent>
		</Dialog>
	);
};
