import type { Prisma } from "@capakraken/db"; import type { TRPCContext } from "../../trpc.js"; import { withToolAccess, type ToolContext, type ToolDef, type ToolExecutor } from "./shared.js"; type AssistantToolErrorResult = { error: string }; type CountryRecord = { id: string; code: string; name: string; dailyWorkingHours: number; scheduleRules?: Prisma.JsonValue | null; isActive?: boolean | null; metroCities?: Array<{ id: string; name: string }> | null; _count?: { resources?: number | null } | null; }; type CountryReadmodelsDeps = { createCountryCaller: (ctx: TRPCContext) => { list: (params?: { isActive: boolean }) => Promise; getByIdentifier: (params: { identifier: string }) => Promise; }; createScopedCallerContext: (ctx: ToolContext) => TRPCContext; formatCountry: (country: CountryRecord) => unknown; toAssistantCountryNotFoundError: ( error: unknown, ) => AssistantToolErrorResult | null; }; export const countryReadmodelToolDefinitions: ToolDef[] = withToolAccess([ { type: "function", function: { name: "list_countries", description: "List countries including working hours, schedule rules, active flag, and metro cities.", parameters: { type: "object", properties: { includeInactive: { type: "boolean", description: "Include inactive countries. Default: false." }, search: { type: "string", description: "Optional country code or name search." }, }, }, }, }, { type: "function", function: { name: "get_country", description: "Get one country with schedule rules, active flag, metro cities, and resource count. Accepts ID, code, or name.", parameters: { type: "object", properties: { identifier: { type: "string", description: "Country ID, code, or name." }, }, required: ["identifier"], }, }, }, ], { get_country: { requiresResourceOverview: true, }, }); export function createCountryReadmodelExecutors( deps: CountryReadmodelsDeps, ): Record { return { async list_countries( params: { includeInactive?: boolean; search?: string }, ctx: ToolContext, ) { const caller = deps.createCountryCaller(deps.createScopedCallerContext(ctx)); const countries = await caller.list( params.includeInactive ? undefined : { isActive: true }, ); const normalizedSearch = params.search?.trim().toLowerCase(); const filteredCountries = normalizedSearch ? countries.filter((country) => country.code.toLowerCase().includes(normalizedSearch) || country.name.toLowerCase().includes(normalizedSearch)) : countries; return { count: filteredCountries.length, countries: filteredCountries.map(deps.formatCountry), }; }, async get_country( params: { identifier: string }, ctx: ToolContext, ) { const caller = deps.createCountryCaller(deps.createScopedCallerContext(ctx)); let country; try { country = await caller.getByIdentifier({ identifier: params.identifier }); } catch (error) { const mapped = deps.toAssistantCountryNotFoundError(error); if (mapped) { return mapped; } throw error; } return deps.formatCountry(country); }, }; }