import { apiClient, apiQueryClient, useAuthenticatedQuery, } from "@/shared/api"; import type { BankOperation, BankOperations, BankSummary } from "./types"; import { moneyCents } from "../model/money"; const OPERATIONS_PAGE_SIZE = 100; function sortBankSummary(data: BankSummary): BankSummary { return { ...data, categories: data.categories.toSorted((a, b) => { const left = moneyCents(a.net.value); const right = moneyCents(b.net.value); if (left === null || right === null) throw new Error("Invalid bank summary amount"); if (left > right) return -1; return left < right ? 1 : 0; }), }; } function useBankCard(id?: string) { return useAuthenticatedQuery({ ...apiQueryClient.queryOptions("get", "/api/v1/bank-cards/{id}", { params: { path: { id: id ?? "" } }, }), enabled: Boolean(id), }); } function useBankCards(isIncludeArchived = false) { return useAuthenticatedQuery( apiQueryClient.queryOptions("get", "/api/v1/bank-cards", { params: { query: { includeArchived: isIncludeArchived ? "true" : "false" }, }, }), ); } function useBankCategories() { return useAuthenticatedQuery( apiQueryClient.queryOptions("get", "/api/v1/bank-cards/categories"), ); } function useBankExpenses( id: string | undefined, query: { limit?: number; page: number }, ) { return useAuthenticatedQuery({ ...apiQueryClient.queryOptions("get", "/api/v1/bank-cards/{id}/expenses", { params: { path: { id: id ?? "" }, query }, }), enabled: Boolean(id), }); } function useBankOperation(id?: string) { return useAuthenticatedQuery({ ...apiQueryClient.queryOptions( "get", "/api/v1/bank-cards/operations/{id}", { params: { path: { id: id ?? "" } } }, ), enabled: Boolean(id), }); } function useBankOperations( id: string | undefined, query: { from?: string; limit?: "all" | number; page: number; to?: string; }, ) { const isAll = query.limit === "all"; const parameters = { path: { id: id ?? "" }, query: { ...query, limit: query.limit === "all" ? OPERATIONS_PAGE_SIZE : query.limit, page: isAll ? 1 : query.page, }, }; const options = apiQueryClient.queryOptions( "get", "/api/v1/bank-cards/{id}/operations", { params: parameters }, ); // eslint-disable-next-line @tanstack/query/exhaustive-deps -- Generated options.queryKey already contains params; retain the shared cache identity. return useAuthenticatedQuery({ enabled: Boolean(id && query.from && query.to), placeholderData: ( previousData: BankOperations | undefined, previousQuery, ) => { const previous = previousQuery?.queryKey[2]?.params; return !isAll && previousQuery?.queryKey.length === options.queryKey.length && previous?.path.id === id && previous?.query?.from === query.from && previous?.query?.to === query.to && previous?.query?.limit === query.limit ? previousData : undefined; }, queryFn: async ({ signal }) => { const items: BankOperation[] = []; for (let page = parameters.query.page; ; page++) { const { data, error } = await apiClient.GET( "/api/v1/bank-cards/{id}/operations", { params: { ...parameters, query: { ...parameters.query, page } }, signal, }, ); // eslint-disable-next-line @typescript-eslint/only-throw-error -- Preserve generated ProblemDetails for apiErrorCode and field-error consumers. if (error) throw error; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- An empty HTTP body can violate the generated response contract. if (!data) throw new Error("Missing bank operations response"); if (!isAll) return data; items.push(...data.items); if (!data.hasNext) return { ...data, hasNext: false, items, page: 1 }; } }, queryKey: isAll ? ([...options.queryKey, "all"] as const) : options.queryKey, }); } function useBankSummary( period: { from: string; to: string }, cardId?: string, ) { return useAuthenticatedQuery({ ...apiQueryClient.queryOptions("get", "/api/v1/bank-cards/summary", { params: { query: { ...period, cardId } }, }), select: sortBankSummary, }); } export { sortBankSummary, useBankCard, useBankCards, useBankCategories, useBankExpenses, useBankOperation, useBankOperations, useBankSummary, };