import { z } from '@botpress/sdk'

export class TodoistSyncClient {
  private readonly _fetch: typeof fetch

  public constructor({ accessToken }: { accessToken: string }) {
    this._fetch = (input, init = {}) => {
      const headers = new Headers(init.headers)
      headers.set('Authorization', `Bearer ${accessToken}`)
      return fetch(input, { ...init, headers })
    }
  }

  public async getAuthenticatedUserIdentity() {
    const identity = await this._get({
      endpoint: 'user',
      responseSchema: {
        id: z.string(),
        avatar_medium: z.string().optional(),
        full_name: z.string().optional(),
      },
    })

    return {
      id: identity.id,
      pictureUrl: identity.avatar_medium,
      name: identity.full_name,
    }
  }

  private async _post<T extends z.ZodRawShape>({
    endpoint,
    params,
    responseSchema,
  }: {
    endpoint: string
    responseSchema: T
    params?: Record<string, any>
  }): Promise<z.infer<z.ZodObject<T, 'strip'>>> {
    return this._sendRequest({
      endpoint,
      init: {
        method: 'POST',
        body: JSON.stringify(params),
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      },
      responseSchema,
    })
  }

  private async _get<T extends z.ZodRawShape>({
    endpoint,
    responseSchema,
  }: {
    endpoint: string
    responseSchema: T
  }): Promise<z.infer<z.ZodObject<T, 'strip'>>> {
    return this._sendRequest({
      endpoint,
      init: {
        method: 'GET',
      },
      responseSchema,
    })
  }

  private async _sendRequest<T extends z.ZodRawShape>({
    endpoint,
    init,
    responseSchema,
  }: {
    endpoint: string
    init: RequestInit
    responseSchema: T
  }): Promise<z.infer<z.ZodObject<T, 'strip'>>> {
    const response = await this._fetch(`https://api.todoist.com/sync/v9/${endpoint}`, init)

    if (!response.ok) {
      throw new Error(`Failed to send ${init.method} request to sync endpoint /${endpoint}: ${response.statusText}`)
    }

    return z.object(responseSchema).parse(await response.json())
  }
}
