import { z } from "zod" import { useInfiniteQuery, useQuery } from "@tanstack/react-query" import { paginatedSchema } from "@/lib/api/common" import { get } from "@/lib/api/client" const PAGE_SIZE = 20 export const searchQueryParamsSchema = z.object({ q: z.string().optional(), genre: z.string().optional(), year: z.number().optional(), person_id: z.string().uuid().optional(), department: z.string().optional(), language: z.string().optional(), limit: z.number().optional(), offset: z.number().optional(), }) export type SearchQueryParams = z.infer export const movieSearchHitDtoSchema = z.object({ movie_id: z.string().uuid(), title: z.string(), release_year: z.number().optional(), director: z.string().optional(), poster_path: z.string().optional(), genres: z.array(z.string()), }) export type MovieSearchHitDto = z.infer export const personSearchHitDtoSchema = z.object({ person_id: z.string().uuid(), name: z.string(), known_for_department: z.string().optional(), profile_path: z.string().optional(), known_for_titles: z.array(z.string()), }) export type PersonSearchHitDto = z.infer export const searchResponseSchema = z.object({ movies: paginatedSchema(movieSearchHitDtoSchema), people: paginatedSchema(personSearchHitDtoSchema), }) export type SearchResponse = z.infer export const personDtoSchema = z.object({ id: z.string().uuid(), external_id: z.string(), name: z.string(), known_for_department: z.string().optional(), profile_path: z.string().optional(), biography: z.string().optional(), birthday: z.string().optional(), deathday: z.string().optional(), place_of_birth: z.string().optional(), also_known_as: z.array(z.string()).default([]), homepage: z.string().optional(), imdb_url: z.string().optional(), enriched: z.boolean().default(false), }) export type PersonDto = z.infer export const castCreditDtoSchema = z.object({ movie_id: z.string().uuid(), title: z.string(), release_year: z.number().optional(), character: z.string(), poster_path: z.string().optional(), }) export type CastCreditDto = z.infer export const crewCreditDtoSchema = z.object({ movie_id: z.string().uuid(), title: z.string(), release_year: z.number().optional(), job: z.string(), department: z.string(), poster_path: z.string().optional(), }) export type CrewCreditDto = z.infer export const personCreditsDtoSchema = z.object({ person: personDtoSchema, cast: z.array(castCreditDtoSchema), crew: z.array(crewCreditDtoSchema), }) export type PersonCreditsDto = z.infer function search(params: SearchQueryParams) { return get("/search", params) } function getPerson(id: string) { return get(`/people/${id}`) } function getPersonCredits(id: string) { return get(`/people/${id}/credits`) } export const searchKeys = { all: ["search"] as const, query: (params: SearchQueryParams) => [...searchKeys.all, params] as const, person: (id: string) => ["people", id] as const, personCredits: (id: string) => ["people", id, "credits"] as const, } export function useSearch(params: SearchQueryParams) { return useQuery({ queryKey: searchKeys.query(params), queryFn: () => search(params), enabled: !!params.q || !!params.genre || !!params.person_id, }) } export function useInfiniteSearch( params: Omit, ) { return useInfiniteQuery({ queryKey: searchKeys.query(params), queryFn: ({ pageParam = 0 }) => search({ ...params, limit: PAGE_SIZE, offset: pageParam }), initialPageParam: 0, getNextPageParam: (last) => { const next = last.movies.offset + last.movies.limit return next < last.movies.total_count ? next : undefined }, enabled: !!params.q || !!params.genre || !!params.person_id, }) } export function usePerson(id: string) { return useQuery({ queryKey: searchKeys.person(id), queryFn: () => getPerson(id), enabled: !!id, }) } export function usePersonCredits(id: string) { return useQuery({ queryKey: searchKeys.personCredits(id), queryFn: () => getPersonCredits(id), enabled: !!id, }) }