import { AxiosError, AxiosResponse } from 'axios'

export type HttpStatusName = keyof typeof httpStatusCode
export type HttpStatus = typeof httpStatusCode[HttpStatusName]
export type HttpStatusRange = keyof typeof httpStatusCodeRange

type HttpStatusResponse = {
  is: (status: HttpStatusName) => boolean,
  isInRange: (statusRange: HttpStatusRange) => boolean,
}

function isAxiosResponse(value: unknown): value is AxiosResponse {
  const response = value as AxiosResponse
  return typeof response.status === 'number'
}

function isAxiosError(value: unknown): value is AxiosError {
  const error = value as AxiosError
  return isAxiosResponse(error.response)
}

function getStatusCode(value: unknown): number {
  if (typeof value === 'number') {
    return value
  }

  if (isAxiosResponse(value)) {
    return value.status
  }

  if (isAxiosError(value)) {
    return getStatusCode(value.response)
  }

  return 0
}

export function httpStatus(value: unknown): HttpStatusResponse {
  const status = getStatusCode(value)

  return {
    is: (statusName: HttpStatusName) => httpStatusCode[statusName] === status,
    isInRange: (statusRange: HttpStatusRange) => httpStatusCodeRange[statusRange].includes(status),
  }
}

export const httpStatusCode = {
  Continue: 100,
  SwitchingProtocols: 101,
  Processing: 102,
  OK: 200,
  Created: 201,
  Accepted: 202,
  NonAuthoritativeInformation: 203,
  NoContent: 204,
  ResetContent: 205,
  PartialContent: 206,
  MultiStatus: 207,
  MultipleChoices: 300,
  MovedPermanently: 301,
  MovedTemporarily: 302,
  SeeOther: 303,
  NotModified: 304,
  UseProxy: 305,
  TemporaryRedirect: 307,
  PermanentRedirect: 308,
  BadRequest: 400,
  Unauthorized: 401,
  PaymentRequired: 402,
  Forbidden: 403,
  NotFound: 404,
  MethodNotAllowed: 405,
  NotAcceptable: 406,
  ProxyAuthenticationRequired: 407,
  RequestTimeout: 408,
  Conflict: 409,
  Gone: 410,
  LengthRequired: 411,
  PreconditionFailed: 412,
  RequestEntityTooLarge: 413,
  RequestURITooLong: 414,
  UnsupportedMediaType: 415,
  RequestedRangeNotSatisfiable: 416,
  ExpectationFailed: 417,
  ImATeapot: 418,
  InsufficientSpaceOnResource: 419,
  MethodFailure: 420,
  MisdirectedRequest: 421,
  UnprocessableEntity: 422,
  Locked: 423,
  FailedDependency: 424,
  PreconditionRequired: 428,
  TooManyRequests: 429,
  RequestHeaderFieldsTooLarge: 431,
  UnavailableForLegalReasons: 451,
  InternalServerError: 500,
  NotImplemented: 501,
  BadGateway: 502,
  ServiceUnavailable: 503,
  GatewayTimeout: 504,
  HTTPVersionNotSupported: 505,
  InsufficientStorage: 507,
  NetworkAuthenticationRequired: 511,
}

export const httpStatusCodeRange = {
  informational: [
    httpStatusCode.Continue,
    httpStatusCode.SwitchingProtocols,
    httpStatusCode.Processing,
  ],
  successful: [
    httpStatusCode.OK,
    httpStatusCode.Created,
    httpStatusCode.Accepted,
    httpStatusCode.NonAuthoritativeInformation,
    httpStatusCode.NoContent,
    httpStatusCode.ResetContent,
    httpStatusCode.PartialContent,
    httpStatusCode.MultiStatus,
  ],
  redirection: [
    httpStatusCode.MultipleChoices,
    httpStatusCode.MovedPermanently,
    httpStatusCode.MovedTemporarily,
    httpStatusCode.SeeOther,
    httpStatusCode.NotModified,
    httpStatusCode.UseProxy,
    httpStatusCode.TemporaryRedirect,
    httpStatusCode.PermanentRedirect,
  ],
  clientError: [
    httpStatusCode.BadRequest,
    httpStatusCode.Unauthorized,
    httpStatusCode.PaymentRequired,
    httpStatusCode.Forbidden,
    httpStatusCode.NotFound,
    httpStatusCode.MethodNotAllowed,
    httpStatusCode.NotAcceptable,
    httpStatusCode.ProxyAuthenticationRequired,
    httpStatusCode.RequestTimeout,
    httpStatusCode.Conflict,
    httpStatusCode.Gone,
    httpStatusCode.LengthRequired,
    httpStatusCode.PreconditionFailed,
    httpStatusCode.RequestEntityTooLarge,
    httpStatusCode.RequestURITooLong,
    httpStatusCode.UnsupportedMediaType,
    httpStatusCode.RequestedRangeNotSatisfiable,
    httpStatusCode.ExpectationFailed,
    httpStatusCode.ImATeapot,
    httpStatusCode.InsufficientSpaceOnResource,
    httpStatusCode.MethodFailure,
    httpStatusCode.MisdirectedRequest,
    httpStatusCode.UnprocessableEntity,
    httpStatusCode.Locked,
    httpStatusCode.FailedDependency,
    httpStatusCode.PreconditionRequired,
    httpStatusCode.TooManyRequests,
    httpStatusCode.RequestHeaderFieldsTooLarge,
    httpStatusCode.UnavailableForLegalReasons,
  ],
  serverError: [
    httpStatusCode.InternalServerError,
    httpStatusCode.NotImplemented,
    httpStatusCode.BadGateway,
    httpStatusCode.ServiceUnavailable,
    httpStatusCode.GatewayTimeout,
    httpStatusCode.HTTPVersionNotSupported,
    httpStatusCode.InsufficientStorage,
    httpStatusCode.NetworkAuthenticationRequired,
  ],
}