import { ConfirmDialog } from '@/components/ui/confirm-dialog';
import {
    Dialog,
    DialogContent,
    DialogDescription,
    DialogFooter,
    DialogHeader,
    DialogTitle,
} from '@/components/ui/dialog';
import { cropImageToFile, type PixelCrop } from '@/lib/image-crop';
import { cn } from '@/lib/utils';
import { Camera, ImageIcon, Loader2, Trash2, ZoomIn } from 'lucide-react';
import { useCallback, useEffect, useState } from 'react';
import { useDropzone, type FileRejection } from 'react-dropzone';
import Cropper, { type Area } from 'react-easy-crop';
import { toast } from 'sonner';

// ── Constants ─────────────────────────────────────────────────────────────────

const ACCEPTED_TYPES = {
    'image/jpeg': ['.jpg', '.jpeg'],
    'image/png': ['.png'],
    'image/webp': ['.webp'],
    'image/gif': ['.gif'],
};

/** Generous pre-crop limit — the exported crop is always compressed well below the server's cap. */
const MAX_RAW_BYTES = 8 * 1024 * 1024;

const AVATAR_SIZE_CLASSES: Record<
    'sm' | 'md' | 'lg' | 'xl',
    { box: string; text: string; icon: string }
> = {
    sm: { box: 'h-10 w-10', text: 'text-sm', icon: 'h-3.5 w-3.5' },
    md: { box: 'h-16 w-16', text: 'text-lg', icon: 'h-4 w-4' },
    lg: { box: 'h-24 w-24', text: 'text-2xl', icon: 'h-5 w-5' },
    xl: { box: 'h-32 w-32', text: 'text-3xl', icon: 'h-6 w-6' },
};

const COVER_BOX_CLASS = 'aspect-video w-full max-w-xs';

// ── Helpers ───────────────────────────────────────────────────────────────────

function initialsOf(name: string): string {
    return name
        .split(' ')
        .filter(Boolean)
        .map((w) => w[0])
        .join('')
        .slice(0, 2)
        .toUpperCase();
}

function rejectionMessage(rejections: FileRejection[]): string {
    const code = rejections[0]?.errors[0]?.code;
    if (code === 'file-too-large') {
        return 'Image is too large. Choose a file under 8MB.';
    }
    if (code === 'file-invalid-type') {
        return 'Unsupported file type. Use JPEG, PNG, WebP, or GIF.';
    }
    return 'That file could not be used.';
}

// ── Component ─────────────────────────────────────────────────────────────────

export interface ImageUploaderProps {
    url: string | null;
    name: string;
    size?: 'sm' | 'md' | 'lg' | 'xl';
    /** 'round' shows an avatar-style circular crop with initials fallback; 'rect' shows a 16:9 cover crop with an icon fallback. */
    cropShape?: 'round' | 'rect';
    disabled?: boolean;
    /** Immediate-upload mode: uploads on crop confirm (existing avatar-management screens). */
    onUpload?: (file: File) => Promise<{ avatar_url: string }>;
    onRemove?: () => Promise<void>;
    /** Deferred mode: hands the cropped file back to the parent's form state instead of uploading immediately. */
    onSelect?: (file: File | null) => void;
}

export function ImageUploader({
    url,
    name,
    size = 'lg',
    cropShape = 'round',
    disabled = false,
    onUpload,
    onRemove,
    onSelect,
}: ImageUploaderProps) {
    const deferred = !!onSelect;

    const [currentUrl, setCurrentUrl] = useState(url);
    const [uploading, setUploading] = useState(false);
    const [removing, setRemoving] = useState(false);
    const [confirmRemoveOpen, setConfirmRemoveOpen] = useState(false);

    // Cropper state
    const [selectedSrc, setSelectedSrc] = useState<string | null>(null);
    const [selectedName, setSelectedName] = useState('image.jpg');
    const [crop, setCrop] = useState({ x: 0, y: 0 });
    const [zoom, setZoom] = useState(1);
    const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(
        null,
    );

    useEffect(() => setCurrentUrl(url), [url]);

    const onCropComplete = useCallback((_: Area, pixels: Area) => {
        setCroppedAreaPixels(pixels);
    }, []);

    const closeCropper = useCallback(() => {
        if (selectedSrc) URL.revokeObjectURL(selectedSrc);
        setSelectedSrc(null);
        setCrop({ x: 0, y: 0 });
        setZoom(1);
        setCroppedAreaPixels(null);
    }, [selectedSrc]);

    const { getRootProps, getInputProps, open, isDragActive } = useDropzone({
        accept: ACCEPTED_TYPES,
        maxFiles: 1,
        maxSize: MAX_RAW_BYTES,
        multiple: false,
        noClick: false,
        disabled: disabled || uploading,
        onDropAccepted: ([file]) => {
            setSelectedName(
                file.name.replace(/\.[^.]+$/, '') + '.jpg' || 'image.jpg',
            );
            setSelectedSrc(URL.createObjectURL(file));
        },
        onDropRejected: (rejections) => {
            toast.error(rejectionMessage(rejections));
        },
    });

    const handleSaveCrop = async () => {
        if (!selectedSrc || !croppedAreaPixels) return;

        if (deferred) {
            const file = await cropImageToFile(
                selectedSrc,
                croppedAreaPixels as PixelCrop,
                selectedName,
            );
            setCurrentUrl(URL.createObjectURL(file));
            onSelect!(file);
            closeCropper();
            return;
        }

        setUploading(true);
        try {
            const file = await cropImageToFile(
                selectedSrc,
                croppedAreaPixels as PixelCrop,
                selectedName,
            );
            const res = await onUpload!(file);
            setCurrentUrl(res.avatar_url);
            toast.success('Photo updated');
            closeCropper();
        } catch {
            toast.error('Upload failed. Please try again.');
        } finally {
            setUploading(false);
        }
    };

    const handleRemove = async () => {
        if (deferred) {
            setCurrentUrl(null);
            setConfirmRemoveOpen(false);
            onSelect!(null);
            return;
        }

        setRemoving(true);
        try {
            await onRemove!();
            setCurrentUrl(null);
            setConfirmRemoveOpen(false);
            toast.success('Photo removed');
        } catch {
            toast.error('Could not remove photo. Please try again.');
        } finally {
            setRemoving(false);
        }
    };

    const busy = uploading || removing;
    const isRound = cropShape === 'round';
    const boxShapeClass = isRound ? 'rounded-full' : 'rounded-lg';
    const avatarDims = AVATAR_SIZE_CLASSES[size];
    const boxClass = isRound ? avatarDims.box : COVER_BOX_CLASS;

    return (
        <div
            className={cn(
                'flex flex-col gap-3',
                isRound ? 'items-center' : 'items-start',
            )}
        >
            <div
                {...getRootProps({
                    className: cn(
                        'group relative cursor-pointer outline-none',
                        disabled && 'pointer-events-none opacity-60',
                    ),
                    'aria-label': 'Change image',
                })}
            >
                <input {...getInputProps()} />

                {currentUrl ? (
                    <img
                        src={currentUrl}
                        alt={name}
                        className={cn(
                            boxClass,
                            boxShapeClass,
                            'ring-border object-cover ring-2',
                        )}
                    />
                ) : isRound ? (
                    <div
                        className={cn(
                            boxClass,
                            avatarDims.text,
                            boxShapeClass,
                            'bg-primary/10 text-primary ring-border flex items-center justify-center font-bold ring-2',
                        )}
                    >
                        {initialsOf(name)}
                    </div>
                ) : (
                    <div
                        className={cn(
                            boxClass,
                            boxShapeClass,
                            'bg-muted/40 text-muted-foreground ring-border ring-dashed flex items-center justify-center ring-2',
                        )}
                    >
                        <ImageIcon className="h-6 w-6" />
                    </div>
                )}

                <div
                    className={cn(
                        'absolute inset-0 flex items-center justify-center bg-black/40 transition-opacity',
                        boxShapeClass,
                        isDragActive
                            ? 'opacity-100'
                            : 'opacity-0 group-hover:opacity-100',
                    )}
                >
                    {busy ? (
                        <Loader2
                            className={cn(
                                isRound ? avatarDims.icon : 'h-5 w-5',
                                'animate-spin text-white',
                            )}
                        />
                    ) : (
                        <Camera
                            className={cn(
                                isRound ? avatarDims.icon : 'h-5 w-5',
                                'text-white',
                            )}
                        />
                    )}
                </div>
            </div>

            <div className="flex gap-2">
                <button
                    type="button"
                    onClick={open}
                    disabled={disabled || busy}
                    className="border-border text-foreground hover:bg-muted/40 flex h-7 items-center gap-1.5 rounded-lg border px-3 text-xs font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-60"
                >
                    <Camera className="h-3 w-3" />
                    {currentUrl ? 'Change' : 'Upload'}
                </button>
                {currentUrl && (
                    <button
                        type="button"
                        onClick={() => setConfirmRemoveOpen(true)}
                        disabled={disabled || busy}
                        className="text-muted-foreground hover:text-destructive flex h-7 items-center gap-1.5 rounded-lg px-3 text-xs font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-60"
                    >
                        <Trash2 className="h-3 w-3" />
                        Remove
                    </button>
                )}
            </div>

            {/* ── Crop dialog ── */}
            <Dialog
                open={!!selectedSrc}
                onOpenChange={(open) => !open && !uploading && closeCropper()}
            >
                <DialogContent className="max-w-md">
                    <DialogHeader>
                        <DialogTitle>Adjust your photo</DialogTitle>
                        <DialogDescription>
                            Drag to reposition, use the slider to zoom.
                        </DialogDescription>
                    </DialogHeader>

                    <div className="bg-muted relative h-72 w-full overflow-hidden rounded-lg">
                        {selectedSrc && (
                            <Cropper
                                image={selectedSrc}
                                crop={crop}
                                zoom={zoom}
                                aspect={isRound ? 1 : 16 / 9}
                                cropShape={isRound ? 'round' : 'rect'}
                                showGrid={!isRound}
                                onCropChange={setCrop}
                                onZoomChange={setZoom}
                                onCropComplete={onCropComplete}
                            />
                        )}
                    </div>

                    <div className="flex items-center gap-3">
                        <ZoomIn className="text-muted-foreground h-4 w-4 shrink-0" />
                        <input
                            type="range"
                            min={1}
                            max={3}
                            step={0.01}
                            value={zoom}
                            onChange={(e) => setZoom(Number(e.target.value))}
                            className="accent-primary bg-muted-foreground/20 h-1.5 w-full cursor-pointer appearance-none rounded-full"
                            aria-label="Zoom"
                        />
                    </div>

                    <DialogFooter>
                        <button
                            type="button"
                            onClick={closeCropper}
                            disabled={uploading}
                            className="border-border text-muted-foreground hover:bg-muted/40 rounded-lg border px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50"
                        >
                            Cancel
                        </button>
                        <button
                            type="button"
                            onClick={handleSaveCrop}
                            disabled={uploading || !croppedAreaPixels}
                            className="bg-primary text-primary-foreground flex items-center justify-center gap-1.5 rounded-lg px-4 py-2 text-sm font-semibold transition-colors hover:opacity-90 disabled:opacity-60"
                        >
                            {uploading && (
                                <Loader2 className="h-3.5 w-3.5 animate-spin" />
                            )}
                            {uploading ? 'Saving…' : 'Save photo'}
                        </button>
                    </DialogFooter>
                </DialogContent>
            </Dialog>

            {/* ── Remove confirmation ── */}
            <ConfirmDialog
                open={confirmRemoveOpen}
                onOpenChange={setConfirmRemoveOpen}
                title="Remove photo?"
                description="This photo will be removed immediately. You can upload a new one at any time."
                confirmLabel="Remove"
                loading={removing}
                onConfirm={handleRemove}
            />
        </div>
    );
}
