/**
 * Generated by orval v8.5.3 🍺
 * Do not edit manually.
 * Api
 * Tx OS API specification
 * OpenAPI spec version: 0.1.0
 */
import { useMutation, useQuery } from "@tanstack/react-query";
import type {
  MutationFunction,
  QueryFunction,
  QueryKey,
  UseMutationOptions,
  UseMutationResult,
  UseQueryOptions,
  UseQueryResult,
} from "@tanstack/react-query";

import type {
  AddAppPermissionBody,
  AddParticipantsBody,
  AddUserRoleBody,
  AdminAppOpensByApp,
  AdminAppOpensByUser,
  AdminResetLinkResponse,
  AdminStats,
  App,
  AppDeletionConflict,
  AppPermissionLink,
  AppPermissionsImpact,
  AppPermissionsImpactBody,
  AppSettings,
  AuditLogList,
  AuthUser,
  ConversationWithDetails,
  CreateAppBody,
  CreateConversationBody,
  CreateGroupBody,
  CreateRoleBody,
  CreateServiceBody,
  CreateServiceOrderBody,
  CreateUserBody,
  DeleteAppParams,
  DeleteGroupParams,
  DeleteServiceParams,
  DeleteUserParams,
  ErrorResponse,
  ExportAuditLogsCsvParams,
  ForgotPasswordBody,
  ForgotPasswordResponse,
  GetAdminAppOpensByAppParams,
  GetAdminAppOpensByUserParams,
  GetAdminStatsParams,
  GetAppPermissionAuditParams,
  GetGroupPermissionAuditParams,
  GetRolePermissionAuditParams,
  GetUserPermissionAuditParams,
  Group,
  GroupDeletionConflict,
  GroupDetail,
  HealthStatus,
  HomeStats,
  LeaveConversationBody,
  ListAuditLogsParams,
  ListGroupsParams,
  ListUsersParams,
  LoginBody,
  MessageWithSender,
  Notification,
  Permission,
  PermissionAuditList,
  RegisterBody,
  ReplaceRolePermissionsBody,
  RequestUploadUrlBody,
  RequestUploadUrlResponse,
  ResetPasswordBody,
  Role,
  RoleDeletionConflict,
  RolePermissionAuditList,
  RolePermissionsImpact,
  RolePermissionsImpactBody,
  RoleUsage,
  SendMessageBody,
  Service,
  ServiceCategory,
  ServiceDeletionConflict,
  ServiceOrder,
  SuccessResponse,
  UpdateAppBody,
  UpdateAppSettingsBody,
  UpdateClockHour12Body,
  UpdateClockStyleBody,
  UpdateConversationBody,
  UpdateConversationStateBody,
  UpdateGroupBody,
  UpdateLanguageBody,
  UpdateMyAppOrderBody,
  UpdateRoleBody,
  UpdateServiceBody,
  UpdateServiceOrderStatusBody,
  UpdateUserBody,
  UserDeletionConflict,
  UserProfile,
  VerifyResetTokenBody,
  VerifyResetTokenResponse,
} from "./api.schemas";

import { customFetch } from "../custom-fetch";
import type { ErrorType, BodyType } from "../custom-fetch";

type AwaitedInput<T> = PromiseLike<T> | T;

type Awaited<O> = O extends AwaitedInput<infer T> ? T : never;

type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];

/**
 * @summary Health check
 */
export const getHealthCheckUrl = () => {
  return `/api/healthz`;
};

export const healthCheck = async (
  options?: RequestInit,
): Promise<HealthStatus> => {
  return customFetch<HealthStatus>(getHealthCheckUrl(), {
    ...options,
    method: "GET",
  });
};

export const getHealthCheckQueryKey = () => {
  return [`/api/healthz`] as const;
};

export const getHealthCheckQueryOptions = <
  TData = Awaited<ReturnType<typeof healthCheck>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof healthCheck>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getHealthCheckQueryKey();

  const queryFn: QueryFunction<Awaited<ReturnType<typeof healthCheck>>> = ({
    signal,
  }) => healthCheck({ signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof healthCheck>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type HealthCheckQueryResult = NonNullable<
  Awaited<ReturnType<typeof healthCheck>>
>;
export type HealthCheckQueryError = ErrorType<unknown>;

/**
 * @summary Health check
 */

export function useHealthCheck<
  TData = Awaited<ReturnType<typeof healthCheck>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof healthCheck>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getHealthCheckQueryOptions(options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Register a new user
 */
export const getRegisterUrl = () => {
  return `/api/auth/register`;
};

export const register = async (
  registerBody: RegisterBody,
  options?: RequestInit,
): Promise<AuthUser> => {
  return customFetch<AuthUser>(getRegisterUrl(), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(registerBody),
  });
};

export const getRegisterMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof register>>,
    TError,
    { data: BodyType<RegisterBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof register>>,
  TError,
  { data: BodyType<RegisterBody> },
  TContext
> => {
  const mutationKey = ["register"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof register>>,
    { data: BodyType<RegisterBody> }
  > = (props) => {
    const { data } = props ?? {};

    return register(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type RegisterMutationResult = NonNullable<
  Awaited<ReturnType<typeof register>>
>;
export type RegisterMutationBody = BodyType<RegisterBody>;
export type RegisterMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Register a new user
 */
export const useRegister = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof register>>,
    TError,
    { data: BodyType<RegisterBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof register>>,
  TError,
  { data: BodyType<RegisterBody> },
  TContext
> => {
  return useMutation(getRegisterMutationOptions(options));
};

/**
 * @summary Login
 */
export const getLoginUrl = () => {
  return `/api/auth/login`;
};

export const login = async (
  loginBody: LoginBody,
  options?: RequestInit,
): Promise<AuthUser> => {
  return customFetch<AuthUser>(getLoginUrl(), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(loginBody),
  });
};

export const getLoginMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof login>>,
    TError,
    { data: BodyType<LoginBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof login>>,
  TError,
  { data: BodyType<LoginBody> },
  TContext
> => {
  const mutationKey = ["login"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof login>>,
    { data: BodyType<LoginBody> }
  > = (props) => {
    const { data } = props ?? {};

    return login(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type LoginMutationResult = NonNullable<
  Awaited<ReturnType<typeof login>>
>;
export type LoginMutationBody = BodyType<LoginBody>;
export type LoginMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Login
 */
export const useLogin = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof login>>,
    TError,
    { data: BodyType<LoginBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof login>>,
  TError,
  { data: BodyType<LoginBody> },
  TContext
> => {
  return useMutation(getLoginMutationOptions(options));
};

/**
 * @summary Logout
 */
export const getLogoutUrl = () => {
  return `/api/auth/logout`;
};

export const logout = async (
  options?: RequestInit,
): Promise<SuccessResponse> => {
  return customFetch<SuccessResponse>(getLogoutUrl(), {
    ...options,
    method: "POST",
  });
};

export const getLogoutMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof logout>>,
    TError,
    void,
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof logout>>,
  TError,
  void,
  TContext
> => {
  const mutationKey = ["logout"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof logout>>,
    void
  > = () => {
    return logout(requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type LogoutMutationResult = NonNullable<
  Awaited<ReturnType<typeof logout>>
>;

export type LogoutMutationError = ErrorType<unknown>;

/**
 * @summary Logout
 */
export const useLogout = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof logout>>,
    TError,
    void,
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof logout>>,
  TError,
  void,
  TContext
> => {
  return useMutation(getLogoutMutationOptions(options));
};

/**
 * @summary Request a password reset link
 */
export const getForgotPasswordUrl = () => {
  return `/api/auth/forgot-password`;
};

export const forgotPassword = async (
  forgotPasswordBody: ForgotPasswordBody,
  options?: RequestInit,
): Promise<ForgotPasswordResponse> => {
  return customFetch<ForgotPasswordResponse>(getForgotPasswordUrl(), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(forgotPasswordBody),
  });
};

export const getForgotPasswordMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof forgotPassword>>,
    TError,
    { data: BodyType<ForgotPasswordBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof forgotPassword>>,
  TError,
  { data: BodyType<ForgotPasswordBody> },
  TContext
> => {
  const mutationKey = ["forgotPassword"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof forgotPassword>>,
    { data: BodyType<ForgotPasswordBody> }
  > = (props) => {
    const { data } = props ?? {};

    return forgotPassword(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type ForgotPasswordMutationResult = NonNullable<
  Awaited<ReturnType<typeof forgotPassword>>
>;
export type ForgotPasswordMutationBody = BodyType<ForgotPasswordBody>;
export type ForgotPasswordMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Request a password reset link
 */
export const useForgotPassword = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof forgotPassword>>,
    TError,
    { data: BodyType<ForgotPasswordBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof forgotPassword>>,
  TError,
  { data: BodyType<ForgotPasswordBody> },
  TContext
> => {
  return useMutation(getForgotPasswordMutationOptions(options));
};

/**
 * @summary Set a new password using a reset token
 */
export const getResetPasswordUrl = () => {
  return `/api/auth/reset-password`;
};

export const resetPassword = async (
  resetPasswordBody: ResetPasswordBody,
  options?: RequestInit,
): Promise<SuccessResponse> => {
  return customFetch<SuccessResponse>(getResetPasswordUrl(), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(resetPasswordBody),
  });
};

export const getResetPasswordMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof resetPassword>>,
    TError,
    { data: BodyType<ResetPasswordBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof resetPassword>>,
  TError,
  { data: BodyType<ResetPasswordBody> },
  TContext
> => {
  const mutationKey = ["resetPassword"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof resetPassword>>,
    { data: BodyType<ResetPasswordBody> }
  > = (props) => {
    const { data } = props ?? {};

    return resetPassword(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type ResetPasswordMutationResult = NonNullable<
  Awaited<ReturnType<typeof resetPassword>>
>;
export type ResetPasswordMutationBody = BodyType<ResetPasswordBody>;
export type ResetPasswordMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Set a new password using a reset token
 */
export const useResetPassword = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof resetPassword>>,
    TError,
    { data: BodyType<ResetPasswordBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof resetPassword>>,
  TError,
  { data: BodyType<ResetPasswordBody> },
  TContext
> => {
  return useMutation(getResetPasswordMutationOptions(options));
};

/**
 * @summary Check whether a reset token is valid
 */
export const getVerifyResetTokenUrl = () => {
  return `/api/auth/reset-password/verify`;
};

export const verifyResetToken = async (
  verifyResetTokenBody: VerifyResetTokenBody,
  options?: RequestInit,
): Promise<VerifyResetTokenResponse> => {
  return customFetch<VerifyResetTokenResponse>(getVerifyResetTokenUrl(), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(verifyResetTokenBody),
  });
};

export const getVerifyResetTokenMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof verifyResetToken>>,
    TError,
    { data: BodyType<VerifyResetTokenBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof verifyResetToken>>,
  TError,
  { data: BodyType<VerifyResetTokenBody> },
  TContext
> => {
  const mutationKey = ["verifyResetToken"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof verifyResetToken>>,
    { data: BodyType<VerifyResetTokenBody> }
  > = (props) => {
    const { data } = props ?? {};

    return verifyResetToken(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type VerifyResetTokenMutationResult = NonNullable<
  Awaited<ReturnType<typeof verifyResetToken>>
>;
export type VerifyResetTokenMutationBody = BodyType<VerifyResetTokenBody>;
export type VerifyResetTokenMutationError = ErrorType<unknown>;

/**
 * @summary Check whether a reset token is valid
 */
export const useVerifyResetToken = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof verifyResetToken>>,
    TError,
    { data: BodyType<VerifyResetTokenBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof verifyResetToken>>,
  TError,
  { data: BodyType<VerifyResetTokenBody> },
  TContext
> => {
  return useMutation(getVerifyResetTokenMutationOptions(options));
};

/**
 * @summary Admin generates a one-time password reset link for a user
 */
export const getAdminIssueResetLinkUrl = (id: number) => {
  return `/api/auth/admin/users/${id}/issue-reset-link`;
};

export const adminIssueResetLink = async (
  id: number,
  options?: RequestInit,
): Promise<AdminResetLinkResponse> => {
  return customFetch<AdminResetLinkResponse>(getAdminIssueResetLinkUrl(id), {
    ...options,
    method: "POST",
  });
};

export const getAdminIssueResetLinkMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof adminIssueResetLink>>,
    TError,
    { id: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof adminIssueResetLink>>,
  TError,
  { id: number },
  TContext
> => {
  const mutationKey = ["adminIssueResetLink"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof adminIssueResetLink>>,
    { id: number }
  > = (props) => {
    const { id } = props ?? {};

    return adminIssueResetLink(id, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type AdminIssueResetLinkMutationResult = NonNullable<
  Awaited<ReturnType<typeof adminIssueResetLink>>
>;

export type AdminIssueResetLinkMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Admin generates a one-time password reset link for a user
 */
export const useAdminIssueResetLink = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof adminIssueResetLink>>,
    TError,
    { id: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof adminIssueResetLink>>,
  TError,
  { id: number },
  TContext
> => {
  return useMutation(getAdminIssueResetLinkMutationOptions(options));
};

/**
 * @summary Get current user
 */
export const getGetMeUrl = () => {
  return `/api/auth/me`;
};

export const getMe = async (options?: RequestInit): Promise<AuthUser> => {
  return customFetch<AuthUser>(getGetMeUrl(), {
    ...options,
    method: "GET",
  });
};

export const getGetMeQueryKey = () => {
  return [`/api/auth/me`] as const;
};

export const getGetMeQueryOptions = <
  TData = Awaited<ReturnType<typeof getMe>>,
  TError = ErrorType<ErrorResponse>,
>(options?: {
  query?: UseQueryOptions<Awaited<ReturnType<typeof getMe>>, TError, TData>;
  request?: SecondParameter<typeof customFetch>;
}) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getGetMeQueryKey();

  const queryFn: QueryFunction<Awaited<ReturnType<typeof getMe>>> = ({
    signal,
  }) => getMe({ signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof getMe>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type GetMeQueryResult = NonNullable<Awaited<ReturnType<typeof getMe>>>;
export type GetMeQueryError = ErrorType<ErrorResponse>;

/**
 * @summary Get current user
 */

export function useGetMe<
  TData = Awaited<ReturnType<typeof getMe>>,
  TError = ErrorType<ErrorResponse>,
>(options?: {
  query?: UseQueryOptions<Awaited<ReturnType<typeof getMe>>, TError, TData>;
  request?: SecondParameter<typeof customFetch>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetMeQueryOptions(options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Update preferred language
 */
export const getUpdateLanguageUrl = () => {
  return `/api/auth/me/language`;
};

export const updateLanguage = async (
  updateLanguageBody: UpdateLanguageBody,
  options?: RequestInit,
): Promise<AuthUser> => {
  return customFetch<AuthUser>(getUpdateLanguageUrl(), {
    ...options,
    method: "PATCH",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(updateLanguageBody),
  });
};

export const getUpdateLanguageMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateLanguage>>,
    TError,
    { data: BodyType<UpdateLanguageBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof updateLanguage>>,
  TError,
  { data: BodyType<UpdateLanguageBody> },
  TContext
> => {
  const mutationKey = ["updateLanguage"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof updateLanguage>>,
    { data: BodyType<UpdateLanguageBody> }
  > = (props) => {
    const { data } = props ?? {};

    return updateLanguage(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type UpdateLanguageMutationResult = NonNullable<
  Awaited<ReturnType<typeof updateLanguage>>
>;
export type UpdateLanguageMutationBody = BodyType<UpdateLanguageBody>;
export type UpdateLanguageMutationError = ErrorType<unknown>;

/**
 * @summary Update preferred language
 */
export const useUpdateLanguage = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateLanguage>>,
    TError,
    { data: BodyType<UpdateLanguageBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof updateLanguage>>,
  TError,
  { data: BodyType<UpdateLanguageBody> },
  TContext
> => {
  return useMutation(getUpdateLanguageMutationOptions(options));
};

/**
 * @summary Update preferred home-screen clock style
 */
export const getUpdateClockStyleUrl = () => {
  return `/api/auth/me/clock-style`;
};

export const updateClockStyle = async (
  updateClockStyleBody: UpdateClockStyleBody,
  options?: RequestInit,
): Promise<AuthUser> => {
  return customFetch<AuthUser>(getUpdateClockStyleUrl(), {
    ...options,
    method: "PATCH",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(updateClockStyleBody),
  });
};

export const getUpdateClockStyleMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateClockStyle>>,
    TError,
    { data: BodyType<UpdateClockStyleBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof updateClockStyle>>,
  TError,
  { data: BodyType<UpdateClockStyleBody> },
  TContext
> => {
  const mutationKey = ["updateClockStyle"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof updateClockStyle>>,
    { data: BodyType<UpdateClockStyleBody> }
  > = (props) => {
    const { data } = props ?? {};

    return updateClockStyle(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type UpdateClockStyleMutationResult = NonNullable<
  Awaited<ReturnType<typeof updateClockStyle>>
>;
export type UpdateClockStyleMutationBody = BodyType<UpdateClockStyleBody>;
export type UpdateClockStyleMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Update preferred home-screen clock style
 */
export const useUpdateClockStyle = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateClockStyle>>,
    TError,
    { data: BodyType<UpdateClockStyleBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof updateClockStyle>>,
  TError,
  { data: BodyType<UpdateClockStyleBody> },
  TContext
> => {
  return useMutation(getUpdateClockStyleMutationOptions(options));
};

/**
 * @summary Update preferred 12/24-hour clock format
 */
export const getUpdateClockHour12Url = () => {
  return `/api/auth/me/clock-hour12`;
};

export const updateClockHour12 = async (
  updateClockHour12Body: UpdateClockHour12Body,
  options?: RequestInit,
): Promise<AuthUser> => {
  return customFetch<AuthUser>(getUpdateClockHour12Url(), {
    ...options,
    method: "PATCH",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(updateClockHour12Body),
  });
};

export const getUpdateClockHour12MutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateClockHour12>>,
    TError,
    { data: BodyType<UpdateClockHour12Body> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof updateClockHour12>>,
  TError,
  { data: BodyType<UpdateClockHour12Body> },
  TContext
> => {
  const mutationKey = ["updateClockHour12"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof updateClockHour12>>,
    { data: BodyType<UpdateClockHour12Body> }
  > = (props) => {
    const { data } = props ?? {};

    return updateClockHour12(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type UpdateClockHour12MutationResult = NonNullable<
  Awaited<ReturnType<typeof updateClockHour12>>
>;
export type UpdateClockHour12MutationBody = BodyType<UpdateClockHour12Body>;
export type UpdateClockHour12MutationError = ErrorType<ErrorResponse>;

/**
 * @summary Update preferred 12/24-hour clock format
 */
export const useUpdateClockHour12 = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateClockHour12>>,
    TError,
    { data: BodyType<UpdateClockHour12Body> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof updateClockHour12>>,
  TError,
  { data: BodyType<UpdateClockHour12Body> },
  TContext
> => {
  return useMutation(getUpdateClockHour12MutationOptions(options));
};

/**
 * @summary List all active apps
 */
export const getListAppsUrl = () => {
  return `/api/apps`;
};

export const listApps = async (options?: RequestInit): Promise<App[]> => {
  return customFetch<App[]>(getListAppsUrl(), {
    ...options,
    method: "GET",
  });
};

export const getListAppsQueryKey = () => {
  return [`/api/apps`] as const;
};

export const getListAppsQueryOptions = <
  TData = Awaited<ReturnType<typeof listApps>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<Awaited<ReturnType<typeof listApps>>, TError, TData>;
  request?: SecondParameter<typeof customFetch>;
}) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getListAppsQueryKey();

  const queryFn: QueryFunction<Awaited<ReturnType<typeof listApps>>> = ({
    signal,
  }) => listApps({ signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof listApps>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type ListAppsQueryResult = NonNullable<
  Awaited<ReturnType<typeof listApps>>
>;
export type ListAppsQueryError = ErrorType<unknown>;

/**
 * @summary List all active apps
 */

export function useListApps<
  TData = Awaited<ReturnType<typeof listApps>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<Awaited<ReturnType<typeof listApps>>, TError, TData>;
  request?: SecondParameter<typeof customFetch>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getListAppsQueryOptions(options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Create a new app (admin)
 */
export const getCreateAppUrl = () => {
  return `/api/apps`;
};

export const createApp = async (
  createAppBody: CreateAppBody,
  options?: RequestInit,
): Promise<App> => {
  return customFetch<App>(getCreateAppUrl(), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(createAppBody),
  });
};

export const getCreateAppMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof createApp>>,
    TError,
    { data: BodyType<CreateAppBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof createApp>>,
  TError,
  { data: BodyType<CreateAppBody> },
  TContext
> => {
  const mutationKey = ["createApp"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof createApp>>,
    { data: BodyType<CreateAppBody> }
  > = (props) => {
    const { data } = props ?? {};

    return createApp(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type CreateAppMutationResult = NonNullable<
  Awaited<ReturnType<typeof createApp>>
>;
export type CreateAppMutationBody = BodyType<CreateAppBody>;
export type CreateAppMutationError = ErrorType<unknown>;

/**
 * @summary Create a new app (admin)
 */
export const useCreateApp = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof createApp>>,
    TError,
    { data: BodyType<CreateAppBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof createApp>>,
  TError,
  { data: BodyType<CreateAppBody> },
  TContext
> => {
  return useMutation(getCreateAppMutationOptions(options));
};

/**
 * @summary Get app by ID
 */
export const getGetAppUrl = (id: number) => {
  return `/api/apps/${id}`;
};

export const getApp = async (
  id: number,
  options?: RequestInit,
): Promise<App> => {
  return customFetch<App>(getGetAppUrl(id), {
    ...options,
    method: "GET",
  });
};

export const getGetAppQueryKey = (id: number) => {
  return [`/api/apps/${id}`] as const;
};

export const getGetAppQueryOptions = <
  TData = Awaited<ReturnType<typeof getApp>>,
  TError = ErrorType<unknown>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<Awaited<ReturnType<typeof getApp>>, TError, TData>;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getGetAppQueryKey(id);

  const queryFn: QueryFunction<Awaited<ReturnType<typeof getApp>>> = ({
    signal,
  }) => getApp(id, { signal, ...requestOptions });

  return {
    queryKey,
    queryFn,
    enabled: !!id,
    ...queryOptions,
  } as UseQueryOptions<Awaited<ReturnType<typeof getApp>>, TError, TData> & {
    queryKey: QueryKey;
  };
};

export type GetAppQueryResult = NonNullable<Awaited<ReturnType<typeof getApp>>>;
export type GetAppQueryError = ErrorType<unknown>;

/**
 * @summary Get app by ID
 */

export function useGetApp<
  TData = Awaited<ReturnType<typeof getApp>>,
  TError = ErrorType<unknown>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<Awaited<ReturnType<typeof getApp>>, TError, TData>;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetAppQueryOptions(id, options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Update app (admin)
 */
export const getUpdateAppUrl = (id: number) => {
  return `/api/apps/${id}`;
};

export const updateApp = async (
  id: number,
  updateAppBody: UpdateAppBody,
  options?: RequestInit,
): Promise<App> => {
  return customFetch<App>(getUpdateAppUrl(id), {
    ...options,
    method: "PATCH",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(updateAppBody),
  });
};

export const getUpdateAppMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateApp>>,
    TError,
    { id: number; data: BodyType<UpdateAppBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof updateApp>>,
  TError,
  { id: number; data: BodyType<UpdateAppBody> },
  TContext
> => {
  const mutationKey = ["updateApp"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof updateApp>>,
    { id: number; data: BodyType<UpdateAppBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return updateApp(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type UpdateAppMutationResult = NonNullable<
  Awaited<ReturnType<typeof updateApp>>
>;
export type UpdateAppMutationBody = BodyType<UpdateAppBody>;
export type UpdateAppMutationError = ErrorType<unknown>;

/**
 * @summary Update app (admin)
 */
export const useUpdateApp = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateApp>>,
    TError,
    { id: number; data: BodyType<UpdateAppBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof updateApp>>,
  TError,
  { id: number; data: BodyType<UpdateAppBody> },
  TContext
> => {
  return useMutation(getUpdateAppMutationOptions(options));
};

/**
 * @summary Delete app (admin)
 */
export const getDeleteAppUrl = (id: number, params?: DeleteAppParams) => {
  const normalizedParams = new URLSearchParams();

  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined) {
      normalizedParams.append(key, value === null ? "null" : value.toString());
    }
  });

  const stringifiedParams = normalizedParams.toString();

  return stringifiedParams.length > 0
    ? `/api/apps/${id}?${stringifiedParams}`
    : `/api/apps/${id}`;
};

export const deleteApp = async (
  id: number,
  params?: DeleteAppParams,
  options?: RequestInit,
): Promise<void> => {
  return customFetch<void>(getDeleteAppUrl(id, params), {
    ...options,
    method: "DELETE",
  });
};

export const getDeleteAppMutationOptions = <
  TError = ErrorType<AppDeletionConflict>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof deleteApp>>,
    TError,
    { id: number; params?: DeleteAppParams },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof deleteApp>>,
  TError,
  { id: number; params?: DeleteAppParams },
  TContext
> => {
  const mutationKey = ["deleteApp"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof deleteApp>>,
    { id: number; params?: DeleteAppParams }
  > = (props) => {
    const { id, params } = props ?? {};

    return deleteApp(id, params, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type DeleteAppMutationResult = NonNullable<
  Awaited<ReturnType<typeof deleteApp>>
>;

export type DeleteAppMutationError = ErrorType<AppDeletionConflict>;

/**
 * @summary Delete app (admin)
 */
export const useDeleteApp = <
  TError = ErrorType<AppDeletionConflict>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof deleteApp>>,
    TError,
    { id: number; params?: DeleteAppParams },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof deleteApp>>,
  TError,
  { id: number; params?: DeleteAppParams },
  TContext
> => {
  return useMutation(getDeleteAppMutationOptions(options));
};

/**
 * Returns the permissions currently required for users to see this app
in their launcher. An app with no rows here is unrestricted (visible
to anyone). When more than one permission is configured, holding any
one of them is sufficient.

 * @summary List the permissions that gate this app (admin)
 */
export const getListAppPermissionsUrl = (id: number) => {
  return `/api/apps/${id}/permissions`;
};

export const listAppPermissions = async (
  id: number,
  options?: RequestInit,
): Promise<Permission[]> => {
  return customFetch<Permission[]>(getListAppPermissionsUrl(id), {
    ...options,
    method: "GET",
  });
};

export const getListAppPermissionsQueryKey = (id: number) => {
  return [`/api/apps/${id}/permissions`] as const;
};

export const getListAppPermissionsQueryOptions = <
  TData = Awaited<ReturnType<typeof listAppPermissions>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof listAppPermissions>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getListAppPermissionsQueryKey(id);

  const queryFn: QueryFunction<
    Awaited<ReturnType<typeof listAppPermissions>>
  > = ({ signal }) => listAppPermissions(id, { signal, ...requestOptions });

  return {
    queryKey,
    queryFn,
    enabled: !!id,
    ...queryOptions,
  } as UseQueryOptions<
    Awaited<ReturnType<typeof listAppPermissions>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type ListAppPermissionsQueryResult = NonNullable<
  Awaited<ReturnType<typeof listAppPermissions>>
>;
export type ListAppPermissionsQueryError = ErrorType<ErrorResponse>;

/**
 * @summary List the permissions that gate this app (admin)
 */

export function useListAppPermissions<
  TData = Awaited<ReturnType<typeof listAppPermissions>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof listAppPermissions>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getListAppPermissionsQueryOptions(id, options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * Adds a row to `app_permissions` for the (app_id, permission_id) pair.
Uses `ON CONFLICT DO NOTHING` against the composite primary key so
re-adding an existing pair is a no-op.

 * @summary Add a required permission to this app (admin)
 */
export const getAddAppPermissionUrl = (id: number) => {
  return `/api/apps/${id}/permissions`;
};

export const addAppPermission = async (
  id: number,
  addAppPermissionBody: AddAppPermissionBody,
  options?: RequestInit,
): Promise<AppPermissionLink> => {
  return customFetch<AppPermissionLink>(getAddAppPermissionUrl(id), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(addAppPermissionBody),
  });
};

export const getAddAppPermissionMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof addAppPermission>>,
    TError,
    { id: number; data: BodyType<AddAppPermissionBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof addAppPermission>>,
  TError,
  { id: number; data: BodyType<AddAppPermissionBody> },
  TContext
> => {
  const mutationKey = ["addAppPermission"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof addAppPermission>>,
    { id: number; data: BodyType<AddAppPermissionBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return addAppPermission(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type AddAppPermissionMutationResult = NonNullable<
  Awaited<ReturnType<typeof addAppPermission>>
>;
export type AddAppPermissionMutationBody = BodyType<AddAppPermissionBody>;
export type AddAppPermissionMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Add a required permission to this app (admin)
 */
export const useAddAppPermission = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof addAppPermission>>,
    TError,
    { id: number; data: BodyType<AddAppPermissionBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof addAppPermission>>,
  TError,
  { id: number; data: BodyType<AddAppPermissionBody> },
  TContext
> => {
  return useMutation(getAddAppPermissionMutationOptions(options));
};

/**
 * Given a candidate set of permission IDs that would gate this app,
returns how many users would lose visibility of the app (excluding
admins, who always see every app), the count of users who currently
see the app, and the groups that grant the app via `group_apps`. A
member of any returned group always sees the app regardless of
permission changes, so listing them lets the admin see which
populations are protected from the loss.

Mirrors `POST /roles/{id}/permissions/impact-preview` so the admin
UI can show the same kind of warning before committing the change.

 * @summary Preview the impact of changing the permissions required for an app (admin)
 */
export const getPreviewAppPermissionsImpactUrl = (id: number) => {
  return `/api/apps/${id}/permissions/impact-preview`;
};

export const previewAppPermissionsImpact = async (
  id: number,
  appPermissionsImpactBody: AppPermissionsImpactBody,
  options?: RequestInit,
): Promise<AppPermissionsImpact> => {
  return customFetch<AppPermissionsImpact>(
    getPreviewAppPermissionsImpactUrl(id),
    {
      ...options,
      method: "POST",
      headers: { "Content-Type": "application/json", ...options?.headers },
      body: JSON.stringify(appPermissionsImpactBody),
    },
  );
};

export const getPreviewAppPermissionsImpactMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof previewAppPermissionsImpact>>,
    TError,
    { id: number; data: BodyType<AppPermissionsImpactBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof previewAppPermissionsImpact>>,
  TError,
  { id: number; data: BodyType<AppPermissionsImpactBody> },
  TContext
> => {
  const mutationKey = ["previewAppPermissionsImpact"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof previewAppPermissionsImpact>>,
    { id: number; data: BodyType<AppPermissionsImpactBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return previewAppPermissionsImpact(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type PreviewAppPermissionsImpactMutationResult = NonNullable<
  Awaited<ReturnType<typeof previewAppPermissionsImpact>>
>;
export type PreviewAppPermissionsImpactMutationBody =
  BodyType<AppPermissionsImpactBody>;
export type PreviewAppPermissionsImpactMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Preview the impact of changing the permissions required for an app (admin)
 */
export const usePreviewAppPermissionsImpact = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof previewAppPermissionsImpact>>,
    TError,
    { id: number; data: BodyType<AppPermissionsImpactBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof previewAppPermissionsImpact>>,
  TError,
  { id: number; data: BodyType<AppPermissionsImpactBody> },
  TContext
> => {
  return useMutation(getPreviewAppPermissionsImpactMutationOptions(options));
};

/**
 * @summary Remove a required permission from this app (admin)
 */
export const getRemoveAppPermissionUrl = (id: number, permissionId: number) => {
  return `/api/apps/${id}/permissions/${permissionId}`;
};

export const removeAppPermission = async (
  id: number,
  permissionId: number,
  options?: RequestInit,
): Promise<void> => {
  return customFetch<void>(getRemoveAppPermissionUrl(id, permissionId), {
    ...options,
    method: "DELETE",
  });
};

export const getRemoveAppPermissionMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof removeAppPermission>>,
    TError,
    { id: number; permissionId: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof removeAppPermission>>,
  TError,
  { id: number; permissionId: number },
  TContext
> => {
  const mutationKey = ["removeAppPermission"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof removeAppPermission>>,
    { id: number; permissionId: number }
  > = (props) => {
    const { id, permissionId } = props ?? {};

    return removeAppPermission(id, permissionId, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type RemoveAppPermissionMutationResult = NonNullable<
  Awaited<ReturnType<typeof removeAppPermission>>
>;

export type RemoveAppPermissionMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Remove a required permission from this app (admin)
 */
export const useRemoveAppPermission = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof removeAppPermission>>,
    TError,
    { id: number; permissionId: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof removeAppPermission>>,
  TError,
  { id: number; permissionId: number },
  TContext
> => {
  return useMutation(getRemoveAppPermissionMutationOptions(options));
};

/**
 * @summary Log an app open event for the current user
 */
export const getLogAppOpenUrl = (id: number) => {
  return `/api/apps/${id}/open`;
};

export const logAppOpen = async (
  id: number,
  options?: RequestInit,
): Promise<void> => {
  return customFetch<void>(getLogAppOpenUrl(id), {
    ...options,
    method: "POST",
  });
};

export const getLogAppOpenMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof logAppOpen>>,
    TError,
    { id: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof logAppOpen>>,
  TError,
  { id: number },
  TContext
> => {
  const mutationKey = ["logAppOpen"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof logAppOpen>>,
    { id: number }
  > = (props) => {
    const { id } = props ?? {};

    return logAppOpen(id, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type LogAppOpenMutationResult = NonNullable<
  Awaited<ReturnType<typeof logAppOpen>>
>;

export type LogAppOpenMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Log an app open event for the current user
 */
export const useLogAppOpen = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof logAppOpen>>,
    TError,
    { id: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof logAppOpen>>,
  TError,
  { id: number },
  TContext
> => {
  return useMutation(getLogAppOpenMutationOptions(options));
};

/**
 * @summary Set the current user's preferred home apps order
 */
export const getUpdateMyAppOrderUrl = () => {
  return `/api/me/app-order`;
};

export const updateMyAppOrder = async (
  updateMyAppOrderBody: UpdateMyAppOrderBody,
  options?: RequestInit,
): Promise<App[]> => {
  return customFetch<App[]>(getUpdateMyAppOrderUrl(), {
    ...options,
    method: "PUT",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(updateMyAppOrderBody),
  });
};

export const getUpdateMyAppOrderMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateMyAppOrder>>,
    TError,
    { data: BodyType<UpdateMyAppOrderBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof updateMyAppOrder>>,
  TError,
  { data: BodyType<UpdateMyAppOrderBody> },
  TContext
> => {
  const mutationKey = ["updateMyAppOrder"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof updateMyAppOrder>>,
    { data: BodyType<UpdateMyAppOrderBody> }
  > = (props) => {
    const { data } = props ?? {};

    return updateMyAppOrder(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type UpdateMyAppOrderMutationResult = NonNullable<
  Awaited<ReturnType<typeof updateMyAppOrder>>
>;
export type UpdateMyAppOrderMutationBody = BodyType<UpdateMyAppOrderBody>;
export type UpdateMyAppOrderMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Set the current user's preferred home apps order
 */
export const useUpdateMyAppOrder = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateMyAppOrder>>,
    TError,
    { data: BodyType<UpdateMyAppOrderBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof updateMyAppOrder>>,
  TError,
  { data: BodyType<UpdateMyAppOrderBody> },
  TContext
> => {
  return useMutation(getUpdateMyAppOrderMutationOptions(options));
};

/**
 * @summary Get application settings (public)
 */
export const getGetAppSettingsUrl = () => {
  return `/api/settings`;
};

export const getAppSettings = async (
  options?: RequestInit,
): Promise<AppSettings> => {
  return customFetch<AppSettings>(getGetAppSettingsUrl(), {
    ...options,
    method: "GET",
  });
};

export const getGetAppSettingsQueryKey = () => {
  return [`/api/settings`] as const;
};

export const getGetAppSettingsQueryOptions = <
  TData = Awaited<ReturnType<typeof getAppSettings>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof getAppSettings>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getGetAppSettingsQueryKey();

  const queryFn: QueryFunction<Awaited<ReturnType<typeof getAppSettings>>> = ({
    signal,
  }) => getAppSettings({ signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof getAppSettings>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type GetAppSettingsQueryResult = NonNullable<
  Awaited<ReturnType<typeof getAppSettings>>
>;
export type GetAppSettingsQueryError = ErrorType<unknown>;

/**
 * @summary Get application settings (public)
 */

export function useGetAppSettings<
  TData = Awaited<ReturnType<typeof getAppSettings>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof getAppSettings>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetAppSettingsQueryOptions(options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Update application settings (admin)
 */
export const getUpdateAppSettingsUrl = () => {
  return `/api/settings`;
};

export const updateAppSettings = async (
  updateAppSettingsBody: UpdateAppSettingsBody,
  options?: RequestInit,
): Promise<AppSettings> => {
  return customFetch<AppSettings>(getUpdateAppSettingsUrl(), {
    ...options,
    method: "PATCH",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(updateAppSettingsBody),
  });
};

export const getUpdateAppSettingsMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateAppSettings>>,
    TError,
    { data: BodyType<UpdateAppSettingsBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof updateAppSettings>>,
  TError,
  { data: BodyType<UpdateAppSettingsBody> },
  TContext
> => {
  const mutationKey = ["updateAppSettings"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof updateAppSettings>>,
    { data: BodyType<UpdateAppSettingsBody> }
  > = (props) => {
    const { data } = props ?? {};

    return updateAppSettings(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type UpdateAppSettingsMutationResult = NonNullable<
  Awaited<ReturnType<typeof updateAppSettings>>
>;
export type UpdateAppSettingsMutationBody = BodyType<UpdateAppSettingsBody>;
export type UpdateAppSettingsMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Update application settings (admin)
 */
export const useUpdateAppSettings = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateAppSettings>>,
    TError,
    { data: BodyType<UpdateAppSettingsBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof updateAppSettings>>,
  TError,
  { data: BodyType<UpdateAppSettingsBody> },
  TContext
> => {
  return useMutation(getUpdateAppSettingsMutationOptions(options));
};

/**
 * @summary Request a presigned URL for file upload
 */
export const getRequestUploadUrlUrl = () => {
  return `/api/storage/uploads/request-url`;
};

export const requestUploadUrl = async (
  requestUploadUrlBody: RequestUploadUrlBody,
  options?: RequestInit,
): Promise<RequestUploadUrlResponse> => {
  return customFetch<RequestUploadUrlResponse>(getRequestUploadUrlUrl(), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(requestUploadUrlBody),
  });
};

export const getRequestUploadUrlMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof requestUploadUrl>>,
    TError,
    { data: BodyType<RequestUploadUrlBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof requestUploadUrl>>,
  TError,
  { data: BodyType<RequestUploadUrlBody> },
  TContext
> => {
  const mutationKey = ["requestUploadUrl"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof requestUploadUrl>>,
    { data: BodyType<RequestUploadUrlBody> }
  > = (props) => {
    const { data } = props ?? {};

    return requestUploadUrl(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type RequestUploadUrlMutationResult = NonNullable<
  Awaited<ReturnType<typeof requestUploadUrl>>
>;
export type RequestUploadUrlMutationBody = BodyType<RequestUploadUrlBody>;
export type RequestUploadUrlMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Request a presigned URL for file upload
 */
export const useRequestUploadUrl = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof requestUploadUrl>>,
    TError,
    { data: BodyType<RequestUploadUrlBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof requestUploadUrl>>,
  TError,
  { data: BodyType<RequestUploadUrlBody> },
  TContext
> => {
  return useMutation(getRequestUploadUrlMutationOptions(options));
};

/**
 * @summary List all services
 */
export const getListServicesUrl = () => {
  return `/api/services`;
};

export const listServices = async (
  options?: RequestInit,
): Promise<Service[]> => {
  return customFetch<Service[]>(getListServicesUrl(), {
    ...options,
    method: "GET",
  });
};

export const getListServicesQueryKey = () => {
  return [`/api/services`] as const;
};

export const getListServicesQueryOptions = <
  TData = Awaited<ReturnType<typeof listServices>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof listServices>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getListServicesQueryKey();

  const queryFn: QueryFunction<Awaited<ReturnType<typeof listServices>>> = ({
    signal,
  }) => listServices({ signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof listServices>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type ListServicesQueryResult = NonNullable<
  Awaited<ReturnType<typeof listServices>>
>;
export type ListServicesQueryError = ErrorType<unknown>;

/**
 * @summary List all services
 */

export function useListServices<
  TData = Awaited<ReturnType<typeof listServices>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof listServices>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getListServicesQueryOptions(options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Create a service (admin)
 */
export const getCreateServiceUrl = () => {
  return `/api/services`;
};

export const createService = async (
  createServiceBody: CreateServiceBody,
  options?: RequestInit,
): Promise<Service> => {
  return customFetch<Service>(getCreateServiceUrl(), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(createServiceBody),
  });
};

export const getCreateServiceMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof createService>>,
    TError,
    { data: BodyType<CreateServiceBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof createService>>,
  TError,
  { data: BodyType<CreateServiceBody> },
  TContext
> => {
  const mutationKey = ["createService"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof createService>>,
    { data: BodyType<CreateServiceBody> }
  > = (props) => {
    const { data } = props ?? {};

    return createService(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type CreateServiceMutationResult = NonNullable<
  Awaited<ReturnType<typeof createService>>
>;
export type CreateServiceMutationBody = BodyType<CreateServiceBody>;
export type CreateServiceMutationError = ErrorType<unknown>;

/**
 * @summary Create a service (admin)
 */
export const useCreateService = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof createService>>,
    TError,
    { data: BodyType<CreateServiceBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof createService>>,
  TError,
  { data: BodyType<CreateServiceBody> },
  TContext
> => {
  return useMutation(getCreateServiceMutationOptions(options));
};

/**
 * @summary Get service by ID
 */
export const getGetServiceUrl = (id: number) => {
  return `/api/services/${id}`;
};

export const getService = async (
  id: number,
  options?: RequestInit,
): Promise<Service> => {
  return customFetch<Service>(getGetServiceUrl(id), {
    ...options,
    method: "GET",
  });
};

export const getGetServiceQueryKey = (id: number) => {
  return [`/api/services/${id}`] as const;
};

export const getGetServiceQueryOptions = <
  TData = Awaited<ReturnType<typeof getService>>,
  TError = ErrorType<unknown>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getService>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getGetServiceQueryKey(id);

  const queryFn: QueryFunction<Awaited<ReturnType<typeof getService>>> = ({
    signal,
  }) => getService(id, { signal, ...requestOptions });

  return {
    queryKey,
    queryFn,
    enabled: !!id,
    ...queryOptions,
  } as UseQueryOptions<
    Awaited<ReturnType<typeof getService>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type GetServiceQueryResult = NonNullable<
  Awaited<ReturnType<typeof getService>>
>;
export type GetServiceQueryError = ErrorType<unknown>;

/**
 * @summary Get service by ID
 */

export function useGetService<
  TData = Awaited<ReturnType<typeof getService>>,
  TError = ErrorType<unknown>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getService>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetServiceQueryOptions(id, options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Update service (admin)
 */
export const getUpdateServiceUrl = (id: number) => {
  return `/api/services/${id}`;
};

export const updateService = async (
  id: number,
  updateServiceBody: UpdateServiceBody,
  options?: RequestInit,
): Promise<Service> => {
  return customFetch<Service>(getUpdateServiceUrl(id), {
    ...options,
    method: "PATCH",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(updateServiceBody),
  });
};

export const getUpdateServiceMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateService>>,
    TError,
    { id: number; data: BodyType<UpdateServiceBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof updateService>>,
  TError,
  { id: number; data: BodyType<UpdateServiceBody> },
  TContext
> => {
  const mutationKey = ["updateService"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof updateService>>,
    { id: number; data: BodyType<UpdateServiceBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return updateService(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type UpdateServiceMutationResult = NonNullable<
  Awaited<ReturnType<typeof updateService>>
>;
export type UpdateServiceMutationBody = BodyType<UpdateServiceBody>;
export type UpdateServiceMutationError = ErrorType<unknown>;

/**
 * @summary Update service (admin)
 */
export const useUpdateService = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateService>>,
    TError,
    { id: number; data: BodyType<UpdateServiceBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof updateService>>,
  TError,
  { id: number; data: BodyType<UpdateServiceBody> },
  TContext
> => {
  return useMutation(getUpdateServiceMutationOptions(options));
};

/**
 * @summary Delete service (admin)
 */
export const getDeleteServiceUrl = (
  id: number,
  params?: DeleteServiceParams,
) => {
  const normalizedParams = new URLSearchParams();

  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined) {
      normalizedParams.append(key, value === null ? "null" : value.toString());
    }
  });

  const stringifiedParams = normalizedParams.toString();

  return stringifiedParams.length > 0
    ? `/api/services/${id}?${stringifiedParams}`
    : `/api/services/${id}`;
};

export const deleteService = async (
  id: number,
  params?: DeleteServiceParams,
  options?: RequestInit,
): Promise<void> => {
  return customFetch<void>(getDeleteServiceUrl(id, params), {
    ...options,
    method: "DELETE",
  });
};

export const getDeleteServiceMutationOptions = <
  TError = ErrorType<ServiceDeletionConflict>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof deleteService>>,
    TError,
    { id: number; params?: DeleteServiceParams },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof deleteService>>,
  TError,
  { id: number; params?: DeleteServiceParams },
  TContext
> => {
  const mutationKey = ["deleteService"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof deleteService>>,
    { id: number; params?: DeleteServiceParams }
  > = (props) => {
    const { id, params } = props ?? {};

    return deleteService(id, params, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type DeleteServiceMutationResult = NonNullable<
  Awaited<ReturnType<typeof deleteService>>
>;

export type DeleteServiceMutationError = ErrorType<ServiceDeletionConflict>;

/**
 * @summary Delete service (admin)
 */
export const useDeleteService = <
  TError = ErrorType<ServiceDeletionConflict>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof deleteService>>,
    TError,
    { id: number; params?: DeleteServiceParams },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof deleteService>>,
  TError,
  { id: number; params?: DeleteServiceParams },
  TContext
> => {
  return useMutation(getDeleteServiceMutationOptions(options));
};

/**
 * @summary Place a service order
 */
export const getCreateServiceOrderUrl = () => {
  return `/api/orders`;
};

export const createServiceOrder = async (
  createServiceOrderBody: CreateServiceOrderBody,
  options?: RequestInit,
): Promise<ServiceOrder> => {
  return customFetch<ServiceOrder>(getCreateServiceOrderUrl(), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(createServiceOrderBody),
  });
};

export const getCreateServiceOrderMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof createServiceOrder>>,
    TError,
    { data: BodyType<CreateServiceOrderBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof createServiceOrder>>,
  TError,
  { data: BodyType<CreateServiceOrderBody> },
  TContext
> => {
  const mutationKey = ["createServiceOrder"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof createServiceOrder>>,
    { data: BodyType<CreateServiceOrderBody> }
  > = (props) => {
    const { data } = props ?? {};

    return createServiceOrder(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type CreateServiceOrderMutationResult = NonNullable<
  Awaited<ReturnType<typeof createServiceOrder>>
>;
export type CreateServiceOrderMutationBody = BodyType<CreateServiceOrderBody>;
export type CreateServiceOrderMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Place a service order
 */
export const useCreateServiceOrder = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof createServiceOrder>>,
    TError,
    { data: BodyType<CreateServiceOrderBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof createServiceOrder>>,
  TError,
  { data: BodyType<CreateServiceOrderBody> },
  TContext
> => {
  return useMutation(getCreateServiceOrderMutationOptions(options));
};

/**
 * @summary List orders placed by the current user
 */
export const getListMyServiceOrdersUrl = () => {
  return `/api/orders/my`;
};

export const listMyServiceOrders = async (
  options?: RequestInit,
): Promise<ServiceOrder[]> => {
  return customFetch<ServiceOrder[]>(getListMyServiceOrdersUrl(), {
    ...options,
    method: "GET",
  });
};

export const getListMyServiceOrdersQueryKey = () => {
  return [`/api/orders/my`] as const;
};

export const getListMyServiceOrdersQueryOptions = <
  TData = Awaited<ReturnType<typeof listMyServiceOrders>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof listMyServiceOrders>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getListMyServiceOrdersQueryKey();

  const queryFn: QueryFunction<
    Awaited<ReturnType<typeof listMyServiceOrders>>
  > = ({ signal }) => listMyServiceOrders({ signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof listMyServiceOrders>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type ListMyServiceOrdersQueryResult = NonNullable<
  Awaited<ReturnType<typeof listMyServiceOrders>>
>;
export type ListMyServiceOrdersQueryError = ErrorType<unknown>;

/**
 * @summary List orders placed by the current user
 */

export function useListMyServiceOrders<
  TData = Awaited<ReturnType<typeof listMyServiceOrders>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof listMyServiceOrders>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getListMyServiceOrdersQueryOptions(options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary List active orders for receivers (requires orders.receive permission). Returns pending+unassigned orders plus orders assigned to me that are not completed/cancelled.
 */
export const getListIncomingServiceOrdersUrl = () => {
  return `/api/orders/incoming`;
};

export const listIncomingServiceOrders = async (
  options?: RequestInit,
): Promise<ServiceOrder[]> => {
  return customFetch<ServiceOrder[]>(getListIncomingServiceOrdersUrl(), {
    ...options,
    method: "GET",
  });
};

export const getListIncomingServiceOrdersQueryKey = () => {
  return [`/api/orders/incoming`] as const;
};

export const getListIncomingServiceOrdersQueryOptions = <
  TData = Awaited<ReturnType<typeof listIncomingServiceOrders>>,
  TError = ErrorType<ErrorResponse>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof listIncomingServiceOrders>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey =
    queryOptions?.queryKey ?? getListIncomingServiceOrdersQueryKey();

  const queryFn: QueryFunction<
    Awaited<ReturnType<typeof listIncomingServiceOrders>>
  > = ({ signal }) => listIncomingServiceOrders({ signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof listIncomingServiceOrders>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type ListIncomingServiceOrdersQueryResult = NonNullable<
  Awaited<ReturnType<typeof listIncomingServiceOrders>>
>;
export type ListIncomingServiceOrdersQueryError = ErrorType<ErrorResponse>;

/**
 * @summary List active orders for receivers (requires orders.receive permission). Returns pending+unassigned orders plus orders assigned to me that are not completed/cancelled.
 */

export function useListIncomingServiceOrders<
  TData = Awaited<ReturnType<typeof listIncomingServiceOrders>>,
  TError = ErrorType<ErrorResponse>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof listIncomingServiceOrders>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getListIncomingServiceOrdersQueryOptions(options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Update order status. Preparing/completed restricted to assigned receiver or admin; cancelled allowed for owner (while pending|received) or admin (any time); pending/received are restore-from-cancelled transitions allowed for owner or admin.
 */
export const getUpdateServiceOrderStatusUrl = (id: number) => {
  return `/api/orders/${id}/status`;
};

export const updateServiceOrderStatus = async (
  id: number,
  updateServiceOrderStatusBody: UpdateServiceOrderStatusBody,
  options?: RequestInit,
): Promise<ServiceOrder> => {
  return customFetch<ServiceOrder>(getUpdateServiceOrderStatusUrl(id), {
    ...options,
    method: "PATCH",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(updateServiceOrderStatusBody),
  });
};

export const getUpdateServiceOrderStatusMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateServiceOrderStatus>>,
    TError,
    { id: number; data: BodyType<UpdateServiceOrderStatusBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof updateServiceOrderStatus>>,
  TError,
  { id: number; data: BodyType<UpdateServiceOrderStatusBody> },
  TContext
> => {
  const mutationKey = ["updateServiceOrderStatus"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof updateServiceOrderStatus>>,
    { id: number; data: BodyType<UpdateServiceOrderStatusBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return updateServiceOrderStatus(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type UpdateServiceOrderStatusMutationResult = NonNullable<
  Awaited<ReturnType<typeof updateServiceOrderStatus>>
>;
export type UpdateServiceOrderStatusMutationBody =
  BodyType<UpdateServiceOrderStatusBody>;
export type UpdateServiceOrderStatusMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Update order status. Preparing/completed restricted to assigned receiver or admin; cancelled allowed for owner (while pending|received) or admin (any time); pending/received are restore-from-cancelled transitions allowed for owner or admin.
 */
export const useUpdateServiceOrderStatus = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateServiceOrderStatus>>,
    TError,
    { id: number; data: BodyType<UpdateServiceOrderStatusBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof updateServiceOrderStatus>>,
  TError,
  { id: number; data: BodyType<UpdateServiceOrderStatusBody> },
  TContext
> => {
  return useMutation(getUpdateServiceOrderStatusMutationOptions(options));
};

/**
 * @summary Receiver atomically claims a pending order (sets status=received, assigned_to=current user). Requires orders.receive permission. Returns 409 already_claimed if the order is no longer pending+unassigned.
 */
export const getConfirmServiceOrderReceiptUrl = (id: number) => {
  return `/api/orders/${id}/confirm-receipt`;
};

export const confirmServiceOrderReceipt = async (
  id: number,
  options?: RequestInit,
): Promise<ServiceOrder> => {
  return customFetch<ServiceOrder>(getConfirmServiceOrderReceiptUrl(id), {
    ...options,
    method: "PATCH",
  });
};

export const getConfirmServiceOrderReceiptMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof confirmServiceOrderReceipt>>,
    TError,
    { id: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof confirmServiceOrderReceipt>>,
  TError,
  { id: number },
  TContext
> => {
  const mutationKey = ["confirmServiceOrderReceipt"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof confirmServiceOrderReceipt>>,
    { id: number }
  > = (props) => {
    const { id } = props ?? {};

    return confirmServiceOrderReceipt(id, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type ConfirmServiceOrderReceiptMutationResult = NonNullable<
  Awaited<ReturnType<typeof confirmServiceOrderReceipt>>
>;

export type ConfirmServiceOrderReceiptMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Receiver atomically claims a pending order (sets status=received, assigned_to=current user). Requires orders.receive permission. Returns 409 already_claimed if the order is no longer pending+unassigned.
 */
export const useConfirmServiceOrderReceipt = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof confirmServiceOrderReceipt>>,
    TError,
    { id: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof confirmServiceOrderReceipt>>,
  TError,
  { id: number },
  TContext
> => {
  return useMutation(getConfirmServiceOrderReceiptMutationOptions(options));
};

/**
 * @summary List service categories
 */
export const getListServiceCategoriesUrl = () => {
  return `/api/service-categories`;
};

export const listServiceCategories = async (
  options?: RequestInit,
): Promise<ServiceCategory[]> => {
  return customFetch<ServiceCategory[]>(getListServiceCategoriesUrl(), {
    ...options,
    method: "GET",
  });
};

export const getListServiceCategoriesQueryKey = () => {
  return [`/api/service-categories`] as const;
};

export const getListServiceCategoriesQueryOptions = <
  TData = Awaited<ReturnType<typeof listServiceCategories>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof listServiceCategories>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getListServiceCategoriesQueryKey();

  const queryFn: QueryFunction<
    Awaited<ReturnType<typeof listServiceCategories>>
  > = ({ signal }) => listServiceCategories({ signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof listServiceCategories>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type ListServiceCategoriesQueryResult = NonNullable<
  Awaited<ReturnType<typeof listServiceCategories>>
>;
export type ListServiceCategoriesQueryError = ErrorType<unknown>;

/**
 * @summary List service categories
 */

export function useListServiceCategories<
  TData = Awaited<ReturnType<typeof listServiceCategories>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof listServiceCategories>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getListServiceCategoriesQueryOptions(options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary List conversations for current user
 */
export const getListConversationsUrl = () => {
  return `/api/conversations`;
};

export const listConversations = async (
  options?: RequestInit,
): Promise<ConversationWithDetails[]> => {
  return customFetch<ConversationWithDetails[]>(getListConversationsUrl(), {
    ...options,
    method: "GET",
  });
};

export const getListConversationsQueryKey = () => {
  return [`/api/conversations`] as const;
};

export const getListConversationsQueryOptions = <
  TData = Awaited<ReturnType<typeof listConversations>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof listConversations>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getListConversationsQueryKey();

  const queryFn: QueryFunction<
    Awaited<ReturnType<typeof listConversations>>
  > = ({ signal }) => listConversations({ signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof listConversations>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type ListConversationsQueryResult = NonNullable<
  Awaited<ReturnType<typeof listConversations>>
>;
export type ListConversationsQueryError = ErrorType<unknown>;

/**
 * @summary List conversations for current user
 */

export function useListConversations<
  TData = Awaited<ReturnType<typeof listConversations>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof listConversations>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getListConversationsQueryOptions(options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Create a conversation
 */
export const getCreateConversationUrl = () => {
  return `/api/conversations`;
};

export const createConversation = async (
  createConversationBody: CreateConversationBody,
  options?: RequestInit,
): Promise<ConversationWithDetails> => {
  return customFetch<ConversationWithDetails>(getCreateConversationUrl(), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(createConversationBody),
  });
};

export const getCreateConversationMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof createConversation>>,
    TError,
    { data: BodyType<CreateConversationBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof createConversation>>,
  TError,
  { data: BodyType<CreateConversationBody> },
  TContext
> => {
  const mutationKey = ["createConversation"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof createConversation>>,
    { data: BodyType<CreateConversationBody> }
  > = (props) => {
    const { data } = props ?? {};

    return createConversation(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type CreateConversationMutationResult = NonNullable<
  Awaited<ReturnType<typeof createConversation>>
>;
export type CreateConversationMutationBody = BodyType<CreateConversationBody>;
export type CreateConversationMutationError = ErrorType<unknown>;

/**
 * @summary Create a conversation
 */
export const useCreateConversation = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof createConversation>>,
    TError,
    { data: BodyType<CreateConversationBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof createConversation>>,
  TError,
  { data: BodyType<CreateConversationBody> },
  TContext
> => {
  return useMutation(getCreateConversationMutationOptions(options));
};

/**
 * @summary Get conversation by ID
 */
export const getGetConversationUrl = (id: number) => {
  return `/api/conversations/${id}`;
};

export const getConversation = async (
  id: number,
  options?: RequestInit,
): Promise<ConversationWithDetails> => {
  return customFetch<ConversationWithDetails>(getGetConversationUrl(id), {
    ...options,
    method: "GET",
  });
};

export const getGetConversationQueryKey = (id: number) => {
  return [`/api/conversations/${id}`] as const;
};

export const getGetConversationQueryOptions = <
  TData = Awaited<ReturnType<typeof getConversation>>,
  TError = ErrorType<unknown>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getConversation>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getGetConversationQueryKey(id);

  const queryFn: QueryFunction<Awaited<ReturnType<typeof getConversation>>> = ({
    signal,
  }) => getConversation(id, { signal, ...requestOptions });

  return {
    queryKey,
    queryFn,
    enabled: !!id,
    ...queryOptions,
  } as UseQueryOptions<
    Awaited<ReturnType<typeof getConversation>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type GetConversationQueryResult = NonNullable<
  Awaited<ReturnType<typeof getConversation>>
>;
export type GetConversationQueryError = ErrorType<unknown>;

/**
 * @summary Get conversation by ID
 */

export function useGetConversation<
  TData = Awaited<ReturnType<typeof getConversation>>,
  TError = ErrorType<unknown>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getConversation>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetConversationQueryOptions(id, options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Update conversation properties (admin only)
 */
export const getUpdateConversationUrl = (id: number) => {
  return `/api/conversations/${id}`;
};

export const updateConversation = async (
  id: number,
  updateConversationBody: UpdateConversationBody,
  options?: RequestInit,
): Promise<ConversationWithDetails> => {
  return customFetch<ConversationWithDetails>(getUpdateConversationUrl(id), {
    ...options,
    method: "PATCH",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(updateConversationBody),
  });
};

export const getUpdateConversationMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateConversation>>,
    TError,
    { id: number; data: BodyType<UpdateConversationBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof updateConversation>>,
  TError,
  { id: number; data: BodyType<UpdateConversationBody> },
  TContext
> => {
  const mutationKey = ["updateConversation"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof updateConversation>>,
    { id: number; data: BodyType<UpdateConversationBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return updateConversation(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type UpdateConversationMutationResult = NonNullable<
  Awaited<ReturnType<typeof updateConversation>>
>;
export type UpdateConversationMutationBody = BodyType<UpdateConversationBody>;
export type UpdateConversationMutationError = ErrorType<unknown>;

/**
 * @summary Update conversation properties (admin only)
 */
export const useUpdateConversation = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateConversation>>,
    TError,
    { id: number; data: BodyType<UpdateConversationBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof updateConversation>>,
  TError,
  { id: number; data: BodyType<UpdateConversationBody> },
  TContext
> => {
  return useMutation(getUpdateConversationMutationOptions(options));
};

/**
 * @summary Add participants to a group conversation (admin only)
 */
export const getAddConversationParticipantsUrl = (id: number) => {
  return `/api/conversations/${id}/participants`;
};

export const addConversationParticipants = async (
  id: number,
  addParticipantsBody: AddParticipantsBody,
  options?: RequestInit,
): Promise<ConversationWithDetails> => {
  return customFetch<ConversationWithDetails>(
    getAddConversationParticipantsUrl(id),
    {
      ...options,
      method: "POST",
      headers: { "Content-Type": "application/json", ...options?.headers },
      body: JSON.stringify(addParticipantsBody),
    },
  );
};

export const getAddConversationParticipantsMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof addConversationParticipants>>,
    TError,
    { id: number; data: BodyType<AddParticipantsBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof addConversationParticipants>>,
  TError,
  { id: number; data: BodyType<AddParticipantsBody> },
  TContext
> => {
  const mutationKey = ["addConversationParticipants"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof addConversationParticipants>>,
    { id: number; data: BodyType<AddParticipantsBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return addConversationParticipants(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type AddConversationParticipantsMutationResult = NonNullable<
  Awaited<ReturnType<typeof addConversationParticipants>>
>;
export type AddConversationParticipantsMutationBody =
  BodyType<AddParticipantsBody>;
export type AddConversationParticipantsMutationError = ErrorType<unknown>;

/**
 * @summary Add participants to a group conversation (admin only)
 */
export const useAddConversationParticipants = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof addConversationParticipants>>,
    TError,
    { id: number; data: BodyType<AddParticipantsBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof addConversationParticipants>>,
  TError,
  { id: number; data: BodyType<AddParticipantsBody> },
  TContext
> => {
  return useMutation(getAddConversationParticipantsMutationOptions(options));
};

/**
 * @summary Remove a participant from a group conversation (admin only)
 */
export const getRemoveConversationParticipantUrl = (
  id: number,
  userId: number,
) => {
  return `/api/conversations/${id}/participants/${userId}`;
};

export const removeConversationParticipant = async (
  id: number,
  userId: number,
  options?: RequestInit,
): Promise<ConversationWithDetails> => {
  return customFetch<ConversationWithDetails>(
    getRemoveConversationParticipantUrl(id, userId),
    {
      ...options,
      method: "DELETE",
    },
  );
};

export const getRemoveConversationParticipantMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof removeConversationParticipant>>,
    TError,
    { id: number; userId: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof removeConversationParticipant>>,
  TError,
  { id: number; userId: number },
  TContext
> => {
  const mutationKey = ["removeConversationParticipant"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof removeConversationParticipant>>,
    { id: number; userId: number }
  > = (props) => {
    const { id, userId } = props ?? {};

    return removeConversationParticipant(id, userId, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type RemoveConversationParticipantMutationResult = NonNullable<
  Awaited<ReturnType<typeof removeConversationParticipant>>
>;

export type RemoveConversationParticipantMutationError = ErrorType<unknown>;

/**
 * @summary Remove a participant from a group conversation (admin only)
 */
export const useRemoveConversationParticipant = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof removeConversationParticipant>>,
    TError,
    { id: number; userId: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof removeConversationParticipant>>,
  TError,
  { id: number; userId: number },
  TContext
> => {
  return useMutation(getRemoveConversationParticipantMutationOptions(options));
};

/**
 * @summary Update per-user state (mute, archive)
 */
export const getUpdateConversationStateUrl = (id: number) => {
  return `/api/conversations/${id}/state`;
};

export const updateConversationState = async (
  id: number,
  updateConversationStateBody: UpdateConversationStateBody,
  options?: RequestInit,
): Promise<ConversationWithDetails> => {
  return customFetch<ConversationWithDetails>(
    getUpdateConversationStateUrl(id),
    {
      ...options,
      method: "PATCH",
      headers: { "Content-Type": "application/json", ...options?.headers },
      body: JSON.stringify(updateConversationStateBody),
    },
  );
};

export const getUpdateConversationStateMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateConversationState>>,
    TError,
    { id: number; data: BodyType<UpdateConversationStateBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof updateConversationState>>,
  TError,
  { id: number; data: BodyType<UpdateConversationStateBody> },
  TContext
> => {
  const mutationKey = ["updateConversationState"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof updateConversationState>>,
    { id: number; data: BodyType<UpdateConversationStateBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return updateConversationState(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type UpdateConversationStateMutationResult = NonNullable<
  Awaited<ReturnType<typeof updateConversationState>>
>;
export type UpdateConversationStateMutationBody =
  BodyType<UpdateConversationStateBody>;
export type UpdateConversationStateMutationError = ErrorType<unknown>;

/**
 * @summary Update per-user state (mute, archive)
 */
export const useUpdateConversationState = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateConversationState>>,
    TError,
    { id: number; data: BodyType<UpdateConversationStateBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof updateConversationState>>,
  TError,
  { id: number; data: BodyType<UpdateConversationStateBody> },
  TContext
> => {
  return useMutation(getUpdateConversationStateMutationOptions(options));
};

/**
 * @summary Leave a group conversation
 */
export const getLeaveConversationUrl = (id: number) => {
  return `/api/conversations/${id}/leave`;
};

export const leaveConversation = async (
  id: number,
  leaveConversationBody?: LeaveConversationBody,
  options?: RequestInit,
): Promise<SuccessResponse> => {
  return customFetch<SuccessResponse>(getLeaveConversationUrl(id), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(leaveConversationBody),
  });
};

export const getLeaveConversationMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof leaveConversation>>,
    TError,
    { id: number; data: BodyType<LeaveConversationBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof leaveConversation>>,
  TError,
  { id: number; data: BodyType<LeaveConversationBody> },
  TContext
> => {
  const mutationKey = ["leaveConversation"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof leaveConversation>>,
    { id: number; data: BodyType<LeaveConversationBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return leaveConversation(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type LeaveConversationMutationResult = NonNullable<
  Awaited<ReturnType<typeof leaveConversation>>
>;
export type LeaveConversationMutationBody = BodyType<LeaveConversationBody>;
export type LeaveConversationMutationError = ErrorType<unknown>;

/**
 * @summary Leave a group conversation
 */
export const useLeaveConversation = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof leaveConversation>>,
    TError,
    { id: number; data: BodyType<LeaveConversationBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof leaveConversation>>,
  TError,
  { id: number; data: BodyType<LeaveConversationBody> },
  TContext
> => {
  return useMutation(getLeaveConversationMutationOptions(options));
};

/**
 * @summary List messages in a conversation
 */
export const getListMessagesUrl = (id: number) => {
  return `/api/conversations/${id}/messages`;
};

export const listMessages = async (
  id: number,
  options?: RequestInit,
): Promise<MessageWithSender[]> => {
  return customFetch<MessageWithSender[]>(getListMessagesUrl(id), {
    ...options,
    method: "GET",
  });
};

export const getListMessagesQueryKey = (id: number) => {
  return [`/api/conversations/${id}/messages`] as const;
};

export const getListMessagesQueryOptions = <
  TData = Awaited<ReturnType<typeof listMessages>>,
  TError = ErrorType<unknown>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof listMessages>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getListMessagesQueryKey(id);

  const queryFn: QueryFunction<Awaited<ReturnType<typeof listMessages>>> = ({
    signal,
  }) => listMessages(id, { signal, ...requestOptions });

  return {
    queryKey,
    queryFn,
    enabled: !!id,
    ...queryOptions,
  } as UseQueryOptions<
    Awaited<ReturnType<typeof listMessages>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type ListMessagesQueryResult = NonNullable<
  Awaited<ReturnType<typeof listMessages>>
>;
export type ListMessagesQueryError = ErrorType<unknown>;

/**
 * @summary List messages in a conversation
 */

export function useListMessages<
  TData = Awaited<ReturnType<typeof listMessages>>,
  TError = ErrorType<unknown>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof listMessages>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getListMessagesQueryOptions(id, options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Send a message
 */
export const getSendMessageUrl = (id: number) => {
  return `/api/conversations/${id}/messages`;
};

export const sendMessage = async (
  id: number,
  sendMessageBody: SendMessageBody,
  options?: RequestInit,
): Promise<MessageWithSender> => {
  return customFetch<MessageWithSender>(getSendMessageUrl(id), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(sendMessageBody),
  });
};

export const getSendMessageMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof sendMessage>>,
    TError,
    { id: number; data: BodyType<SendMessageBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof sendMessage>>,
  TError,
  { id: number; data: BodyType<SendMessageBody> },
  TContext
> => {
  const mutationKey = ["sendMessage"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof sendMessage>>,
    { id: number; data: BodyType<SendMessageBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return sendMessage(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type SendMessageMutationResult = NonNullable<
  Awaited<ReturnType<typeof sendMessage>>
>;
export type SendMessageMutationBody = BodyType<SendMessageBody>;
export type SendMessageMutationError = ErrorType<unknown>;

/**
 * @summary Send a message
 */
export const useSendMessage = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof sendMessage>>,
    TError,
    { id: number; data: BodyType<SendMessageBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof sendMessage>>,
  TError,
  { id: number; data: BodyType<SendMessageBody> },
  TContext
> => {
  return useMutation(getSendMessageMutationOptions(options));
};

/**
 * @summary Mark all messages in conversation as read
 */
export const getMarkConversationReadUrl = (id: number) => {
  return `/api/conversations/${id}/read`;
};

export const markConversationRead = async (
  id: number,
  options?: RequestInit,
): Promise<SuccessResponse> => {
  return customFetch<SuccessResponse>(getMarkConversationReadUrl(id), {
    ...options,
    method: "POST",
  });
};

export const getMarkConversationReadMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof markConversationRead>>,
    TError,
    { id: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof markConversationRead>>,
  TError,
  { id: number },
  TContext
> => {
  const mutationKey = ["markConversationRead"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof markConversationRead>>,
    { id: number }
  > = (props) => {
    const { id } = props ?? {};

    return markConversationRead(id, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type MarkConversationReadMutationResult = NonNullable<
  Awaited<ReturnType<typeof markConversationRead>>
>;

export type MarkConversationReadMutationError = ErrorType<unknown>;

/**
 * @summary Mark all messages in conversation as read
 */
export const useMarkConversationRead = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof markConversationRead>>,
    TError,
    { id: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof markConversationRead>>,
  TError,
  { id: number },
  TContext
> => {
  return useMutation(getMarkConversationReadMutationOptions(options));
};

/**
 * @summary List notifications for current user
 */
export const getListNotificationsUrl = () => {
  return `/api/notifications`;
};

export const listNotifications = async (
  options?: RequestInit,
): Promise<Notification[]> => {
  return customFetch<Notification[]>(getListNotificationsUrl(), {
    ...options,
    method: "GET",
  });
};

export const getListNotificationsQueryKey = () => {
  return [`/api/notifications`] as const;
};

export const getListNotificationsQueryOptions = <
  TData = Awaited<ReturnType<typeof listNotifications>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof listNotifications>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getListNotificationsQueryKey();

  const queryFn: QueryFunction<
    Awaited<ReturnType<typeof listNotifications>>
  > = ({ signal }) => listNotifications({ signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof listNotifications>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type ListNotificationsQueryResult = NonNullable<
  Awaited<ReturnType<typeof listNotifications>>
>;
export type ListNotificationsQueryError = ErrorType<unknown>;

/**
 * @summary List notifications for current user
 */

export function useListNotifications<
  TData = Awaited<ReturnType<typeof listNotifications>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof listNotifications>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getListNotificationsQueryOptions(options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Mark notification as read
 */
export const getMarkNotificationReadUrl = (id: number) => {
  return `/api/notifications/${id}/read`;
};

export const markNotificationRead = async (
  id: number,
  options?: RequestInit,
): Promise<Notification> => {
  return customFetch<Notification>(getMarkNotificationReadUrl(id), {
    ...options,
    method: "PATCH",
  });
};

export const getMarkNotificationReadMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof markNotificationRead>>,
    TError,
    { id: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof markNotificationRead>>,
  TError,
  { id: number },
  TContext
> => {
  const mutationKey = ["markNotificationRead"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof markNotificationRead>>,
    { id: number }
  > = (props) => {
    const { id } = props ?? {};

    return markNotificationRead(id, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type MarkNotificationReadMutationResult = NonNullable<
  Awaited<ReturnType<typeof markNotificationRead>>
>;

export type MarkNotificationReadMutationError = ErrorType<unknown>;

/**
 * @summary Mark notification as read
 */
export const useMarkNotificationRead = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof markNotificationRead>>,
    TError,
    { id: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof markNotificationRead>>,
  TError,
  { id: number },
  TContext
> => {
  return useMutation(getMarkNotificationReadMutationOptions(options));
};

/**
 * @summary Mark all notifications as read
 */
export const getMarkAllNotificationsReadUrl = () => {
  return `/api/notifications/read-all`;
};

export const markAllNotificationsRead = async (
  options?: RequestInit,
): Promise<SuccessResponse> => {
  return customFetch<SuccessResponse>(getMarkAllNotificationsReadUrl(), {
    ...options,
    method: "POST",
  });
};

export const getMarkAllNotificationsReadMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof markAllNotificationsRead>>,
    TError,
    void,
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof markAllNotificationsRead>>,
  TError,
  void,
  TContext
> => {
  const mutationKey = ["markAllNotificationsRead"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof markAllNotificationsRead>>,
    void
  > = () => {
    return markAllNotificationsRead(requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type MarkAllNotificationsReadMutationResult = NonNullable<
  Awaited<ReturnType<typeof markAllNotificationsRead>>
>;

export type MarkAllNotificationsReadMutationError = ErrorType<unknown>;

/**
 * @summary Mark all notifications as read
 */
export const useMarkAllNotificationsRead = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof markAllNotificationsRead>>,
    TError,
    void,
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof markAllNotificationsRead>>,
  TError,
  void,
  TContext
> => {
  return useMutation(getMarkAllNotificationsReadMutationOptions(options));
};

/**
 * @summary List all users (admin)
 */
export const getListUsersUrl = (params?: ListUsersParams) => {
  const normalizedParams = new URLSearchParams();

  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined) {
      normalizedParams.append(key, value === null ? "null" : value.toString());
    }
  });

  const stringifiedParams = normalizedParams.toString();

  return stringifiedParams.length > 0
    ? `/api/users?${stringifiedParams}`
    : `/api/users`;
};

export const listUsers = async (
  params?: ListUsersParams,
  options?: RequestInit,
): Promise<UserProfile[]> => {
  return customFetch<UserProfile[]>(getListUsersUrl(params), {
    ...options,
    method: "GET",
  });
};

export const getListUsersQueryKey = (params?: ListUsersParams) => {
  return [`/api/users`, ...(params ? [params] : [])] as const;
};

export const getListUsersQueryOptions = <
  TData = Awaited<ReturnType<typeof listUsers>>,
  TError = ErrorType<unknown>,
>(
  params?: ListUsersParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof listUsers>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getListUsersQueryKey(params);

  const queryFn: QueryFunction<Awaited<ReturnType<typeof listUsers>>> = ({
    signal,
  }) => listUsers(params, { signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof listUsers>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type ListUsersQueryResult = NonNullable<
  Awaited<ReturnType<typeof listUsers>>
>;
export type ListUsersQueryError = ErrorType<unknown>;

/**
 * @summary List all users (admin)
 */

export function useListUsers<
  TData = Awaited<ReturnType<typeof listUsers>>,
  TError = ErrorType<unknown>,
>(
  params?: ListUsersParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof listUsers>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getListUsersQueryOptions(params, options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Create a user (admin)
 */
export const getCreateUserUrl = () => {
  return `/api/users`;
};

export const createUser = async (
  createUserBody: CreateUserBody,
  options?: RequestInit,
): Promise<UserProfile> => {
  return customFetch<UserProfile>(getCreateUserUrl(), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(createUserBody),
  });
};

export const getCreateUserMutationOptions = <
  TError = ErrorType<void>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof createUser>>,
    TError,
    { data: BodyType<CreateUserBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof createUser>>,
  TError,
  { data: BodyType<CreateUserBody> },
  TContext
> => {
  const mutationKey = ["createUser"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof createUser>>,
    { data: BodyType<CreateUserBody> }
  > = (props) => {
    const { data } = props ?? {};

    return createUser(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type CreateUserMutationResult = NonNullable<
  Awaited<ReturnType<typeof createUser>>
>;
export type CreateUserMutationBody = BodyType<CreateUserBody>;
export type CreateUserMutationError = ErrorType<void>;

/**
 * @summary Create a user (admin)
 */
export const useCreateUser = <
  TError = ErrorType<void>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof createUser>>,
    TError,
    { data: BodyType<CreateUserBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof createUser>>,
  TError,
  { data: BodyType<CreateUserBody> },
  TContext
> => {
  return useMutation(getCreateUserMutationOptions(options));
};

/**
 * @summary Get user by ID (admin)
 */
export const getGetUserUrl = (id: number) => {
  return `/api/users/${id}`;
};

export const getUser = async (
  id: number,
  options?: RequestInit,
): Promise<UserProfile> => {
  return customFetch<UserProfile>(getGetUserUrl(id), {
    ...options,
    method: "GET",
  });
};

export const getGetUserQueryKey = (id: number) => {
  return [`/api/users/${id}`] as const;
};

export const getGetUserQueryOptions = <
  TData = Awaited<ReturnType<typeof getUser>>,
  TError = ErrorType<unknown>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<Awaited<ReturnType<typeof getUser>>, TError, TData>;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getGetUserQueryKey(id);

  const queryFn: QueryFunction<Awaited<ReturnType<typeof getUser>>> = ({
    signal,
  }) => getUser(id, { signal, ...requestOptions });

  return {
    queryKey,
    queryFn,
    enabled: !!id,
    ...queryOptions,
  } as UseQueryOptions<Awaited<ReturnType<typeof getUser>>, TError, TData> & {
    queryKey: QueryKey;
  };
};

export type GetUserQueryResult = NonNullable<
  Awaited<ReturnType<typeof getUser>>
>;
export type GetUserQueryError = ErrorType<unknown>;

/**
 * @summary Get user by ID (admin)
 */

export function useGetUser<
  TData = Awaited<ReturnType<typeof getUser>>,
  TError = ErrorType<unknown>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<Awaited<ReturnType<typeof getUser>>, TError, TData>;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetUserQueryOptions(id, options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Update user (admin)
 */
export const getUpdateUserUrl = (id: number) => {
  return `/api/users/${id}`;
};

export const updateUser = async (
  id: number,
  updateUserBody: UpdateUserBody,
  options?: RequestInit,
): Promise<UserProfile> => {
  return customFetch<UserProfile>(getUpdateUserUrl(id), {
    ...options,
    method: "PATCH",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(updateUserBody),
  });
};

export const getUpdateUserMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateUser>>,
    TError,
    { id: number; data: BodyType<UpdateUserBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof updateUser>>,
  TError,
  { id: number; data: BodyType<UpdateUserBody> },
  TContext
> => {
  const mutationKey = ["updateUser"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof updateUser>>,
    { id: number; data: BodyType<UpdateUserBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return updateUser(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type UpdateUserMutationResult = NonNullable<
  Awaited<ReturnType<typeof updateUser>>
>;
export type UpdateUserMutationBody = BodyType<UpdateUserBody>;
export type UpdateUserMutationError = ErrorType<unknown>;

/**
 * @summary Update user (admin)
 */
export const useUpdateUser = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateUser>>,
    TError,
    { id: number; data: BodyType<UpdateUserBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof updateUser>>,
  TError,
  { id: number; data: BodyType<UpdateUserBody> },
  TContext
> => {
  return useMutation(getUpdateUserMutationOptions(options));
};

/**
 * @summary Delete user (admin)
 */
export const getDeleteUserUrl = (id: number, params?: DeleteUserParams) => {
  const normalizedParams = new URLSearchParams();

  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined) {
      normalizedParams.append(key, value === null ? "null" : value.toString());
    }
  });

  const stringifiedParams = normalizedParams.toString();

  return stringifiedParams.length > 0
    ? `/api/users/${id}?${stringifiedParams}`
    : `/api/users/${id}`;
};

export const deleteUser = async (
  id: number,
  params?: DeleteUserParams,
  options?: RequestInit,
): Promise<void> => {
  return customFetch<void>(getDeleteUserUrl(id, params), {
    ...options,
    method: "DELETE",
  });
};

export const getDeleteUserMutationOptions = <
  TError = ErrorType<UserDeletionConflict>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof deleteUser>>,
    TError,
    { id: number; params?: DeleteUserParams },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof deleteUser>>,
  TError,
  { id: number; params?: DeleteUserParams },
  TContext
> => {
  const mutationKey = ["deleteUser"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof deleteUser>>,
    { id: number; params?: DeleteUserParams }
  > = (props) => {
    const { id, params } = props ?? {};

    return deleteUser(id, params, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type DeleteUserMutationResult = NonNullable<
  Awaited<ReturnType<typeof deleteUser>>
>;

export type DeleteUserMutationError = ErrorType<UserDeletionConflict>;

/**
 * @summary Delete user (admin)
 */
export const useDeleteUser = <
  TError = ErrorType<UserDeletionConflict>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof deleteUser>>,
    TError,
    { id: number; params?: DeleteUserParams },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof deleteUser>>,
  TError,
  { id: number; params?: DeleteUserParams },
  TContext
> => {
  return useMutation(getDeleteUserMutationOptions(options));
};

/**
 * Permanently deletes the order. Allowed when the caller owns the order
AND it is in a terminal status (completed/cancelled), OR the caller is
an admin (any status).

 * @summary Delete a service order
 */
export const getDeleteServiceOrderUrl = (id: number) => {
  return `/api/orders/${id}`;
};

export const deleteServiceOrder = async (
  id: number,
  options?: RequestInit,
): Promise<void> => {
  return customFetch<void>(getDeleteServiceOrderUrl(id), {
    ...options,
    method: "DELETE",
  });
};

export const getDeleteServiceOrderMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof deleteServiceOrder>>,
    TError,
    { id: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof deleteServiceOrder>>,
  TError,
  { id: number },
  TContext
> => {
  const mutationKey = ["deleteServiceOrder"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof deleteServiceOrder>>,
    { id: number }
  > = (props) => {
    const { id } = props ?? {};

    return deleteServiceOrder(id, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type DeleteServiceOrderMutationResult = NonNullable<
  Awaited<ReturnType<typeof deleteServiceOrder>>
>;

export type DeleteServiceOrderMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Delete a service order
 */
export const useDeleteServiceOrder = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof deleteServiceOrder>>,
    TError,
    { id: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof deleteServiceOrder>>,
  TError,
  { id: number },
  TContext
> => {
  return useMutation(getDeleteServiceOrderMutationOptions(options));
};

/**
 * @summary Idempotently add a role to a user (admin)
 */
export const getAddUserRoleUrl = (id: number) => {
  return `/api/users/${id}/roles`;
};

export const addUserRole = async (
  id: number,
  addUserRoleBody: AddUserRoleBody,
  options?: RequestInit,
): Promise<UserProfile> => {
  return customFetch<UserProfile>(getAddUserRoleUrl(id), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(addUserRoleBody),
  });
};

export const getAddUserRoleMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof addUserRole>>,
    TError,
    { id: number; data: BodyType<AddUserRoleBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof addUserRole>>,
  TError,
  { id: number; data: BodyType<AddUserRoleBody> },
  TContext
> => {
  const mutationKey = ["addUserRole"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof addUserRole>>,
    { id: number; data: BodyType<AddUserRoleBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return addUserRole(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type AddUserRoleMutationResult = NonNullable<
  Awaited<ReturnType<typeof addUserRole>>
>;
export type AddUserRoleMutationBody = BodyType<AddUserRoleBody>;
export type AddUserRoleMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Idempotently add a role to a user (admin)
 */
export const useAddUserRole = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof addUserRole>>,
    TError,
    { id: number; data: BodyType<AddUserRoleBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof addUserRole>>,
  TError,
  { id: number; data: BodyType<AddUserRoleBody> },
  TContext
> => {
  return useMutation(getAddUserRoleMutationOptions(options));
};

/**
 * @summary Idempotently remove a role from a user (admin)
 */
export const getRemoveUserRoleUrl = (id: number, roleName: string) => {
  return `/api/users/${id}/roles/${roleName}`;
};

export const removeUserRole = async (
  id: number,
  roleName: string,
  options?: RequestInit,
): Promise<UserProfile> => {
  return customFetch<UserProfile>(getRemoveUserRoleUrl(id, roleName), {
    ...options,
    method: "DELETE",
  });
};

export const getRemoveUserRoleMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof removeUserRole>>,
    TError,
    { id: number; roleName: string },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof removeUserRole>>,
  TError,
  { id: number; roleName: string },
  TContext
> => {
  const mutationKey = ["removeUserRole"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof removeUserRole>>,
    { id: number; roleName: string }
  > = (props) => {
    const { id, roleName } = props ?? {};

    return removeUserRole(id, roleName, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type RemoveUserRoleMutationResult = NonNullable<
  Awaited<ReturnType<typeof removeUserRole>>
>;

export type RemoveUserRoleMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Idempotently remove a role from a user (admin)
 */
export const useRemoveUserRole = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof removeUserRole>>,
    TError,
    { id: number; roleName: string },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof removeUserRole>>,
  TError,
  { id: number; roleName: string },
  TContext
> => {
  return useMutation(getRemoveUserRoleMutationOptions(options));
};

/**
 * @summary List all available permissions (admin)
 */
export const getListPermissionsUrl = () => {
  return `/api/permissions`;
};

export const listPermissions = async (
  options?: RequestInit,
): Promise<Permission[]> => {
  return customFetch<Permission[]>(getListPermissionsUrl(), {
    ...options,
    method: "GET",
  });
};

export const getListPermissionsQueryKey = () => {
  return [`/api/permissions`] as const;
};

export const getListPermissionsQueryOptions = <
  TData = Awaited<ReturnType<typeof listPermissions>>,
  TError = ErrorType<ErrorResponse>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof listPermissions>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getListPermissionsQueryKey();

  const queryFn: QueryFunction<Awaited<ReturnType<typeof listPermissions>>> = ({
    signal,
  }) => listPermissions({ signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof listPermissions>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type ListPermissionsQueryResult = NonNullable<
  Awaited<ReturnType<typeof listPermissions>>
>;
export type ListPermissionsQueryError = ErrorType<ErrorResponse>;

/**
 * @summary List all available permissions (admin)
 */

export function useListPermissions<
  TData = Awaited<ReturnType<typeof listPermissions>>,
  TError = ErrorType<ErrorResponse>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof listPermissions>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getListPermissionsQueryOptions(options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary List roles (admin)
 */
export const getListRolesUrl = () => {
  return `/api/roles`;
};

export const listRoles = async (options?: RequestInit): Promise<Role[]> => {
  return customFetch<Role[]>(getListRolesUrl(), {
    ...options,
    method: "GET",
  });
};

export const getListRolesQueryKey = () => {
  return [`/api/roles`] as const;
};

export const getListRolesQueryOptions = <
  TData = Awaited<ReturnType<typeof listRoles>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<Awaited<ReturnType<typeof listRoles>>, TError, TData>;
  request?: SecondParameter<typeof customFetch>;
}) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getListRolesQueryKey();

  const queryFn: QueryFunction<Awaited<ReturnType<typeof listRoles>>> = ({
    signal,
  }) => listRoles({ signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof listRoles>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type ListRolesQueryResult = NonNullable<
  Awaited<ReturnType<typeof listRoles>>
>;
export type ListRolesQueryError = ErrorType<unknown>;

/**
 * @summary List roles (admin)
 */

export function useListRoles<
  TData = Awaited<ReturnType<typeof listRoles>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<Awaited<ReturnType<typeof listRoles>>, TError, TData>;
  request?: SecondParameter<typeof customFetch>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getListRolesQueryOptions(options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Create role (admin)
 */
export const getCreateRoleUrl = () => {
  return `/api/roles`;
};

export const createRole = async (
  createRoleBody: CreateRoleBody,
  options?: RequestInit,
): Promise<Role> => {
  return customFetch<Role>(getCreateRoleUrl(), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(createRoleBody),
  });
};

export const getCreateRoleMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof createRole>>,
    TError,
    { data: BodyType<CreateRoleBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof createRole>>,
  TError,
  { data: BodyType<CreateRoleBody> },
  TContext
> => {
  const mutationKey = ["createRole"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof createRole>>,
    { data: BodyType<CreateRoleBody> }
  > = (props) => {
    const { data } = props ?? {};

    return createRole(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type CreateRoleMutationResult = NonNullable<
  Awaited<ReturnType<typeof createRole>>
>;
export type CreateRoleMutationBody = BodyType<CreateRoleBody>;
export type CreateRoleMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Create role (admin)
 */
export const useCreateRole = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof createRole>>,
    TError,
    { data: BodyType<CreateRoleBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof createRole>>,
  TError,
  { data: BodyType<CreateRoleBody> },
  TContext
> => {
  return useMutation(getCreateRoleMutationOptions(options));
};

/**
 * @summary Update role name and descriptions (admin)
 */
export const getUpdateRoleUrl = (id: number) => {
  return `/api/roles/${id}`;
};

export const updateRole = async (
  id: number,
  updateRoleBody: UpdateRoleBody,
  options?: RequestInit,
): Promise<Role> => {
  return customFetch<Role>(getUpdateRoleUrl(id), {
    ...options,
    method: "PATCH",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(updateRoleBody),
  });
};

export const getUpdateRoleMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateRole>>,
    TError,
    { id: number; data: BodyType<UpdateRoleBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof updateRole>>,
  TError,
  { id: number; data: BodyType<UpdateRoleBody> },
  TContext
> => {
  const mutationKey = ["updateRole"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof updateRole>>,
    { id: number; data: BodyType<UpdateRoleBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return updateRole(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type UpdateRoleMutationResult = NonNullable<
  Awaited<ReturnType<typeof updateRole>>
>;
export type UpdateRoleMutationBody = BodyType<UpdateRoleBody>;
export type UpdateRoleMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Update role name and descriptions (admin)
 */
export const useUpdateRole = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateRole>>,
    TError,
    { id: number; data: BodyType<UpdateRoleBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof updateRole>>,
  TError,
  { id: number; data: BodyType<UpdateRoleBody> },
  TContext
> => {
  return useMutation(getUpdateRoleMutationOptions(options));
};

/**
 * @summary Delete role (admin)
 */
export const getDeleteRoleUrl = (id: number) => {
  return `/api/roles/${id}`;
};

export const deleteRole = async (
  id: number,
  options?: RequestInit,
): Promise<void> => {
  return customFetch<void>(getDeleteRoleUrl(id), {
    ...options,
    method: "DELETE",
  });
};

export const getDeleteRoleMutationOptions = <
  TError = ErrorType<ErrorResponse | RoleDeletionConflict>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof deleteRole>>,
    TError,
    { id: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof deleteRole>>,
  TError,
  { id: number },
  TContext
> => {
  const mutationKey = ["deleteRole"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof deleteRole>>,
    { id: number }
  > = (props) => {
    const { id } = props ?? {};

    return deleteRole(id, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type DeleteRoleMutationResult = NonNullable<
  Awaited<ReturnType<typeof deleteRole>>
>;

export type DeleteRoleMutationError = ErrorType<
  ErrorResponse | RoleDeletionConflict
>;

/**
 * @summary Delete role (admin)
 */
export const useDeleteRole = <
  TError = ErrorType<ErrorResponse | RoleDeletionConflict>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof deleteRole>>,
    TError,
    { id: number },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof deleteRole>>,
  TError,
  { id: number },
  TContext
> => {
  return useMutation(getDeleteRoleMutationOptions(options));
};

/**
 * @summary Count users and groups currently assigned to a role (admin)
 */
export const getGetRoleUsageUrl = (id: number) => {
  return `/api/roles/${id}/usage`;
};

export const getRoleUsage = async (
  id: number,
  options?: RequestInit,
): Promise<RoleUsage> => {
  return customFetch<RoleUsage>(getGetRoleUsageUrl(id), {
    ...options,
    method: "GET",
  });
};

export const getGetRoleUsageQueryKey = (id: number) => {
  return [`/api/roles/${id}/usage`] as const;
};

export const getGetRoleUsageQueryOptions = <
  TData = Awaited<ReturnType<typeof getRoleUsage>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getRoleUsage>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getGetRoleUsageQueryKey(id);

  const queryFn: QueryFunction<Awaited<ReturnType<typeof getRoleUsage>>> = ({
    signal,
  }) => getRoleUsage(id, { signal, ...requestOptions });

  return {
    queryKey,
    queryFn,
    enabled: !!id,
    ...queryOptions,
  } as UseQueryOptions<
    Awaited<ReturnType<typeof getRoleUsage>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type GetRoleUsageQueryResult = NonNullable<
  Awaited<ReturnType<typeof getRoleUsage>>
>;
export type GetRoleUsageQueryError = ErrorType<ErrorResponse>;

/**
 * @summary Count users and groups currently assigned to a role (admin)
 */

export function useGetRoleUsage<
  TData = Awaited<ReturnType<typeof getRoleUsage>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getRoleUsage>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetRoleUsageQueryOptions(id, options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * Returns permission-change audit records for the given role, newest
first. Supports offset-based pagination via `limit`/`offset` and
optional filters by acting user (`actorUserId`) and a date range
(`from`/`to`, inclusive UTC days). The response includes the total
matching count and the next offset to load (or `null` when the end
has been reached). Each entry captures the actor (if known), the
previous permission IDs, the new permission IDs, and the timestamp.

 * @summary List recent permission-change audit entries for a role (admin)
 */
export const getGetRolePermissionAuditUrl = (
  id: number,
  params?: GetRolePermissionAuditParams,
) => {
  const normalizedParams = new URLSearchParams();

  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined) {
      normalizedParams.append(key, value === null ? "null" : value.toString());
    }
  });

  const stringifiedParams = normalizedParams.toString();

  return stringifiedParams.length > 0
    ? `/api/roles/${id}/audit?${stringifiedParams}`
    : `/api/roles/${id}/audit`;
};

export const getRolePermissionAudit = async (
  id: number,
  params?: GetRolePermissionAuditParams,
  options?: RequestInit,
): Promise<RolePermissionAuditList> => {
  return customFetch<RolePermissionAuditList>(
    getGetRolePermissionAuditUrl(id, params),
    {
      ...options,
      method: "GET",
    },
  );
};

export const getGetRolePermissionAuditQueryKey = (
  id: number,
  params?: GetRolePermissionAuditParams,
) => {
  return [`/api/roles/${id}/audit`, ...(params ? [params] : [])] as const;
};

export const getGetRolePermissionAuditQueryOptions = <
  TData = Awaited<ReturnType<typeof getRolePermissionAudit>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  params?: GetRolePermissionAuditParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getRolePermissionAudit>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey =
    queryOptions?.queryKey ?? getGetRolePermissionAuditQueryKey(id, params);

  const queryFn: QueryFunction<
    Awaited<ReturnType<typeof getRolePermissionAudit>>
  > = ({ signal }) =>
    getRolePermissionAudit(id, params, { signal, ...requestOptions });

  return {
    queryKey,
    queryFn,
    enabled: !!id,
    ...queryOptions,
  } as UseQueryOptions<
    Awaited<ReturnType<typeof getRolePermissionAudit>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type GetRolePermissionAuditQueryResult = NonNullable<
  Awaited<ReturnType<typeof getRolePermissionAudit>>
>;
export type GetRolePermissionAuditQueryError = ErrorType<ErrorResponse>;

/**
 * @summary List recent permission-change audit entries for a role (admin)
 */

export function useGetRolePermissionAudit<
  TData = Awaited<ReturnType<typeof getRolePermissionAudit>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  params?: GetRolePermissionAuditParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getRolePermissionAudit>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetRolePermissionAuditQueryOptions(
    id,
    params,
    options,
  );

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * Given a candidate set of permission IDs for the role, returns the list of
permissions that would be removed and, for each one, how many users would
lose the permission (because they hold this role directly or via a group
and have no other role granting the permission), plus the groups that
currently grant this role.

 * @summary Preview the impact of changing a role's permission set (admin)
 */
export const getPreviewRolePermissionsImpactUrl = (id: number) => {
  return `/api/roles/${id}/permissions/impact-preview`;
};

export const previewRolePermissionsImpact = async (
  id: number,
  rolePermissionsImpactBody: RolePermissionsImpactBody,
  options?: RequestInit,
): Promise<RolePermissionsImpact> => {
  return customFetch<RolePermissionsImpact>(
    getPreviewRolePermissionsImpactUrl(id),
    {
      ...options,
      method: "POST",
      headers: { "Content-Type": "application/json", ...options?.headers },
      body: JSON.stringify(rolePermissionsImpactBody),
    },
  );
};

export const getPreviewRolePermissionsImpactMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof previewRolePermissionsImpact>>,
    TError,
    { id: number; data: BodyType<RolePermissionsImpactBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof previewRolePermissionsImpact>>,
  TError,
  { id: number; data: BodyType<RolePermissionsImpactBody> },
  TContext
> => {
  const mutationKey = ["previewRolePermissionsImpact"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof previewRolePermissionsImpact>>,
    { id: number; data: BodyType<RolePermissionsImpactBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return previewRolePermissionsImpact(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type PreviewRolePermissionsImpactMutationResult = NonNullable<
  Awaited<ReturnType<typeof previewRolePermissionsImpact>>
>;
export type PreviewRolePermissionsImpactMutationBody =
  BodyType<RolePermissionsImpactBody>;
export type PreviewRolePermissionsImpactMutationError =
  ErrorType<ErrorResponse>;

/**
 * @summary Preview the impact of changing a role's permission set (admin)
 */
export const usePreviewRolePermissionsImpact = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof previewRolePermissionsImpact>>,
    TError,
    { id: number; data: BodyType<RolePermissionsImpactBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof previewRolePermissionsImpact>>,
  TError,
  { id: number; data: BodyType<RolePermissionsImpactBody> },
  TContext
> => {
  return useMutation(getPreviewRolePermissionsImpactMutationOptions(options));
};

/**
 * @summary List permissions assigned to a role (admin)
 */
export const getGetRolePermissionsUrl = (id: number) => {
  return `/api/roles/${id}/permissions`;
};

export const getRolePermissions = async (
  id: number,
  options?: RequestInit,
): Promise<Permission[]> => {
  return customFetch<Permission[]>(getGetRolePermissionsUrl(id), {
    ...options,
    method: "GET",
  });
};

export const getGetRolePermissionsQueryKey = (id: number) => {
  return [`/api/roles/${id}/permissions`] as const;
};

export const getGetRolePermissionsQueryOptions = <
  TData = Awaited<ReturnType<typeof getRolePermissions>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getRolePermissions>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getGetRolePermissionsQueryKey(id);

  const queryFn: QueryFunction<
    Awaited<ReturnType<typeof getRolePermissions>>
  > = ({ signal }) => getRolePermissions(id, { signal, ...requestOptions });

  return {
    queryKey,
    queryFn,
    enabled: !!id,
    ...queryOptions,
  } as UseQueryOptions<
    Awaited<ReturnType<typeof getRolePermissions>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type GetRolePermissionsQueryResult = NonNullable<
  Awaited<ReturnType<typeof getRolePermissions>>
>;
export type GetRolePermissionsQueryError = ErrorType<ErrorResponse>;

/**
 * @summary List permissions assigned to a role (admin)
 */

export function useGetRolePermissions<
  TData = Awaited<ReturnType<typeof getRolePermissions>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getRolePermissions>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetRolePermissionsQueryOptions(id, options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Atomically replace the permissions assigned to a role (admin)
 */
export const getReplaceRolePermissionsUrl = (id: number) => {
  return `/api/roles/${id}/permissions`;
};

export const replaceRolePermissions = async (
  id: number,
  replaceRolePermissionsBody: ReplaceRolePermissionsBody,
  options?: RequestInit,
): Promise<Permission[]> => {
  return customFetch<Permission[]>(getReplaceRolePermissionsUrl(id), {
    ...options,
    method: "PUT",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(replaceRolePermissionsBody),
  });
};

export const getReplaceRolePermissionsMutationOptions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof replaceRolePermissions>>,
    TError,
    { id: number; data: BodyType<ReplaceRolePermissionsBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof replaceRolePermissions>>,
  TError,
  { id: number; data: BodyType<ReplaceRolePermissionsBody> },
  TContext
> => {
  const mutationKey = ["replaceRolePermissions"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof replaceRolePermissions>>,
    { id: number; data: BodyType<ReplaceRolePermissionsBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return replaceRolePermissions(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type ReplaceRolePermissionsMutationResult = NonNullable<
  Awaited<ReturnType<typeof replaceRolePermissions>>
>;
export type ReplaceRolePermissionsMutationBody =
  BodyType<ReplaceRolePermissionsBody>;
export type ReplaceRolePermissionsMutationError = ErrorType<ErrorResponse>;

/**
 * @summary Atomically replace the permissions assigned to a role (admin)
 */
export const useReplaceRolePermissions = <
  TError = ErrorType<ErrorResponse>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof replaceRolePermissions>>,
    TError,
    { id: number; data: BodyType<ReplaceRolePermissionsBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof replaceRolePermissions>>,
  TError,
  { id: number; data: BodyType<ReplaceRolePermissionsBody> },
  TContext
> => {
  return useMutation(getReplaceRolePermissionsMutationOptions(options));
};

/**
 * @summary List groups (admin)
 */
export const getListGroupsUrl = (params?: ListGroupsParams) => {
  const normalizedParams = new URLSearchParams();

  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined) {
      normalizedParams.append(key, value === null ? "null" : value.toString());
    }
  });

  const stringifiedParams = normalizedParams.toString();

  return stringifiedParams.length > 0
    ? `/api/groups?${stringifiedParams}`
    : `/api/groups`;
};

export const listGroups = async (
  params?: ListGroupsParams,
  options?: RequestInit,
): Promise<Group[]> => {
  return customFetch<Group[]>(getListGroupsUrl(params), {
    ...options,
    method: "GET",
  });
};

export const getListGroupsQueryKey = (params?: ListGroupsParams) => {
  return [`/api/groups`, ...(params ? [params] : [])] as const;
};

export const getListGroupsQueryOptions = <
  TData = Awaited<ReturnType<typeof listGroups>>,
  TError = ErrorType<unknown>,
>(
  params?: ListGroupsParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof listGroups>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getListGroupsQueryKey(params);

  const queryFn: QueryFunction<Awaited<ReturnType<typeof listGroups>>> = ({
    signal,
  }) => listGroups(params, { signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof listGroups>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type ListGroupsQueryResult = NonNullable<
  Awaited<ReturnType<typeof listGroups>>
>;
export type ListGroupsQueryError = ErrorType<unknown>;

/**
 * @summary List groups (admin)
 */

export function useListGroups<
  TData = Awaited<ReturnType<typeof listGroups>>,
  TError = ErrorType<unknown>,
>(
  params?: ListGroupsParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof listGroups>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getListGroupsQueryOptions(params, options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Create group (admin)
 */
export const getCreateGroupUrl = () => {
  return `/api/groups`;
};

export const createGroup = async (
  createGroupBody: CreateGroupBody,
  options?: RequestInit,
): Promise<Group> => {
  return customFetch<Group>(getCreateGroupUrl(), {
    ...options,
    method: "POST",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(createGroupBody),
  });
};

export const getCreateGroupMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof createGroup>>,
    TError,
    { data: BodyType<CreateGroupBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof createGroup>>,
  TError,
  { data: BodyType<CreateGroupBody> },
  TContext
> => {
  const mutationKey = ["createGroup"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof createGroup>>,
    { data: BodyType<CreateGroupBody> }
  > = (props) => {
    const { data } = props ?? {};

    return createGroup(data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type CreateGroupMutationResult = NonNullable<
  Awaited<ReturnType<typeof createGroup>>
>;
export type CreateGroupMutationBody = BodyType<CreateGroupBody>;
export type CreateGroupMutationError = ErrorType<unknown>;

/**
 * @summary Create group (admin)
 */
export const useCreateGroup = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof createGroup>>,
    TError,
    { data: BodyType<CreateGroupBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof createGroup>>,
  TError,
  { data: BodyType<CreateGroupBody> },
  TContext
> => {
  return useMutation(getCreateGroupMutationOptions(options));
};

/**
 * @summary Get group with members and apps (admin)
 */
export const getGetGroupUrl = (id: number) => {
  return `/api/groups/${id}`;
};

export const getGroup = async (
  id: number,
  options?: RequestInit,
): Promise<GroupDetail> => {
  return customFetch<GroupDetail>(getGetGroupUrl(id), {
    ...options,
    method: "GET",
  });
};

export const getGetGroupQueryKey = (id: number) => {
  return [`/api/groups/${id}`] as const;
};

export const getGetGroupQueryOptions = <
  TData = Awaited<ReturnType<typeof getGroup>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getGroup>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getGetGroupQueryKey(id);

  const queryFn: QueryFunction<Awaited<ReturnType<typeof getGroup>>> = ({
    signal,
  }) => getGroup(id, { signal, ...requestOptions });

  return {
    queryKey,
    queryFn,
    enabled: !!id,
    ...queryOptions,
  } as UseQueryOptions<Awaited<ReturnType<typeof getGroup>>, TError, TData> & {
    queryKey: QueryKey;
  };
};

export type GetGroupQueryResult = NonNullable<
  Awaited<ReturnType<typeof getGroup>>
>;
export type GetGroupQueryError = ErrorType<ErrorResponse>;

/**
 * @summary Get group with members and apps (admin)
 */

export function useGetGroup<
  TData = Awaited<ReturnType<typeof getGroup>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getGroup>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetGroupQueryOptions(id, options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Update group (admin)
 */
export const getUpdateGroupUrl = (id: number) => {
  return `/api/groups/${id}`;
};

export const updateGroup = async (
  id: number,
  updateGroupBody: UpdateGroupBody,
  options?: RequestInit,
): Promise<GroupDetail> => {
  return customFetch<GroupDetail>(getUpdateGroupUrl(id), {
    ...options,
    method: "PATCH",
    headers: { "Content-Type": "application/json", ...options?.headers },
    body: JSON.stringify(updateGroupBody),
  });
};

export const getUpdateGroupMutationOptions = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateGroup>>,
    TError,
    { id: number; data: BodyType<UpdateGroupBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof updateGroup>>,
  TError,
  { id: number; data: BodyType<UpdateGroupBody> },
  TContext
> => {
  const mutationKey = ["updateGroup"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof updateGroup>>,
    { id: number; data: BodyType<UpdateGroupBody> }
  > = (props) => {
    const { id, data } = props ?? {};

    return updateGroup(id, data, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type UpdateGroupMutationResult = NonNullable<
  Awaited<ReturnType<typeof updateGroup>>
>;
export type UpdateGroupMutationBody = BodyType<UpdateGroupBody>;
export type UpdateGroupMutationError = ErrorType<unknown>;

/**
 * @summary Update group (admin)
 */
export const useUpdateGroup = <
  TError = ErrorType<unknown>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof updateGroup>>,
    TError,
    { id: number; data: BodyType<UpdateGroupBody> },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof updateGroup>>,
  TError,
  { id: number; data: BodyType<UpdateGroupBody> },
  TContext
> => {
  return useMutation(getUpdateGroupMutationOptions(options));
};

/**
 * @summary Delete group (admin)
 */
export const getDeleteGroupUrl = (id: number, params?: DeleteGroupParams) => {
  const normalizedParams = new URLSearchParams();

  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined) {
      normalizedParams.append(key, value === null ? "null" : value.toString());
    }
  });

  const stringifiedParams = normalizedParams.toString();

  return stringifiedParams.length > 0
    ? `/api/groups/${id}?${stringifiedParams}`
    : `/api/groups/${id}`;
};

export const deleteGroup = async (
  id: number,
  params?: DeleteGroupParams,
  options?: RequestInit,
): Promise<void> => {
  return customFetch<void>(getDeleteGroupUrl(id, params), {
    ...options,
    method: "DELETE",
  });
};

export const getDeleteGroupMutationOptions = <
  TError = ErrorType<ErrorResponse | GroupDeletionConflict>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof deleteGroup>>,
    TError,
    { id: number; params?: DeleteGroupParams },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
  Awaited<ReturnType<typeof deleteGroup>>,
  TError,
  { id: number; params?: DeleteGroupParams },
  TContext
> => {
  const mutationKey = ["deleteGroup"];
  const { mutation: mutationOptions, request: requestOptions } = options
    ? options.mutation &&
      "mutationKey" in options.mutation &&
      options.mutation.mutationKey
      ? options
      : { ...options, mutation: { ...options.mutation, mutationKey } }
    : { mutation: { mutationKey }, request: undefined };

  const mutationFn: MutationFunction<
    Awaited<ReturnType<typeof deleteGroup>>,
    { id: number; params?: DeleteGroupParams }
  > = (props) => {
    const { id, params } = props ?? {};

    return deleteGroup(id, params, requestOptions);
  };

  return { mutationFn, ...mutationOptions };
};

export type DeleteGroupMutationResult = NonNullable<
  Awaited<ReturnType<typeof deleteGroup>>
>;

export type DeleteGroupMutationError = ErrorType<
  ErrorResponse | GroupDeletionConflict
>;

/**
 * @summary Delete group (admin)
 */
export const useDeleteGroup = <
  TError = ErrorType<ErrorResponse | GroupDeletionConflict>,
  TContext = unknown,
>(options?: {
  mutation?: UseMutationOptions<
    Awaited<ReturnType<typeof deleteGroup>>,
    TError,
    { id: number; params?: DeleteGroupParams },
    TContext
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseMutationResult<
  Awaited<ReturnType<typeof deleteGroup>>,
  TError,
  { id: number; params?: DeleteGroupParams },
  TContext
> => {
  return useMutation(getDeleteGroupMutationOptions(options));
};

/**
 * Returns permission-change audit records for the given user, newest
first. Mirrors the role audit endpoint shape and supports the same
`limit`/`offset`/`actorUserId`/`from`/`to` filters. Each entry
captures the actor (if known), the previous and new id sets, and
the change kind (`user.roles` or `user.groups`).

 * @summary List recent permission-change audit entries for a user (admin)
 */
export const getGetUserPermissionAuditUrl = (
  id: number,
  params?: GetUserPermissionAuditParams,
) => {
  const normalizedParams = new URLSearchParams();

  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined) {
      normalizedParams.append(key, value === null ? "null" : value.toString());
    }
  });

  const stringifiedParams = normalizedParams.toString();

  return stringifiedParams.length > 0
    ? `/api/users/${id}/audit?${stringifiedParams}`
    : `/api/users/${id}/audit`;
};

export const getUserPermissionAudit = async (
  id: number,
  params?: GetUserPermissionAuditParams,
  options?: RequestInit,
): Promise<PermissionAuditList> => {
  return customFetch<PermissionAuditList>(
    getGetUserPermissionAuditUrl(id, params),
    {
      ...options,
      method: "GET",
    },
  );
};

export const getGetUserPermissionAuditQueryKey = (
  id: number,
  params?: GetUserPermissionAuditParams,
) => {
  return [`/api/users/${id}/audit`, ...(params ? [params] : [])] as const;
};

export const getGetUserPermissionAuditQueryOptions = <
  TData = Awaited<ReturnType<typeof getUserPermissionAudit>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  params?: GetUserPermissionAuditParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getUserPermissionAudit>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey =
    queryOptions?.queryKey ?? getGetUserPermissionAuditQueryKey(id, params);

  const queryFn: QueryFunction<
    Awaited<ReturnType<typeof getUserPermissionAudit>>
  > = ({ signal }) =>
    getUserPermissionAudit(id, params, { signal, ...requestOptions });

  return {
    queryKey,
    queryFn,
    enabled: !!id,
    ...queryOptions,
  } as UseQueryOptions<
    Awaited<ReturnType<typeof getUserPermissionAudit>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type GetUserPermissionAuditQueryResult = NonNullable<
  Awaited<ReturnType<typeof getUserPermissionAudit>>
>;
export type GetUserPermissionAuditQueryError = ErrorType<ErrorResponse>;

/**
 * @summary List recent permission-change audit entries for a user (admin)
 */

export function useGetUserPermissionAudit<
  TData = Awaited<ReturnType<typeof getUserPermissionAudit>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  params?: GetUserPermissionAuditParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getUserPermissionAudit>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetUserPermissionAuditQueryOptions(
    id,
    params,
    options,
  );

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * Returns permission-change audit records for the given group, newest
first. Captures changes to the group's user, role and app sets via
the discriminator field `changeKind`.

 * @summary List recent permission-change audit entries for a group (admin)
 */
export const getGetGroupPermissionAuditUrl = (
  id: number,
  params?: GetGroupPermissionAuditParams,
) => {
  const normalizedParams = new URLSearchParams();

  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined) {
      normalizedParams.append(key, value === null ? "null" : value.toString());
    }
  });

  const stringifiedParams = normalizedParams.toString();

  return stringifiedParams.length > 0
    ? `/api/groups/${id}/audit?${stringifiedParams}`
    : `/api/groups/${id}/audit`;
};

export const getGroupPermissionAudit = async (
  id: number,
  params?: GetGroupPermissionAuditParams,
  options?: RequestInit,
): Promise<PermissionAuditList> => {
  return customFetch<PermissionAuditList>(
    getGetGroupPermissionAuditUrl(id, params),
    {
      ...options,
      method: "GET",
    },
  );
};

export const getGetGroupPermissionAuditQueryKey = (
  id: number,
  params?: GetGroupPermissionAuditParams,
) => {
  return [`/api/groups/${id}/audit`, ...(params ? [params] : [])] as const;
};

export const getGetGroupPermissionAuditQueryOptions = <
  TData = Awaited<ReturnType<typeof getGroupPermissionAudit>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  params?: GetGroupPermissionAuditParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getGroupPermissionAudit>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey =
    queryOptions?.queryKey ?? getGetGroupPermissionAuditQueryKey(id, params);

  const queryFn: QueryFunction<
    Awaited<ReturnType<typeof getGroupPermissionAudit>>
  > = ({ signal }) =>
    getGroupPermissionAudit(id, params, { signal, ...requestOptions });

  return {
    queryKey,
    queryFn,
    enabled: !!id,
    ...queryOptions,
  } as UseQueryOptions<
    Awaited<ReturnType<typeof getGroupPermissionAudit>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type GetGroupPermissionAuditQueryResult = NonNullable<
  Awaited<ReturnType<typeof getGroupPermissionAudit>>
>;
export type GetGroupPermissionAuditQueryError = ErrorType<ErrorResponse>;

/**
 * @summary List recent permission-change audit entries for a group (admin)
 */

export function useGetGroupPermissionAudit<
  TData = Awaited<ReturnType<typeof getGroupPermissionAudit>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  params?: GetGroupPermissionAuditParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getGroupPermissionAudit>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetGroupPermissionAuditQueryOptions(
    id,
    params,
    options,
  );

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * Returns permission-change audit records for the given app, newest
first. Captures changes to the set of permissions required to open
the app (`changeKind: app.permissions`).

 * @summary List recent permission-change audit entries for an app (admin)
 */
export const getGetAppPermissionAuditUrl = (
  id: number,
  params?: GetAppPermissionAuditParams,
) => {
  const normalizedParams = new URLSearchParams();

  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined) {
      normalizedParams.append(key, value === null ? "null" : value.toString());
    }
  });

  const stringifiedParams = normalizedParams.toString();

  return stringifiedParams.length > 0
    ? `/api/apps/${id}/audit?${stringifiedParams}`
    : `/api/apps/${id}/audit`;
};

export const getAppPermissionAudit = async (
  id: number,
  params?: GetAppPermissionAuditParams,
  options?: RequestInit,
): Promise<PermissionAuditList> => {
  return customFetch<PermissionAuditList>(
    getGetAppPermissionAuditUrl(id, params),
    {
      ...options,
      method: "GET",
    },
  );
};

export const getGetAppPermissionAuditQueryKey = (
  id: number,
  params?: GetAppPermissionAuditParams,
) => {
  return [`/api/apps/${id}/audit`, ...(params ? [params] : [])] as const;
};

export const getGetAppPermissionAuditQueryOptions = <
  TData = Awaited<ReturnType<typeof getAppPermissionAudit>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  params?: GetAppPermissionAuditParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getAppPermissionAudit>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey =
    queryOptions?.queryKey ?? getGetAppPermissionAuditQueryKey(id, params);

  const queryFn: QueryFunction<
    Awaited<ReturnType<typeof getAppPermissionAudit>>
  > = ({ signal }) =>
    getAppPermissionAudit(id, params, { signal, ...requestOptions });

  return {
    queryKey,
    queryFn,
    enabled: !!id,
    ...queryOptions,
  } as UseQueryOptions<
    Awaited<ReturnType<typeof getAppPermissionAudit>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type GetAppPermissionAuditQueryResult = NonNullable<
  Awaited<ReturnType<typeof getAppPermissionAudit>>
>;
export type GetAppPermissionAuditQueryError = ErrorType<ErrorResponse>;

/**
 * @summary List recent permission-change audit entries for an app (admin)
 */

export function useGetAppPermissionAudit<
  TData = Awaited<ReturnType<typeof getAppPermissionAudit>>,
  TError = ErrorType<ErrorResponse>,
>(
  id: number,
  params?: GetAppPermissionAuditParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getAppPermissionAudit>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetAppPermissionAuditQueryOptions(
    id,
    params,
    options,
  );

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Get home screen stats
 */
export const getGetHomeStatsUrl = () => {
  return `/api/stats/home`;
};

export const getHomeStats = async (
  options?: RequestInit,
): Promise<HomeStats> => {
  return customFetch<HomeStats>(getGetHomeStatsUrl(), {
    ...options,
    method: "GET",
  });
};

export const getGetHomeStatsQueryKey = () => {
  return [`/api/stats/home`] as const;
};

export const getGetHomeStatsQueryOptions = <
  TData = Awaited<ReturnType<typeof getHomeStats>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof getHomeStats>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getGetHomeStatsQueryKey();

  const queryFn: QueryFunction<Awaited<ReturnType<typeof getHomeStats>>> = ({
    signal,
  }) => getHomeStats({ signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof getHomeStats>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type GetHomeStatsQueryResult = NonNullable<
  Awaited<ReturnType<typeof getHomeStats>>
>;
export type GetHomeStatsQueryError = ErrorType<unknown>;

/**
 * @summary Get home screen stats
 */

export function useGetHomeStats<
  TData = Awaited<ReturnType<typeof getHomeStats>>,
  TError = ErrorType<unknown>,
>(options?: {
  query?: UseQueryOptions<
    Awaited<ReturnType<typeof getHomeStats>>,
    TError,
    TData
  >;
  request?: SecondParameter<typeof customFetch>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetHomeStatsQueryOptions(options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Get admin dashboard trend stats
 */
export const getGetAdminStatsUrl = (params?: GetAdminStatsParams) => {
  const normalizedParams = new URLSearchParams();

  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined) {
      normalizedParams.append(key, value === null ? "null" : value.toString());
    }
  });

  const stringifiedParams = normalizedParams.toString();

  return stringifiedParams.length > 0
    ? `/api/stats/admin?${stringifiedParams}`
    : `/api/stats/admin`;
};

export const getAdminStats = async (
  params?: GetAdminStatsParams,
  options?: RequestInit,
): Promise<AdminStats> => {
  return customFetch<AdminStats>(getGetAdminStatsUrl(params), {
    ...options,
    method: "GET",
  });
};

export const getGetAdminStatsQueryKey = (params?: GetAdminStatsParams) => {
  return [`/api/stats/admin`, ...(params ? [params] : [])] as const;
};

export const getGetAdminStatsQueryOptions = <
  TData = Awaited<ReturnType<typeof getAdminStats>>,
  TError = ErrorType<ErrorResponse>,
>(
  params?: GetAdminStatsParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getAdminStats>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getGetAdminStatsQueryKey(params);

  const queryFn: QueryFunction<Awaited<ReturnType<typeof getAdminStats>>> = ({
    signal,
  }) => getAdminStats(params, { signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof getAdminStats>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type GetAdminStatsQueryResult = NonNullable<
  Awaited<ReturnType<typeof getAdminStats>>
>;
export type GetAdminStatsQueryError = ErrorType<ErrorResponse>;

/**
 * @summary Get admin dashboard trend stats
 */

export function useGetAdminStats<
  TData = Awaited<ReturnType<typeof getAdminStats>>,
  TError = ErrorType<ErrorResponse>,
>(
  params?: GetAdminStatsParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getAdminStats>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetAdminStatsQueryOptions(params, options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Recent opens of a single app within the selected range
 */
export const getGetAdminAppOpensByAppUrl = (
  appId: number,
  params?: GetAdminAppOpensByAppParams,
) => {
  const normalizedParams = new URLSearchParams();

  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined) {
      normalizedParams.append(key, value === null ? "null" : value.toString());
    }
  });

  const stringifiedParams = normalizedParams.toString();

  return stringifiedParams.length > 0
    ? `/api/stats/admin/app-opens/by-app/${appId}?${stringifiedParams}`
    : `/api/stats/admin/app-opens/by-app/${appId}`;
};

export const getAdminAppOpensByApp = async (
  appId: number,
  params?: GetAdminAppOpensByAppParams,
  options?: RequestInit,
): Promise<AdminAppOpensByApp> => {
  return customFetch<AdminAppOpensByApp>(
    getGetAdminAppOpensByAppUrl(appId, params),
    {
      ...options,
      method: "GET",
    },
  );
};

export const getGetAdminAppOpensByAppQueryKey = (
  appId: number,
  params?: GetAdminAppOpensByAppParams,
) => {
  return [
    `/api/stats/admin/app-opens/by-app/${appId}`,
    ...(params ? [params] : []),
  ] as const;
};

export const getGetAdminAppOpensByAppQueryOptions = <
  TData = Awaited<ReturnType<typeof getAdminAppOpensByApp>>,
  TError = ErrorType<ErrorResponse>,
>(
  appId: number,
  params?: GetAdminAppOpensByAppParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getAdminAppOpensByApp>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey =
    queryOptions?.queryKey ?? getGetAdminAppOpensByAppQueryKey(appId, params);

  const queryFn: QueryFunction<
    Awaited<ReturnType<typeof getAdminAppOpensByApp>>
  > = ({ signal }) =>
    getAdminAppOpensByApp(appId, params, { signal, ...requestOptions });

  return {
    queryKey,
    queryFn,
    enabled: !!appId,
    ...queryOptions,
  } as UseQueryOptions<
    Awaited<ReturnType<typeof getAdminAppOpensByApp>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type GetAdminAppOpensByAppQueryResult = NonNullable<
  Awaited<ReturnType<typeof getAdminAppOpensByApp>>
>;
export type GetAdminAppOpensByAppQueryError = ErrorType<ErrorResponse>;

/**
 * @summary Recent opens of a single app within the selected range
 */

export function useGetAdminAppOpensByApp<
  TData = Awaited<ReturnType<typeof getAdminAppOpensByApp>>,
  TError = ErrorType<ErrorResponse>,
>(
  appId: number,
  params?: GetAdminAppOpensByAppParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getAdminAppOpensByApp>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetAdminAppOpensByAppQueryOptions(
    appId,
    params,
    options,
  );

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * @summary Recent app opens by a single user within the selected range
 */
export const getGetAdminAppOpensByUserUrl = (
  userId: number,
  params?: GetAdminAppOpensByUserParams,
) => {
  const normalizedParams = new URLSearchParams();

  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined) {
      normalizedParams.append(key, value === null ? "null" : value.toString());
    }
  });

  const stringifiedParams = normalizedParams.toString();

  return stringifiedParams.length > 0
    ? `/api/stats/admin/app-opens/by-user/${userId}?${stringifiedParams}`
    : `/api/stats/admin/app-opens/by-user/${userId}`;
};

export const getAdminAppOpensByUser = async (
  userId: number,
  params?: GetAdminAppOpensByUserParams,
  options?: RequestInit,
): Promise<AdminAppOpensByUser> => {
  return customFetch<AdminAppOpensByUser>(
    getGetAdminAppOpensByUserUrl(userId, params),
    {
      ...options,
      method: "GET",
    },
  );
};

export const getGetAdminAppOpensByUserQueryKey = (
  userId: number,
  params?: GetAdminAppOpensByUserParams,
) => {
  return [
    `/api/stats/admin/app-opens/by-user/${userId}`,
    ...(params ? [params] : []),
  ] as const;
};

export const getGetAdminAppOpensByUserQueryOptions = <
  TData = Awaited<ReturnType<typeof getAdminAppOpensByUser>>,
  TError = ErrorType<ErrorResponse>,
>(
  userId: number,
  params?: GetAdminAppOpensByUserParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getAdminAppOpensByUser>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey =
    queryOptions?.queryKey ?? getGetAdminAppOpensByUserQueryKey(userId, params);

  const queryFn: QueryFunction<
    Awaited<ReturnType<typeof getAdminAppOpensByUser>>
  > = ({ signal }) =>
    getAdminAppOpensByUser(userId, params, { signal, ...requestOptions });

  return {
    queryKey,
    queryFn,
    enabled: !!userId,
    ...queryOptions,
  } as UseQueryOptions<
    Awaited<ReturnType<typeof getAdminAppOpensByUser>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type GetAdminAppOpensByUserQueryResult = NonNullable<
  Awaited<ReturnType<typeof getAdminAppOpensByUser>>
>;
export type GetAdminAppOpensByUserQueryError = ErrorType<ErrorResponse>;

/**
 * @summary Recent app opens by a single user within the selected range
 */

export function useGetAdminAppOpensByUser<
  TData = Awaited<ReturnType<typeof getAdminAppOpensByUser>>,
  TError = ErrorType<ErrorResponse>,
>(
  userId: number,
  params?: GetAdminAppOpensByUserParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof getAdminAppOpensByUser>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getGetAdminAppOpensByUserQueryOptions(
    userId,
    params,
    options,
  );

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * Returns recent entries from the audit log, newest first. Admin only.
Filter by action (exact match) and/or a date range (inclusive).

 * @summary List audit log entries (admin)
 */
export const getListAuditLogsUrl = (params?: ListAuditLogsParams) => {
  const normalizedParams = new URLSearchParams();

  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined) {
      normalizedParams.append(key, value === null ? "null" : value.toString());
    }
  });

  const stringifiedParams = normalizedParams.toString();

  return stringifiedParams.length > 0
    ? `/api/admin/audit-logs?${stringifiedParams}`
    : `/api/admin/audit-logs`;
};

export const listAuditLogs = async (
  params?: ListAuditLogsParams,
  options?: RequestInit,
): Promise<AuditLogList> => {
  return customFetch<AuditLogList>(getListAuditLogsUrl(params), {
    ...options,
    method: "GET",
  });
};

export const getListAuditLogsQueryKey = (params?: ListAuditLogsParams) => {
  return [`/api/admin/audit-logs`, ...(params ? [params] : [])] as const;
};

export const getListAuditLogsQueryOptions = <
  TData = Awaited<ReturnType<typeof listAuditLogs>>,
  TError = ErrorType<ErrorResponse>,
>(
  params?: ListAuditLogsParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof listAuditLogs>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getListAuditLogsQueryKey(params);

  const queryFn: QueryFunction<Awaited<ReturnType<typeof listAuditLogs>>> = ({
    signal,
  }) => listAuditLogs(params, { signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof listAuditLogs>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type ListAuditLogsQueryResult = NonNullable<
  Awaited<ReturnType<typeof listAuditLogs>>
>;
export type ListAuditLogsQueryError = ErrorType<ErrorResponse>;

/**
 * @summary List audit log entries (admin)
 */

export function useListAuditLogs<
  TData = Awaited<ReturnType<typeof listAuditLogs>>,
  TError = ErrorType<ErrorResponse>,
>(
  params?: ListAuditLogsParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof listAuditLogs>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getListAuditLogsQueryOptions(params, options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}

/**
 * Streams the filtered audit log as a CSV download.
Honors the same `action`, `forcedOnly`, `from`, and `to` filters as the
list endpoint.
Columns: action, actor_username, target_type, target_id, created_at, metadata.
Capped at 50,000 rows per export to bound response size.

 * @summary Export filtered audit log as CSV (admin)
 */
export const getExportAuditLogsCsvUrl = (params?: ExportAuditLogsCsvParams) => {
  const normalizedParams = new URLSearchParams();

  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined) {
      normalizedParams.append(key, value === null ? "null" : value.toString());
    }
  });

  const stringifiedParams = normalizedParams.toString();

  return stringifiedParams.length > 0
    ? `/api/admin/audit-logs/export?${stringifiedParams}`
    : `/api/admin/audit-logs/export`;
};

export const exportAuditLogsCsv = async (
  params?: ExportAuditLogsCsvParams,
  options?: RequestInit,
): Promise<Blob> => {
  return customFetch<Blob>(getExportAuditLogsCsvUrl(params), {
    ...options,
    method: "GET",
  });
};

export const getExportAuditLogsCsvQueryKey = (
  params?: ExportAuditLogsCsvParams,
) => {
  return [`/api/admin/audit-logs/export`, ...(params ? [params] : [])] as const;
};

export const getExportAuditLogsCsvQueryOptions = <
  TData = Awaited<ReturnType<typeof exportAuditLogsCsv>>,
  TError = ErrorType<ErrorResponse>,
>(
  params?: ExportAuditLogsCsvParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof exportAuditLogsCsv>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
) => {
  const { query: queryOptions, request: requestOptions } = options ?? {};

  const queryKey =
    queryOptions?.queryKey ?? getExportAuditLogsCsvQueryKey(params);

  const queryFn: QueryFunction<
    Awaited<ReturnType<typeof exportAuditLogsCsv>>
  > = ({ signal }) => exportAuditLogsCsv(params, { signal, ...requestOptions });

  return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
    Awaited<ReturnType<typeof exportAuditLogsCsv>>,
    TError,
    TData
  > & { queryKey: QueryKey };
};

export type ExportAuditLogsCsvQueryResult = NonNullable<
  Awaited<ReturnType<typeof exportAuditLogsCsv>>
>;
export type ExportAuditLogsCsvQueryError = ErrorType<ErrorResponse>;

/**
 * @summary Export filtered audit log as CSV (admin)
 */

export function useExportAuditLogsCsv<
  TData = Awaited<ReturnType<typeof exportAuditLogsCsv>>,
  TError = ErrorType<ErrorResponse>,
>(
  params?: ExportAuditLogsCsvParams,
  options?: {
    query?: UseQueryOptions<
      Awaited<ReturnType<typeof exportAuditLogsCsv>>,
      TError,
      TData
    >;
    request?: SecondParameter<typeof customFetch>;
  },
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
  const queryOptions = getExportAuditLogsCsvQueryOptions(params, options);

  const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
    queryKey: QueryKey;
  };

  return { ...query, queryKey: queryOptions.queryKey };
}
