import { userAgentHeader } from './lib/branding'
import { applyProtocolUrlGates } from './lib/protocol/wireGates'
import type { AccountType } from './state/accountStore'

export function objectToForm(obj: { [key: string | number]: any }) {
  let form = new FormData()

  Object.keys(obj).forEach((key) => {
    form.append(key, obj[key])
  })

  return form
}

export async function postForm(
  url: string,
  data?: { [key: string | number]: any },
  token?: string,
  contentType?: string
) {
  // Send a POST request with data formatted with FormData returning JSON.
  // Mastodon Pass Phase 1 §2.4 / item 12 — User-Agent applied here too, since
  // this primitive is reached during the OAuth flow (auth-code exchange,
  // refresh, revoke) before any `ApiContext` exists.
  let headers: { [key: string]: string } = { 'User-Agent': userAgentHeader() }

  if (token) headers['Authorization'] = `Bearer ${token}`
  if (contentType) headers['Content-Type'] = contentType

  const resp = await fetch(url, {
    method: 'POST',
    body: data ? objectToForm(data) : undefined,
    headers,
  })

  return resp
}

export async function get(url: string, token?: string, data?: any) {
  let completeURL
  if (data) {
    let params = new URLSearchParams(data)
    completeURL = `${url}?${params.toString()}`
  } else {
    completeURL = url
  }

  const headers: { [key: string]: string } = { 'User-Agent': userAgentHeader() }
  if (token) headers['Authorization'] = `Bearer ${token}`

  const resp = await fetch(completeURL, {
    method: 'GET',
    redirect: 'follow',
    headers,
  })

  return resp
}

export function getJsonWithTimeout(
  url: string,
  token?: string,
  data?: any,
  customHeaders?: { [key: string]: string },
  timeout = 5000
): Promise<Response> {
  let completeURL
  if (data) {
    let params = new URLSearchParams(data)
    completeURL = `${url}?${params.toString()}`
  } else {
    completeURL = url
  }

  let reqHeaders: { [key: string]: string } = { 'User-Agent': userAgentHeader() }
  if (token) reqHeaders.Authorization = `Bearer ${token}`

  if (customHeaders) {
    reqHeaders = { ...reqHeaders, ...customHeaders }
  }

  return Promise.race([
    fetch(completeURL, {
      method: 'GET',
      redirect: 'follow',
      headers: reqHeaders,
    }),
    new Promise<Response>((_, reject) => {
      setTimeout(() => {
        reject(new Error(`Request for ${url} timed out after ${timeout} milliseconds`))
      }, timeout)
    }),
  ])
}

/**
 * Pre-auth credential probe.
 *
 * `protocol` must be supplied by the caller because no `ApiContext` exists
 * yet — every other authenticated request inherits its wire gates (`_pe=1`,
 * etc.) from `ApiContext.accountType`. The login-completion paths in
 * `AuthProvider` already classify the server via preflight before they reach
 * here, so the type is known at the call site.
 */
export async function verifyCredentials(
  domain: string,
  token: string,
  protocol: AccountType
) {
  const url = new URL('api/v1/accounts/verify_credentials', `https://${domain}`)
  applyProtocolUrlGates(url, protocol)
  const resp = await get(url.toString(), token)

  if (!resp.ok) {
    throw new Error(`Credential verification failed with status ${resp.status}`)
  }

  return resp.json()
}
