import { Input as MDInput } from "@methanium/ui/markdown";
import { useMTP } from "@tensamin/mtp";
import { mtp } from "@tensamin/shared/data";
import { useStorage } from "@tensamin/storage/context";
import {
  Avatar,
  AvatarFallback,
  AvatarImage,
  Button,
  cn,
  Input,
  useIsMobile,
} from "@methanium/ui";
import { useUser, type User } from "@tensamin/user/context";
import { Check } from "lucide-react";
import { useEffect, useRef, useState } from "react";

async function prepImage(
  file: File,
  size = 300,
  quality = 0.8,
): Promise<string> {
  const bitmap = await createImageBitmap(file);
  const canvas = document.createElement("canvas");
  canvas.width = size;
  canvas.height = size;
  const context = canvas.getContext("2d");
  if (!context) throw new Error("Could not get canvas context");
  const scale = Math.max(size / bitmap.width, size / bitmap.height);
  const width = bitmap.width * scale;
  const height = bitmap.height * scale;
  try {
    context.drawImage(
      bitmap,
      (size - width) / 2,
      (size - height) / 2,
      width,
      height,
    );
  } finally {
    bitmap.close();
  }
  return canvas.toDataURL("image/webp", quality);
}

export default function Page() {
  const { get, update } = useUser();
  const { load } = useStorage();
  const { send } = useMTP();
  const isMobile = useIsMobile();
  const [currentUser, setCurrentUser] = useState<User | null>(null);
  const [draftUser, setDraftUser] = useState<Partial<User>>({});
  const [errorMessage, setErrorMessage] = useState("");
  const [saveSucceeded, setSaveSucceeded] = useState(false);
  const avatarUploadRef = useRef<HTMLInputElement>(null);
  const draftInitializedRef = useRef(false);
  const effectiveAvatar =
    draftUser.Avatar === "none" ? undefined : draftUser.Avatar;
  const updateDraftUser = (
    updater: (previous: Partial<User>) => Partial<User>,
  ) => {
    setSaveSucceeded(false);
    setErrorMessage("");
    setDraftUser(updater);
  };
  useEffect(() => {
    void (async () => setCurrentUser(await get(await load("user_id"))))();
  }, [get, load]);
  useEffect(() => {
    if (!currentUser || draftInitializedRef.current) return;
    setDraftUser(currentUser);
    draftInitializedRef.current = true;
  }, [currentUser]);
  async function handleAvatarUpload(file: File) {
    const avatar = await prepImage(file);
    updateDraftUser((previous) => ({ ...previous, Avatar: avatar }));
    if (avatarUploadRef.current) avatarUploadRef.current.value = "";
  }
  if (!currentUser) return <p>Loading...</p>;
  return (
    <>
      <input
        ref={avatarUploadRef}
        hidden
        onChange={(event) =>
          event.target.files?.[0] && handleAvatarUpload(event.target.files[0])
        }
        type="file"
      />
      <div className={cn("flex flex-col gap-5", isMobile ? "w-full" : "w-80")}>
        <div className="flex items-center gap-3">
          <Avatar className="size-14">
            <AvatarImage src={effectiveAvatar} />
            <AvatarFallback className="text-2xl">
              {draftUser.Display?.slice(0, 2).toUpperCase() ||
                currentUser.Display.slice(0, 2).toUpperCase()}
            </AvatarFallback>
          </Avatar>
          <div className="flex flex-col gap-1">
            <p>Avatar</p>
            <div className="flex gap-1">
              <Button onClick={() => avatarUploadRef.current?.click()}>
                Upload avatar
              </Button>
              <Button
                onClick={() =>
                  updateDraftUser((previous) => ({
                    ...previous,
                    Avatar: undefined,
                  }))
                }
                variant="destructive"
                disabled={effectiveAvatar === undefined}
              >
                Remove
              </Button>
            </div>
            <p className="text-sm text-muted-foreground">
              GIFs are supported in decentralised mode or with Tensamin Premium.
              <br />
              Maximum file size is 16mb.
            </p>
          </div>
        </div>
        <Input
          className="w-full"
          onChange={(event) =>
            updateDraftUser((previous) => ({
              ...previous,
              display: event.target.value,
            }))
          }
          placeholder="Display Name"
          value={draftUser.Display || ""}
        />
        <Input
          className="w-full"
          onChange={(event) =>
            updateDraftUser((previous) => ({
              ...previous,
              username: event.target.value,
            }))
          }
          placeholder="Username"
          value={draftUser.Username || ""}
        />
        <MDInput
          paddingY="4px"
          paddingX="10px"
          fontSize=".875rem"
          placeholder="About Me"
          setValue={(value) =>
            updateDraftUser((previous) => ({ ...previous, about: value }))
          }
          value={draftUser.About || ""}
        />
        <Button
          onClick={async () => {
            const { Avatar, ...draftUsersWithoutAvatar } = draftUser;
            const payload = {
              ...draftUsersWithoutAvatar,
              ...(typeof Avatar === "string"
                ? {
                    Avatar: Avatar.startsWith("data:")
                      ? (Avatar.split(",", 2)[1] ?? "")
                      : Avatar,
                  }
                : {}),
            };
            const validation = mtp.ChangeUserData.request.safeParse(payload);
            if (!validation.success) {
              setSaveSucceeded(false);
              setErrorMessage(
                validation.error.issues[0]?.message ?? "Invalid profile data",
              );
              return;
            }
            try {
              const response = await send("ChangeUserData", validation.data);
              if (response.type.startsWith("Error")) {
                throw new Error(response.type);
              }
              const updatedUser = mtp.GetUserData.response.parse({
                ...currentUser,
                ...draftUser,
              });
              await update(updatedUser);
              setCurrentUser(updatedUser);
              setDraftUser(updatedUser);
              setSaveSucceeded(true);
              setErrorMessage("");
            } catch (error) {
              setSaveSucceeded(false);
              setErrorMessage("Failed to update profile: " + error);
            }
          }}
        >
          {saveSucceeded ? (
            <span className="inline-flex items-center gap-1.5">
              <Check className="size-4" />
              Saved
            </span>
          ) : (
            "Save"
          )}
        </Button>
        {errorMessage && (
          <p className="text-sm text-destructive">{errorMessage}</p>
        )}
      </div>
    </>
  );
}
