'use client';

import type { Preloaded } from 'convex/react';
import type { ChangeEvent } from 'react';
import { useRef, useState } from 'react';
import { isRemoteImageUrl } from '@/lib/avatar';
import { useMutation, usePreloadedQuery, useQuery } from 'convex/react';
import { Loader2, Pencil, Upload, XIcon } from 'lucide-react';
import { toast } from 'sonner';

import type { Id } from '@spoon/backend/convex/_generated/dataModel.js';
import { api } from '@spoon/backend/convex/_generated/api.js';
import {
  Avatar,
  AvatarImage,
  BasedAvatar,
  Button,
  CardContent,
  ImageCrop,
  ImageCropApply,
  ImageCropContent,
  Input,
} from '@spoon/ui';

type AvatarUploadProps = {
  preloadedUser: Preloaded<typeof api.auth.getUser>;
};

const dataUrlToBlob = async (
  dataUrl: string,
): Promise<{ blob: Blob; type: string }> => {
  const re = /^data:([^;,]+)[;,]/;
  const m = re.exec(dataUrl);
  const type = m?.[1] ?? 'image/png';

  const res = await fetch(dataUrl);
  const blob = await res.blob();
  return { blob, type };
};

export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
  const user = usePreloadedQuery(preloadedUser);
  const [isUploading, setIsUploading] = useState(false);
  const [selectedFile, setSelectedFile] = useState<File | null>(null);
  const [croppedImage, setCroppedImage] = useState<string | null>(null);

  const inputRef = useRef<HTMLInputElement>(null);

  const generateUploadUrl = useMutation(api.files.generateUploadUrl);
  const updateUser = useMutation(api.auth.updateUser);

  const remoteImageUrl = isRemoteImageUrl(user?.image) ? user?.image : null;
  const currentImageUrl = useQuery(
    api.files.getImageUrl,
    user?.image && !remoteImageUrl ? { storageId: user.image } : 'skip',
  );
  const avatarUrl = remoteImageUrl ?? currentImageUrl;

  const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0] ?? null;
    if (!file) return;
    if (!file.type.startsWith('image/')) {
      toast.error('Please select an image file.');
      if (inputRef.current) inputRef.current.value = '';
      return;
    }
    setSelectedFile(file);
    setCroppedImage(null);
  };

  const handleReset = () => {
    setSelectedFile(null);
    setCroppedImage(null);
    if (inputRef.current) inputRef.current.value = '';
  };

  const handleSave = async () => {
    if (!croppedImage) {
      toast.error('Please apply a crop first.');
      return;
    }
    setIsUploading(true);
    try {
      const { blob, type } = await dataUrlToBlob(croppedImage);
      const postUrl = await generateUploadUrl();

      const result = await fetch(postUrl, {
        method: 'POST',
        headers: { 'Content-Type': type },
        body: blob,
      });
      if (!result.ok) {
        const msg = await result.text().catch(() => 'Upload failed.');
        throw new Error(msg);
      }

      const uploadResponse = (await result.json()) as {
        storageId: Id<'_storage'>;
      };

      await updateUser({ image: uploadResponse.storageId });

      toast.success('Profile picture updated.');
      handleReset();
    } catch (error) {
      console.error('Upload failed:', error);
      toast.error('Upload failed. Please try again.');
    } finally {
      setIsUploading(false);
    }
  };

  return (
    <CardContent>
      <div className='flex flex-col items-center gap-4'>
        {/* Current avatar + trigger (hidden when cropping) */}
        {!selectedFile && (
          <div
            className='group relative cursor-pointer'
            onClick={() => inputRef.current?.click()}
          >
            <BasedAvatar
              src={avatarUrl ?? undefined}
              fullName={user?.name}
              className='h-42 w-42 text-6xl font-semibold'
              userIconProps={{ size: 100 }}
            />
            <div className='absolute inset-0 flex items-center justify-center rounded-full bg-black/0 transition-all group-hover:bg-black/50'>
              <Upload
                className='text-white opacity-0 transition-opacity group-hover:opacity-100'
                size={24}
              />
            </div>
            <div className='absolute inset-1 flex items-end justify-end transition-all'>
              <Pencil
                className='text-white opacity-100 transition-opacity group-hover:opacity-0'
                size={24}
              />
            </div>
          </div>
        )}

        {/* File input (hidden) */}
        <Input
          ref={inputRef}
          id='avatar-upload'
          type='file'
          accept='image/*'
          className='hidden'
          onChange={handleFileChange}
          disabled={isUploading}
        />

        {/* Crop UI */}
        {selectedFile && !croppedImage && (
          <div className='flex flex-col items-center gap-3'>
            <ImageCrop
              aspect={1}
              circularCrop
              file={selectedFile}
              maxImageSize={3 * 1024 * 1024} // 3MB guard
              onCrop={setCroppedImage}
            >
              <ImageCropContent className='max-w-sm' />
              <div className='flex items-center gap-2'>
                <Button size='icon' variant='outline'>
                  <ImageCropApply className='h-full w-full scale-150' />
                </Button>
                <Button onClick={handleReset} size='icon' variant='destructive'>
                  <XIcon className='scale-150' />
                </Button>
              </div>
            </ImageCrop>
          </div>
        )}

        {/* Cropped preview + actions */}
        {croppedImage && (
          <div className='flex flex-col items-center gap-3'>
            <Avatar className='h-42 w-42'>
              <AvatarImage alt='Cropped preview' src={croppedImage} />
            </Avatar>
            <div className='flex items-center gap-1'>
              <Button
                onClick={handleSave}
                disabled={isUploading}
                variant='secondary'
                className='px-4'
              >
                {isUploading ? (
                  <span className='inline-flex items-center gap-2'>
                    <Loader2 className='h-4 w-4 animate-spin' />
                    Saving...
                  </span>
                ) : (
                  'Save Avatar'
                )}
              </Button>
              <Button
                onClick={handleReset}
                size='icon'
                type='button'
                variant='destructive'
              >
                <XIcon className='size-4' />
              </Button>
            </div>
          </div>
        )}

        {/* Uploading indicator */}
        {isUploading && !croppedImage && (
          <div className='mt-2 flex items-center text-sm text-gray-500'>
            <Loader2 className='mr-2 h-4 w-4 animate-spin' />
            Uploading...
          </div>
        )}
      </div>
    </CardContent>
  );
};
