import { __ } from '@wordpress/i18n';
import type { ApiRoute, ClubLocation, Config, Item, Payload, Sport } from './types';
export class ApiError extends Error {
    constructor(message: string, public readonly field: string | null = null, public readonly conflicts?: {
        id: number;
        reference: string;
        customer: string;
        resource_name: string;
        starts_at: string;
        ends_at: string;
    }[]) { super(message); }
}
export class CatalogApi {
    constructor(public readonly config: Config) { }
    protected url(route: string, parameters: Record<string, string> = {}): string {
        const [path, query] = route.split('?');
        route = path;
        const url = new URL(this.config.restUrl);
        if (query)
            new URLSearchParams(query).forEach((v, k) => url.searchParams.set(k, v));
        const restRoute = url.searchParams.get('rest_route');
        if (restRoute !== null)
            url.searchParams.set('rest_route', restRoute + route);
        else
            url.pathname += route;
        url.searchParams.set('_locale', 'user');
        Object.entries(parameters).forEach(([key, value]) => url.searchParams.set(key, value));
        return url.toString();
    }
    protected async request(route: string, method = 'GET', payload?: unknown, parameters?: Record<string, string>): Promise<Response> {
        const response = await fetch(this.url(route, parameters), {
            method,
            credentials: 'same-origin',
            headers: { 'X-WP-Nonce': this.config.nonce, 'Content-Type': 'application/json' },
            body: payload ? JSON.stringify(payload) : undefined,
        });
        if (!response.ok) {
            const error = await response.json().catch(() => null);
            throw new ApiError(error?.message ?? __('The request failed. Please try again.', 'abece-booking'), error?.data?.field ?? null, error?.data?.conflicts);
        }
        return response;
    }
    async list<T = Item>(route: ApiRoute, page: number, perPage = 25, filters: Record<string, string> = {}): Promise<{
        items: T[];
        pages: number;
        total: number;
    }> {
        const response = await this.request(route, 'GET', undefined, { ...filters, page: String(page), per_page: String(perPage) });
        return {
            items: await response.json(),
            pages: Number(response.headers.get('X-WP-TotalPages') ?? 1),
            total: Number(response.headers.get('X-WP-Total') ?? 0),
        };
    }
    async all<T>(route: ApiRoute, filters: Record<string, string> = {}): Promise<T[]> {
        const items: T[] = [];
        let page = 1;
        let pages = 1;
        do {
            const result = await this.list<T>(route, page++, 100, filters);
            items.push(...result.items);
            pages = result.pages;
        } while (page <= pages);
        return items;
    }
    async get<T>(route: ApiRoute, id: number): Promise<T> {
        return (await this.request(`${route}/${id}`)).json();
    }
    async sports(): Promise<Sport[]> {
        const sports: Sport[] = [];
        let page = 1;
        let pages = 1;
        do {
            const result = await this.list('sports', page++, 100);
            sports.push(...result.items);
            pages = result.pages;
        } while (page <= pages);
        return sports;
    }
    async save<T = Item>(route: ApiRoute, payload: Payload, id?: number): Promise<T> {
        const response = await this.request(id ? `${route}/${id}` : route, id ? 'PATCH' : 'POST', payload);
        return response.json();
    }
    async operation<T>(route: string, payload?: unknown): Promise<T> { return (await this.request(route, payload === undefined ? 'GET' : 'POST', payload)).json(); }
    async remove(route: ApiRoute, id: number): Promise<void> { await this.request(`${route}/${id}`, 'DELETE'); }
    async resourceSuggestions(sport?: string): Promise<{
        sort_order: number;
        name: string | null;
    }> { return (await this.request('resources/suggestions', 'GET', undefined, sport ? { sport_id: sport } : {})).json(); }
    async location(): Promise<ClubLocation> { return (await this.request('settings/location')).json(); }
    async searchLocation(address: string): Promise<{
        results: {
            latitude: number;
            longitude: number;
            label: string;
        }[];
    }> { return (await this.request('settings/location/search', 'POST', { address })).json(); }
    async saveLocation(payload: Payload): Promise<ClubLocation> { return (await this.request('settings/location', 'PATCH', payload)).json(); }
}
