import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";

import { api } from "../client";
import type { components } from "../schema";
import { HttpError, InUseError } from "../errors";
import { keys } from "../query-keys";

type NewVocabularyRequest = components["schemas"]["NewVocabularyRequest"];
type LabelInput = components["schemas"]["LabelInput"];

export function useVocabularies() {
  return useQuery({
    queryKey: keys.vocabularies(),
    queryFn: async () => {
      const { data, error } = await api.GET("/api/admin/vocabularies");

      if (error || !data) throw new Error("failed to load vocabularies");

      return data;
    },
    staleTime: 5 * 60 * 1000,
  });
}

export function useCreateVocabulary() {
  const qc = useQueryClient();

  return useMutation({
    mutationFn: async (body: NewVocabularyRequest) => {
      const { data, error, response } = await api.POST("/api/admin/vocabularies", { body });

      if (error || !data) throw new HttpError(response.status);

      return data;
    },
    onSuccess: () => qc.invalidateQueries({ queryKey: keys.vocabularies() }),
    meta: { successMessage: "toast.created", suppressErrorToast: true },
  });
}

export function useRenameVocabulary() {
  const qc = useQueryClient();

  return useMutation({
    mutationFn: async ({ id, key }: { id: string; key: string }) => {
      const { response } = await api.PATCH("/api/admin/vocabularies/{id}", {
        params: { path: { id } },
        body: { key },
      });

      if (response.status !== 204) throw new HttpError(response.status);
    },
    onSuccess: () => qc.invalidateQueries({ queryKey: keys.vocabularies() }),
    meta: { successMessage: "toast.renamed", suppressErrorToast: true },
  });
}

export function useDeleteVocabulary() {
  const qc = useQueryClient();

  return useMutation({
    mutationFn: async (id: string) => {
      const { error, response } = await api.DELETE("/api/admin/vocabularies/{id}", {
        params: { path: { id } },
      });

      if (response.status === 409) throw new InUseError((error as { count?: number })?.count ?? 0);
      if (response.status !== 204) throw new HttpError(response.status);
    },
    onSuccess: () => qc.invalidateQueries({ queryKey: keys.vocabularies() }),
    meta: { successMessage: "toast.deleted", suppressErrorToast: true },
  });
}

export function useTerms(vocabularyId: string | null | undefined) {
  return useQuery({
    queryKey: keys.terms(vocabularyId),
    enabled: !!vocabularyId,
    queryFn: async () => {
      const { data, error } = await api.GET("/api/admin/vocabularies/{id}/terms", {
        params: { path: { id: vocabularyId! } },
      });

      if (error || !data) throw new Error("failed to load terms");

      return data;
    },
    staleTime: 5 * 60 * 1000,
  });
}

export function useAddTerm() {
  const qc = useQueryClient();

  return useMutation({
    mutationFn: async ({
      vocabularyId,
      external_uri,
      labels,
    }: {
      vocabularyId: string;
      external_uri: string | null;
      labels: LabelInput[];
    }) => {
      const { response } = await api.POST("/api/admin/vocabularies/{id}/terms", {
        params: { path: { id: vocabularyId } },
        body: { external_uri, labels },
      });

      if (response.status !== 201) throw new HttpError(response.status);
    },
    onSuccess: (_result, { vocabularyId }) =>
      qc.invalidateQueries({ queryKey: keys.terms(vocabularyId) }),
    meta: { successMessage: "toast.created", suppressErrorToast: true },
  });
}

export function useUpdateTerm() {
  const qc = useQueryClient();

  return useMutation({
    mutationFn: async ({
      vocabularyId,
      termId,
      external_uri,
      labels,
    }: {
      vocabularyId: string;
      termId: string;
      external_uri: string | null;
      labels: LabelInput[];
    }) => {
      const { response } = await api.PATCH("/api/admin/vocabularies/{id}/terms/{term_id}", {
        params: { path: { id: vocabularyId, term_id: termId } },
        body: { external_uri, labels },
      });

      if (response.status !== 204) throw new HttpError(response.status);
    },
    onSuccess: (_d, { vocabularyId }) => qc.invalidateQueries({ queryKey: keys.terms(vocabularyId) }),
    meta: { successMessage: "toast.saved", suppressErrorToast: true },
  });
}

export function useDeleteTerm() {
  const qc = useQueryClient();

  return useMutation({
    mutationFn: async ({ vocabularyId, termId }: { vocabularyId: string; termId: string }) => {
      const { error, response } = await api.DELETE("/api/admin/vocabularies/{id}/terms/{term_id}", {
        params: { path: { id: vocabularyId, term_id: termId } },
      });

      if (response.status === 409) throw new InUseError((error as { count?: number })?.count ?? 0);
      if (response.status !== 204) throw new HttpError(response.status);
    },
    onSuccess: (_d, { vocabularyId }) => qc.invalidateQueries({ queryKey: keys.terms(vocabularyId) }),
    meta: { successMessage: "toast.deleted", suppressErrorToast: true },
  });
}
