import type { BaseOptions } from '../config.ts';
import type { FacetParams } from '../mod.ts';
import { isDefined } from './guards.ts';


/** Convert interface to a record-type, to allow passing it to index-signature methods */
export type Type<T extends object> = Record<keyof T, T[keyof T]>;
export type UrlParams = Record<string, PrimitiveParam>;
export type PrimitiveParam = string | number | boolean | string[] | number[] | boolean[] | null | undefined;

export class ResponseError extends Error {
  constructor(readonly details: { message: string }) {
    super(details.message);
  }
}

/**
 * Creates the base URL for the Elevate server, including required parameters such as:
 * - customerKey
 * - sessionKey
 * - market
 */
export async function createBaseUrl(endpoint: `queries/${string}` | `notifications/${string}`, config: BaseOptions) {
  const { market, clusterUrl, session } = config;
  const { customerKey, sessionKey } = await session();
  const url = new URL(`/api/storefront/v3/${endpoint}`, clusterUrl);

  addUrlParams(url, { market, customerKey, sessionKey });

  return url;
}

/**
 * Converts arrays of parameters into pipe separated values,
 * and adds their values to the provided URL object.
 */
export function addUrlParams(url: URL, params: UrlParams) {
  for (const [key, value] of Object.entries(params)) {
    if (isDefined(value)) {
      const values = Array.isArray(value) ? value : [value];
      url.searchParams.set(key, values.map(String).join('|'));
    }
  }
}

/** Converts `facets` parameter to a `UrlParams` object */
export function facetsToParams(facets: FacetParams = {}) {
  const result: UrlParams = {};
  for (const [name, value] of Object.entries(facets)) {
    if (Array.isArray(value) || typeof value !== 'object') {
      result[`f.${name}`] = value;
    } else {
      // TODO(csv): skip checking for undefined? Already filtered in `addUrlParams()`
      if (isDefined(value.min)) result[`f.${name}.min`] = value.min;
      if (isDefined(value.max)) result[`f.${name}.max`] = value.max;
    }
  }
  return result;
}
