// Единственное место, где приложение читает код ответа и разбирает тело отказа.
// Экран получает готовый текст, а не `Response`: второй разборщик кодов на
// клиенте разошёлся бы с первым.

const appRoot = '/app'

/** Ответ сервиса на вопрос «кто вошёл». */
export interface Me {
  id: string
  name: string
}

/** Единая форма тела отказа на адресах приложения. */
interface ErrorBody {
  error_code?: string
  message?: string
}

/**
 * Исход запроса. «Сервис вас не узнал» — свой исход, а не разновидность
 * неудачи: человек по нему делает не то, что по «сервис не отвечает». Прежде
 * этот исход уводил ко входу; уводить стало некуда — своего входа у сервиса
 * нет, а заголовок ставит обратный прокси, — и различие сохраняется ради
 * текста, а не ради перехода.
 */
export type ApiResult<T> =
  | { status: 'ok'; value: T }
  | { status: 'unrecognized'; message: string }
  | { status: 'failed'; message: string }

const noConnectionMessage = 'Связи нет'
const unknownFailureMessage = 'Сервис не отвечает как надо'

async function failureMessage(response: Response): Promise<string> {
  try {
    const body = (await response.json()) as ErrorBody
    if (body.message) {
      return body.message
    }
  } catch {
    // Тело не разобралось — своего текста под код ответа не сочиняем.
  }
  return unknownFailureMessage
}

async function request<T>(path: string): Promise<ApiResult<T>> {
  let response: Response
  try {
    response = await fetch(appRoot + path, {
      headers: { Accept: 'application/json' },
    })
  } catch {
    // Сорванный запрос — состояние, а не ошибка: показываем строкой.
    return { status: 'failed', message: noConnectionMessage }
  }

  if (response.status === 401) {
    // Текст берётся у сервера, а не сочиняется здесь: единая форма отказа —
    // обязанность API, и второй словарь на клиенте разошёлся бы с первым.
    // Свой исход у этого кода всё равно есть: «сервис вас не узнал» и «сервис
    // не отвечает» ведут человека к разному.
    return { status: 'unrecognized', message: await failureMessage(response) }
  }

  if (!response.ok) {
    return { status: 'failed', message: await failureMessage(response) }
  }

  try {
    return { status: 'ok', value: (await response.json()) as T }
  } catch {
    return { status: 'failed', message: unknownFailureMessage }
  }
}

/**
 * Спрашивает сервис, кто пришёл. Заголовок ставит обратный прокси, и приложение
 * его не видит вовсе — этот адрес единственный способ узнать вошедшего.
 */
export function fetchMe(): Promise<ApiResult<Me>> {
  return request<Me>('/me')
}
