import { api } from "@code/backend/convex/_generated/api";
import type { Id } from "@code/backend/convex/_generated/dataModel";
import { useMutation, useQuery } from "convex/react";
import { useState } from "react";

import type { PersonalOrganizationState } from "@/hooks/use-personal-organization";
import { projectConversation } from "@/lib/chat/conversation";
import type { AgentStatus, ChatAgentState } from "@/lib/chat/types";

const requestId = (): string =>
  globalThis.crypto?.randomUUID?.() ??
  `${Date.now()}-${Math.random().toString(16).slice(2)}`;

const uploadImage = async (
  file: File,
  generateUploadUrl: () => Promise<string>
) => {
  const response = await fetch(await generateUploadUrl(), {
    body: file,
    headers: { "content-type": file.type },
    method: "POST",
  });
  if (!response.ok) {
    throw new Error(`Image upload failed (${response.status})`);
  }
  const payload: unknown = await response.json();
  if (
    typeof payload !== "object" ||
    payload === null ||
    !("storageId" in payload) ||
    typeof payload.storageId !== "string"
  ) {
    throw new Error("Image upload returned an invalid response");
  }
  return {
    filename: file.name || undefined,
    mimeType: file.type,
    storageId: payload.storageId as Id<"_storage">,
  };
};

export const useOrganizationChatAgent = (
  organization: PersonalOrganizationState
): ChatAgentState => {
  const { organizationId } = organization;
  const rows = useQuery(
    api.conversationMessages.listForCurrentOrganization,
    organizationId ? { organizationId } : "skip"
  );
  const send = useMutation(api.conversationMessages.send);
  const generateUploadUrl = useMutation(
    api.conversationMessages.generateUploadUrl
  );
  const [sendError, setSendError] = useState<Error>();

  const projected = projectConversation(rows ?? []);

  let status: AgentStatus = projected.pending ? "submitted" : "idle";
  if (!organizationId || rows === undefined) {
    status = organization.error ? "error" : "connecting";
  } else if (projected.failedError || sendError) {
    status = "error";
  }

  const sendMessage = async (
    message: string,
    options?: { readonly images?: readonly File[] }
  ): Promise<void> => {
    if (!organizationId) {
      throw (
        organization.error ??
        new Error("Personal organization is still being prepared")
      );
    }
    setSendError(undefined);
    try {
      const images = await Promise.all(
        (options?.images ?? []).map((file) =>
          uploadImage(file, generateUploadUrl)
        )
      );
      await send({
        clientRequestId: requestId(),
        images,
        organizationId,
        rawText: message,
      });
    } catch (error) {
      const normalized =
        error instanceof Error ? error : new Error(String(error));
      setSendError(normalized);
      throw normalized;
    }
  };

  return {
    error:
      organization.error ??
      sendError ??
      (projected.failedError ? new Error(projected.failedError) : undefined),
    historyReady: rows !== undefined,
    messages: projected.messages,
    sendMessage,
    status,
  };
};
