'use client';

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

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

interface 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 currentImageUrl = useQuery(
    api.files.getImageUrl,
    user?.image ? { storageId: user.image } : 'skip',
  );

  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={currentImageUrl ?? 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">
                <ImageCropApply />
                <Button
                  onClick={handleReset}
                  size="icon"
                  type="button"
                  variant="ghost"
                >
                  <XIcon className="size-4" />
                </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}
                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"
                className="hover:dark:bg-accent bg-red-400/80 hover:text-red-800/80 dark:bg-red-500/30 hover:dark:text-red-300/60"
                variant="secondary"
              >
                <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>
  );
};
