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

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

type FileUploadInputProps = {
    label: string;
    inputId?: string;
    buttonText?: string;
    uploadUrl: string;
    accept?: string;
    previewUrl?: string;
    previewType?: 'image' | 'video' | 'file';
    error?: string;
    onSuccess: (filename: string) => void;
    onError?: () => void;
};

export function FileUploadInput({
    label,
    inputId = 'file-upload',
    buttonText = 'Choose file',
    uploadUrl,
    accept,
    previewUrl = '',
    previewType = 'image',
    error,
    onSuccess,
    onError,
}: FileUploadInputProps) {
    const fileInputRef = useRef<HTMLInputElement>(null);
    const [fileName, setFileName] = useState(buttonText);
    const [preview, setPreview] = useState(previewUrl);
    const [progress, setProgress] = useState(0);
    const [uploading, setUploading] = useState(false);

    useEffect(() => {
        if (previewUrl) {
            if (previewType !== 'file') {
                setPreview(previewUrl);
            }

            const basename = previewUrl.split('/').pop() ?? '';
            setFileName(basename.length > 28 ? `${basename.slice(0, 28)}...` : basename || buttonText);

            return;
        }

        setPreview('');
        setFileName(buttonText);

        if (fileInputRef.current) {
            fileInputRef.current.value = '';
        }
    }, [previewUrl, buttonText, previewType]);

    const resetUploadState = () => {
        setUploading(false);
        setProgress(0);
    };

    const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
        const file = event.target.files?.[0];
        if (!file) {
            return;
        }

        if (previewType === 'image' || previewType === 'video') {
            const reader = new FileReader();
            reader.onload = () => setPreview(String(reader.result));
            reader.readAsDataURL(file);
        } else {
            setPreview('');
        }

        setUploading(true);
        setProgress(0);
        setFileName(file.name);

        adminToast
            .promise(
                new Promise<string>((resolve, reject) => {
                    router.post(
                        uploadUrl,
                        { file },
                        {
                            forceFormData: true,
                            preserveScroll: true,
                            onProgress: (progressEvent) => {
                                setProgress(progressEvent?.percentage ?? 0);
                            },
                            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.'),
                },
            )
            .then((filename) => {
                setFileName(filename.length > 28 ? `${filename.slice(0, 28)}...` : filename);
                onSuccess(filename);
            })
            .catch(() => {
                setFileName(buttonText);
                setPreview(previewType === 'file' ? '' : previewUrl);
                onError?.();
            })
            .finally(() => {
                resetUploadState();
            });
    };

    return (
        <div className={cn(formFieldClassName, 'mb-4')}>
            <label htmlFor={inputId} className={formLabelClassName}>
                {label}
            </label>
            <input
                ref={fileInputRef}
                type="file"
                id={inputId}
                disabled={uploading}
                className={cn(fileInputClassName, error && '!border-danger')}
                accept={accept}
                onChange={handleFileChange}
            />
            {fileName !== buttonText && !uploading && <p className={formHelpClassName}>{fileName}</p>}
            <UploadProgress value={progress} visible={uploading} />
            {error && <span className={formErrorClassName}>{error}</span>}
            {preview && previewType === 'image' && (
                <img src={preview} alt="Preview" className={cn('mt-2 max-h-32', mediaPreviewClassName)} />
            )}
            {preview && previewType === 'video' && (
                <video src={preview} controls className={cn('mt-2 max-h-40', mediaPreviewClassName)} />
            )}
        </div>
    );
}
