"use client";

import { useRef, useState } from "react";
import { toast } from "sonner";
import { Camera, Loader2, Trash2 } from "lucide-react";

import { Button } from "@/components/ui/button";
import { Card, CardContent, CardFooter } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  removeAvatar as removeAvatarAction,
  updateProfileNames,
  uploadAvatar as uploadAvatarAction,
} from "@/lib/actions/profile";
import type { ProfileDTO } from "@/types/profile";

// Kept in sync with lib/actions/profile.ts (the server re-checks both).
const MAX_AVATAR_BYTES = 10 * 1024 * 1024;
const ACCEPTED_AVATAR_TYPES = [
  "image/jpeg",
  "image/png",
  "image/webp",
  "image/gif",
];

export function ProfileForm({ initial }: { initial: ProfileDTO }) {
  const [firstName, setFirstName] = useState(initial.firstName ?? "");
  const [lastName, setLastName] = useState(initial.lastName ?? "");
  // What's currently saved in the DB -- the "Save" button is only enabled
  // while the inputs differ from this.
  const [savedNames, setSavedNames] = useState({
    firstName: initial.firstName ?? "",
    lastName: initial.lastName ?? "",
  });
  const [avatar, setAvatar] = useState<string | null>(initial.avatar);
  const [savingNames, setSavingNames] = useState(false);
  const [uploading, setUploading] = useState(false);
  const fileInputRef = useRef<HTMLInputElement>(null);

  const namesDirty =
    firstName.trim() !== savedNames.firstName ||
    lastName.trim() !== savedNames.lastName;

  async function handleSaveNames() {
    setSavingNames(true);
    try {
      await updateProfileNames(firstName, lastName);
      setSavedNames({ firstName, lastName });
      toast.success("Name saved.");
    } catch {
      toast.error("Couldn't save your name. Try again.");
    } finally {
      setSavingNames(false);
    }
  }

  async function handleFileChosen(event: React.ChangeEvent<HTMLInputElement>) {
    const file = event.target.files?.[0];
    event.target.value = ""; // allow re-picking the same file
    if (!file) return;

    // Same rules as the server action -- check early so a bad file never
    // leaves the browser.
    if (!ACCEPTED_AVATAR_TYPES.includes(file.type)) {
      toast.error("Please choose a JPEG, PNG, WebP, or GIF image.");
      return;
    }
    if (file.size > MAX_AVATAR_BYTES) {
      toast.error("Image must be 10 MB or smaller.");
      return;
    }

    setUploading(true);
    try {
      const result = await uploadAvatarAction(file);
      if (result.error) {
        toast.error(result.error);
        return;
      }
      setAvatar(result.avatar ?? null);
      toast.success("Profile photo updated.");
    } catch (error) {
      // A thrown (rather than returned) error is a framework-level
      // rejection -- typically Next.js' server-action body limit 413ing
      // the file before it reaches the action. Say so instead of the
      // generic "try another image", which is misleading here.
      if (error instanceof Error && /body exceeded|413/i.test(error.message)) {
        toast.error(
          "Image is too large for the server to accept. Try one under 10 MB."
        );
      } else {
        toast.error("Couldn't upload that photo. Try again.");
      }
    } finally {
      setUploading(false);
    }
  }

  async function handleRemoveAvatar() {
    setUploading(true);
    try {
      await removeAvatarAction();
      setAvatar(null);
      toast.success("Profile photo removed.");
    } catch {
      toast.error("Couldn't remove the photo. Try again.");
    } finally {
      setUploading(false);
    }
  }

  const initials = (firstName.trim()[0] ?? lastName.trim()[0] ?? "?").toUpperCase();

  return (
    <Card className="max-w-xl">
      <CardContent className="space-y-5">
        {/* Photo */}
        <div className="flex items-center gap-4">
          {avatar ? (
            // eslint-disable-next-line @next/next/no-img-element -- data-URL avatar, no image-optimization pipeline in this self-hosted app
            <img
              src={avatar}
              alt="Profile"
              className="size-16 shrink-0 rounded-full object-cover"
            />
          ) : (
            <span className="flex size-16 shrink-0 items-center justify-center rounded-full bg-primary/10 text-xl font-bold text-primary">
              {initials}
            </span>
          )}
          <div className="flex flex-wrap items-center gap-2">
            <input
              ref={fileInputRef}
              type="file"
              accept={ACCEPTED_AVATAR_TYPES.join(",")}
              className="hidden"
              onChange={handleFileChosen}
              disabled={uploading}
            />
            <Button
              variant="outline"
              size="sm"
              onClick={() => fileInputRef.current?.click()}
              disabled={uploading}
            >
              {uploading ? (
                <Loader2 className="size-3.5 animate-spin" />
              ) : (
                <Camera className="size-3.5" />
              )}
              {uploading ? "Updating…" : avatar ? "Change photo" : "Add photo"}
            </Button>
            {avatar && (
              <Button
                variant="ghost"
                size="sm"
                onClick={handleRemoveAvatar}
                disabled={uploading}
              >
                <Trash2 className="size-3.5" />
                Remove
              </Button>
            )}
          </div>
        </div>

        {/* Name */}
        <div className="grid gap-4 sm:grid-cols-2">
          <div className="space-y-2">
            <Label htmlFor="profile-first-name">First name</Label>
            <Input
              id="profile-first-name"
              value={firstName}
              maxLength={60}
              autoComplete="given-name"
              placeholder="Alex"
              onChange={(e) => setFirstName(e.target.value)}
              onKeyDown={(e) => e.key === "Enter" && namesDirty && !savingNames && handleSaveNames()}
            />
          </div>
          <div className="space-y-2">
            <Label htmlFor="profile-last-name">Last name</Label>
            <Input
              id="profile-last-name"
              value={lastName}
              maxLength={60}
              autoComplete="family-name"
              placeholder="Fertig"
              onChange={(e) => setLastName(e.target.value)}
              onKeyDown={(e) => e.key === "Enter" && namesDirty && !savingNames && handleSaveNames()}
            />
          </div>
        </div>
      </CardContent>

      <CardFooter>
        <Button onClick={handleSaveNames} disabled={!namesDirty || savingNames}>
          {savingNames ? (
            <>
              <Loader2 className="size-3.5 animate-spin" />
              Saving…
            </>
          ) : (
            "Save name"
          )}
        </Button>
      </CardFooter>
    </Card>
  );
}
