import { router } from '@inertiajs/react';
import { useEffect, useRef, useState, type ChangeEvent } from 'react';

import { adminToast } from '@admin/components/ui/admin-toast';
import {
    dangerSoftButtonClassName,
    fileInputClassName,
    formErrorClassName,
    formFieldClassName,
    formHelpClassName,
    formLabelClassName,
    mediaPreviewClassName,
} from '@admin/components/ui/field-styles';
import { firstValidationError, getUploadFlash } from '@admin/lib/inertia-flash';
import { cn } from '@admin/lib/utils';

type MultiFileUploadInputProps = {
    label: string;
    inputId?: string;
    uploadUrl: string;
    paths: string[];
    onChange: (paths: string[]) => void;
    error?: string;
    helpText?: string;
    maxFiles?: number;
};

function uploadOneFile(uploadUrl: string, file: File): Promise<string> {
    return adminToast.promise(
        new Promise<string>((resolve, reject) => {
            router.post(
                uploadUrl,
                { file },
                {
                    forceFormData: true,
                    preserveScroll: true,
                    onSuccess: (page) => {
                        const filename = getUploadFlash(page)?.filename;

                        if (filename) {
                            resolve(filename);
                        } else {
                            reject(new Error('Upload failed — no file path returned.'));
                        }
                    },
                    onError: (errors) => {
                        reject(new Error(firstValidationError(errors) ?? 'Upload failed.'));
                    },
                },
            );
        }),
        {
            loading: 'Uploading...',
            success: 'Upload complete',
            error: (uploadError) => (uploadError instanceof Error ? uploadError.message : 'Upload failed.'),
        },
    );
}

export function MultiFileUploadInput({
    label,
    inputId = 'multi-file-upload',
    uploadUrl,
    paths,
    onChange,
    error,
    helpText,
    maxFiles = 20,
}: MultiFileUploadInputProps) {
    const fileInputRef = useRef<HTMLInputElement>(null);
    const [previews, setPreviews] = useState<string[]>(paths);
    const [uploading, setUploading] = useState(false);

    useEffect(() => {
        setPreviews(paths);

        if (paths.length === 0 && fileInputRef.current) {
            fileInputRef.current.value = '';
        }
    }, [paths]);

    const handleFileChange = async (event: ChangeEvent<HTMLInputElement>) => {
        const files = Array.from(event.target.files ?? []);
        if (files.length === 0) {
            return;
        }

        if (previews.length + files.length > maxFiles) {
            adminToast.error(`Maximum ${maxFiles} images allowed.`);

            return;
        }

        setUploading(true);

        try {
            let nextPaths = [...previews];

            for (const file of files) {
                const filename = await uploadOneFile(uploadUrl, file);
                nextPaths = [...nextPaths, filename];
            }

            setPreviews(nextPaths);
            onChange(nextPaths);
        } catch {
            // Toast handled in uploadOneFile
        } finally {
            setUploading(false);

            if (fileInputRef.current) {
                fileInputRef.current.value = '';
            }
        }
    };

    const removeAt = (index: number) => {
        const next = previews.filter((_, pathIndex) => pathIndex !== index);
        setPreviews(next);
        onChange(next);
    };

    return (
        <div className={cn(formFieldClassName, 'mb-4')}>
            <label htmlFor={inputId} className={formLabelClassName}>
                {label}
            </label>
            {helpText && <p className={formHelpClassName}>{helpText}</p>}
            <input
                ref={fileInputRef}
                type="file"
                id={inputId}
                multiple
                disabled={uploading}
                accept="image/jpeg,image/png,image/jpg,image/webp"
                className={cn(fileInputClassName, error && '!border-danger')}
                onChange={handleFileChange}
            />
            {error && <span className={formErrorClassName}>{error}</span>}

            {previews.length > 0 && (
                <div className="flex flex-wrap gap-2 mt-3">
                    {previews.map((path, index) => (
                        <div key={`${path}-${index}`} className="relative">
                            <img
                                src={`/storage/${path}`}
                                alt=""
                                className={cn('h-20 w-20 object-cover', mediaPreviewClassName)}
                            />
                            <button
                                type="button"
                                className={cn(
                                    dangerSoftButtonClassName,
                                    '!absolute top-1 end-1 !p-1 !min-h-0 leading-none',
                                )}
                                onClick={() => removeAt(index)}
                                aria-label="Remove image"
                            >
                                <i className="ri-close-line text-sm" />
                            </button>
                        </div>
                    ))}
                </div>
            )}
        </div>
    );
}
