export type GatewayRoute =
  | 'search.products'
  | 'search.suggest'
  | 'search.explain'
  | 'products.show'
  | 'catalog.products'
  | 'catalog.facets'
  | 'catalog.offers'
  | 'compare.products'
  | 'price.history'
  | 'mobile.bootstrap'
  | 'mobile.me'
  | 'mobile.compare'
  | 'graphql.execute'
  | 'graphql.persisted';

export interface ClientOptions {
  baseUrl?: string;
  gatewayToken?: string;
  fetchImpl?: typeof fetch;
}

export interface CatalogParams {
  q?: string;
  page?: number;
  per?: number;
  sort?: string;
  min?: number;
  max?: number;
  category_id?: number;
  category_slug?: string;
  brands?: string[];
  stores?: string[];
  store_ids?: number[];
  free_shipping?: boolean;
  coupon?: boolean;
  in_stock?: boolean;
  sponsored?: boolean;
}

export class UcuzabulanApiClient {
  private readonly baseUrl: string;
  private readonly gatewayToken?: string;
  private readonly fetchImpl: typeof fetch;

  constructor(options: ClientOptions = {}) {
    this.baseUrl = (options.baseUrl || 'https://ucuzabulan.com').replace(/\/+$/, '');
    this.gatewayToken = options.gatewayToken;
    this.fetchImpl = options.fetchImpl || fetch.bind(globalThis);
  }

  private buildQuery(params: Record<string, any>): string {
    const usp = new URLSearchParams();
    Object.entries(params || {}).forEach(([key, value]) => {
      if (value === undefined || value === null || value === '') return;
      if (Array.isArray(value)) {
        if (value.length === 0) return;
        usp.set(key, value.join(','));
        return;
      }
      usp.set(key, String(value));
    });
    const raw = usp.toString();
    return raw ? `?${raw}` : '';
  }

  private async getJson(path: string): Promise<any> {
    const res = await this.fetchImpl(this.baseUrl + path, { headers: { 'Accept': 'application/json' } });
    if (!res.ok) throw new Error(`GET ${path} failed: ${res.status}`);
    return res.json();
  }

  private async postJson(path: string, body: Record<string, any>, withGatewayToken = false): Promise<any> {
    const headers: Record<string, string> = { 'Content-Type': 'application/json', 'Accept': 'application/json' };
    if (withGatewayToken && this.gatewayToken) headers['Authorization'] = `Bearer ${this.gatewayToken}`;
    const res = await this.fetchImpl(this.baseUrl + path, {
      method: 'POST',
      headers,
      body: JSON.stringify(body),
    });
    if (!res.ok) throw new Error(`POST ${path} failed: ${res.status}`);
    return res.json();
  }

  searchProducts(q: string, limit = 24, mode?: 'failover' | 'federated') {
    return this.getJson(`/api/v1/search.php${this.buildQuery({ q, limit, mode })}`);
  }

  searchExplain(q: string, limit = 12, mode?: 'failover' | 'federated') {
    return this.getJson(`/api/v1/search_explain.php${this.buildQuery({ q, limit, mode })}`);
  }

  searchSuggest(q: string, limit = 8) {
    return this.getJson(`/api/v1/search_suggest.php${this.buildQuery({ q, limit })}`);
  }

  productBySlug(slug: string) {
    return this.getJson(`/api/v1/product.php${this.buildQuery({ slug })}`);
  }

  catalogProducts(params: CatalogParams = {}) {
    return this.getJson(`/api/v1/catalog.php${this.buildQuery(params)}`);
  }

  catalogFacets(params: Omit<CatalogParams, 'page' | 'per' | 'sort' | 'min' | 'max'> = {}) {
    return this.getJson(`/api/v1/facets.php${this.buildQuery(params as Record<string, any>)}`);
  }

  productOffers(productId: number, params: Record<string, any> = {}) {
    return this.getJson(`/api/v1/offers.php${this.buildQuery({ product_id: productId, ...params })}`);
  }

  priceHistory(productId: number, days = 30) {
    return this.getJson(`/api/v1/price_history.php${this.buildQuery({ product_id: productId, days })}`);
  }

  mobileBootstrap(bannerLimit = 6, categoryLimit = 10, pickLimit = 12) {
    return this.getJson(`/api/v1/mobile_bootstrap.php${this.buildQuery({ banner_limit: bannerLimit, category_limit: categoryLimit, pick_limit: pickLimit })}`);
  }

  mobileMe() {
    return this.getJson('/api/v1/mobile_me.php');
  }

  mobileCompare(ids: number[]) {
    return this.getJson(`/api/v1/mobile_compare.php${this.buildQuery({ ids })}`);
  }

  graphql(query: string, variables: Record<string, any> = {}) {
    return this.postJson('/api/graphql.php', { query, variables });
  }

  graphqlPersisted(key: string, variables: Record<string, any> = {}) {
    return this.postJson('/api/graphql_persisted.php', { key, variables });
  }

  gateway(route: GatewayRoute, params: Record<string, any> = {}) {
    return this.postJson('/api/gateway.php', { route, ...params }, true);
  }
}

export default UcuzabulanApiClient;