import type { StringMapping } from '$lib/utils/types';
import { error } from '@sveltejs/kit';

/**
 * An API response indicating an item count.
 */
export type ItemCount = {
  total: number
}

/**
 * An API response indicating a token.
 */
export type Token = {
  token: string
}

/**
 * An API response representing a User object.
 */
export type User = {
  id: number,
  email: string,
  first_name: string,
  last_name: string,
  created: Date,
  updated: Date,
  is_admin: boolean,
}

/**
 * An API response representing a TodoItem object.
 */
export type TodoItem = {
  id: number,
  title: string,
  description: string,
  done: boolean,
  created: Date,
  updated: Date,
  finished: Date,
}

/**
 * Allowed API-request request types.
 */
type RequestMethod = 'GET' | 'POST' | 'UPDATE' | 'DELETE';

/**
 * Allowed options to an Enpoint's `call()`-method
 */
type EndpointCallOptions = {
  body?: any,
  queryParameters?: StringMapping
}

/**
 * Represents an external API endpoint.
 */
export class Endpoint<T> {
  /** The URL of the API endpoint. */
  readonly url: string;

  /** The request method to be used for API calls. */
  readonly requestMethod: RequestMethod;

  /** The headers to be included in API requests. */
  readonly requestHeaders: StringMapping;

  /** The function to use for making API requests. */
  readonly fetchFunction: Function;

  /**
   * Constructs a new `Endpoint` instance.
   * @param url - The URL of the API endpoint.
   * @param requestMethod - The request method to be used for API calls. (Default: 'GET')
   * @param requestHeaders - The headers to be included in API requests. (Default: {})
   * @param fetchFunction - The function to use for making API requests. (Default: fetch)
   */
  constructor(
    url: string,
    requestMethod: RequestMethod = 'GET',
    requestHeaders: StringMapping = {},
    fetchFunction: Function = fetch
  ) {
    this.url = url;
    this.requestMethod = requestMethod;
    this.requestHeaders = requestHeaders;
    this.fetchFunction = fetchFunction;
  }

  /**
   * Calls the API endpoint with optional query parameters.
   *
   * @param queryParameters - The query parameters to be included in the API call. (Default: new URLSearchParams({}))
   * @returns A Promise that resolves to the JSON response from the API.
   * @throws {error} If the API request fails or returns an error response.
   */
  async call(options: EndpointCallOptions = {}): Promise<T> {
    let endpointUrl = this.url;
    if ('queryParameters' in options) {
      endpointUrl += `?${new URLSearchParams(options.queryParameters)}`;
    }
    console.log(endpointUrl)

    const response = await this.fetchFunction(endpointUrl, {
      method: this.requestMethod,
      headers: this.requestHeaders,
      body: 'body' in options ? JSON.stringify(options.body) : null
    });
    const responseJson = await response.json();

    if (!response.ok) {
      if ('detail' in responseJson) {
        if (typeof responseJson.detail === 'string') {
          throw error(response.status, responseJson.detail);
        } else if (Array.isArray(responseJson.detail)) {
          if ('msg' in responseJson.detail[0] && typeof responseJson.detail[0].msg === 'string') {
            throw error(response.status, responseJson.detail[0].msg);
          }
        }
      }
      throw error(response.status, `API request failed: ${response.statusText}`);
    }

    return responseJson as T;
  }
}
