export type IntDict = Readonly<Record<string, number>>;

// eslint-disable-next-line  @typescript-eslint/no-explicit-any
type Simplify<T> = T extends any[] | Date ? T : {
  [K in keyof T]: T[K];
} & {};

export const ERROR = {
  OK: 0,
  INTERNAL: 1,
  STRICT: 2,
  CR_EXPECTED: 25,
  LF_EXPECTED: 3,
  UNEXPECTED_CONTENT_LENGTH: 4,
  UNEXPECTED_SPACE: 30,
  CLOSED_CONNECTION: 5,
  INVALID_METHOD: 6,
  INVALID_URL: 7,
  INVALID_CONSTANT: 8,
  INVALID_VERSION: 9,
  INVALID_HEADER_TOKEN: 10,
  INVALID_CONTENT_LENGTH: 11,
  INVALID_CHUNK_SIZE: 12,
  INVALID_STATUS: 13,
  INVALID_EOF_STATE: 14,
  INVALID_TRANSFER_ENCODING: 15,

  CB_MESSAGE_BEGIN: 16,
  CB_HEADERS_COMPLETE: 17,
  CB_MESSAGE_COMPLETE: 18,
  CB_CHUNK_HEADER: 19,
  CB_CHUNK_COMPLETE: 20,

  PAUSED: 21,
  PAUSED_UPGRADE: 22,
  PAUSED_H2_UPGRADE: 23,

  USER: 24,

  CB_URL_COMPLETE: 26,
  CB_STATUS_COMPLETE: 27,
  CB_METHOD_COMPLETE: 32,
  CB_VERSION_COMPLETE: 33,
  CB_HEADER_FIELD_COMPLETE: 28,
  CB_HEADER_VALUE_COMPLETE: 29,
  CB_CHUNK_EXTENSION_NAME_COMPLETE: 34,
  CB_CHUNK_EXTENSION_VALUE_COMPLETE: 35,
  CB_RESET: 31,
  CB_PROTOCOL_COMPLETE: 38,
} as const;

export const TYPE = {
  BOTH: 0, // default
  REQUEST: 1,
  RESPONSE: 2,
} as const;

export const FLAGS = {
  CONNECTION_KEEP_ALIVE: 1 << 0,
  CONNECTION_CLOSE: 1 << 1,
  CONNECTION_UPGRADE: 1 << 2,
  CHUNKED: 1 << 3,
  UPGRADE: 1 << 4,
  CONTENT_LENGTH: 1 << 5,
  SKIPBODY: 1 << 6,
  TRAILING: 1 << 7,
  // 1 << 8 is unused
  TRANSFER_ENCODING: 1 << 9,
} as const;

export const LENIENT_FLAGS = {
  HEADERS: 1 << 0,
  CHUNKED_LENGTH: 1 << 1,
  KEEP_ALIVE: 1 << 2,
  TRANSFER_ENCODING: 1 << 3,
  VERSION: 1 << 4,
  DATA_AFTER_CLOSE: 1 << 5,
  OPTIONAL_LF_AFTER_CR: 1 << 6,
  OPTIONAL_CRLF_AFTER_CHUNK: 1 << 7,
  OPTIONAL_CR_BEFORE_LF: 1 << 8,
  SPACES_AFTER_CHUNK_SIZE: 1 << 9,
  HEADER_VALUE_RELAXED: 1 << 10,
} as const;

export const STATUSES = {
  CONTINUE: 100,
  SWITCHING_PROTOCOLS: 101,
  PROCESSING: 102,
  EARLY_HINTS: 103,
  RESPONSE_IS_STALE: 110, // Unofficial
  REVALIDATION_FAILED: 111, // Unofficial
  DISCONNECTED_OPERATION: 112, // Unofficial
  HEURISTIC_EXPIRATION: 113, // Unofficial
  MISCELLANEOUS_WARNING: 199, // Unofficial
  OK: 200,
  CREATED: 201,
  ACCEPTED: 202,
  NON_AUTHORITATIVE_INFORMATION: 203,
  NO_CONTENT: 204,
  RESET_CONTENT: 205,
  PARTIAL_CONTENT: 206,
  MULTI_STATUS: 207,
  ALREADY_REPORTED: 208,
  TRANSFORMATION_APPLIED: 214, // Unofficial
  IM_USED: 226,
  MISCELLANEOUS_PERSISTENT_WARNING: 299, // Unofficial
  MULTIPLE_CHOICES: 300,
  MOVED_PERMANENTLY: 301,
  FOUND: 302,
  SEE_OTHER: 303,
  NOT_MODIFIED: 304,
  USE_PROXY: 305,
  SWITCH_PROXY: 306, // No longer used
  TEMPORARY_REDIRECT: 307,
  PERMANENT_REDIRECT: 308,
  BAD_REQUEST: 400,
  UNAUTHORIZED: 401,
  PAYMENT_REQUIRED: 402,
  FORBIDDEN: 403,
  NOT_FOUND: 404,
  METHOD_NOT_ALLOWED: 405,
  NOT_ACCEPTABLE: 406,
  PROXY_AUTHENTICATION_REQUIRED: 407,
  REQUEST_TIMEOUT: 408,
  CONFLICT: 409,
  GONE: 410,
  LENGTH_REQUIRED: 411,
  PRECONDITION_FAILED: 412,
  PAYLOAD_TOO_LARGE: 413,
  URI_TOO_LONG: 414,
  UNSUPPORTED_MEDIA_TYPE: 415,
  RANGE_NOT_SATISFIABLE: 416,
  EXPECTATION_FAILED: 417,
  IM_A_TEAPOT: 418,
  PAGE_EXPIRED: 419, // Unofficial
  ENHANCE_YOUR_CALM: 420, // Unofficial
  MISDIRECTED_REQUEST: 421,
  UNPROCESSABLE_ENTITY: 422,
  LOCKED: 423,
  FAILED_DEPENDENCY: 424,
  TOO_EARLY: 425,
  UPGRADE_REQUIRED: 426,
  PRECONDITION_REQUIRED: 428,
  TOO_MANY_REQUESTS: 429,
  REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL: 430, // Unofficial
  REQUEST_HEADER_FIELDS_TOO_LARGE: 431,
  LOGIN_TIMEOUT: 440, // Unofficial
  NO_RESPONSE: 444, // Unofficial
  RETRY_WITH: 449, // Unofficial
  BLOCKED_BY_PARENTAL_CONTROL: 450, // Unofficial
  UNAVAILABLE_FOR_LEGAL_REASONS: 451,
  CLIENT_CLOSED_LOAD_BALANCED_REQUEST: 460, // Unofficial
  INVALID_X_FORWARDED_FOR: 463, // Unofficial
  REQUEST_HEADER_TOO_LARGE: 494, // Unofficial
  SSL_CERTIFICATE_ERROR: 495, // Unofficial
  SSL_CERTIFICATE_REQUIRED: 496, // Unofficial
  HTTP_REQUEST_SENT_TO_HTTPS_PORT: 497, // Unofficial
  INVALID_TOKEN: 498, // Unofficial
  CLIENT_CLOSED_REQUEST: 499, // Unofficial
  INTERNAL_SERVER_ERROR: 500,
  NOT_IMPLEMENTED: 501,
  BAD_GATEWAY: 502,
  SERVICE_UNAVAILABLE: 503,
  GATEWAY_TIMEOUT: 504,
  HTTP_VERSION_NOT_SUPPORTED: 505,
  VARIANT_ALSO_NEGOTIATES: 506,
  INSUFFICIENT_STORAGE: 507,
  LOOP_DETECTED: 508,
  BANDWIDTH_LIMIT_EXCEEDED: 509,
  NOT_EXTENDED: 510,
  NETWORK_AUTHENTICATION_REQUIRED: 511,
  WEB_SERVER_UNKNOWN_ERROR: 520, // Unofficial
  WEB_SERVER_IS_DOWN: 521, // Unofficial
  CONNECTION_TIMEOUT: 522, // Unofficial
  ORIGIN_IS_UNREACHABLE: 523, // Unofficial
  TIMEOUT_OCCURED: 524, // Unofficial
  SSL_HANDSHAKE_FAILED: 525, // Unofficial
  INVALID_SSL_CERTIFICATE: 526, // Unofficial
  RAILGUN_ERROR: 527, // Unofficial
  SITE_IS_OVERLOADED: 529, // Unofficial
  SITE_IS_FROZEN: 530, // Unofficial
  IDENTITY_PROVIDER_AUTHENTICATION_ERROR: 561, // Unofficial
  NETWORK_READ_TIMEOUT: 598, // Unofficial
  NETWORK_CONNECT_TIMEOUT: 599, // Unofficial
} as const;

export const FINISH = {
  SAFE: 0,
  SAFE_WITH_CB: 1,
  UNSAFE: 2,
} as const;

export const HEADER_STATE = {
  GENERAL: 0,
  CONNECTION: 1,
  CONTENT_LENGTH: 2,
  TRANSFER_ENCODING: 3,
  UPGRADE: 4,
  CONNECTION_KEEP_ALIVE: 5,
  CONNECTION_CLOSE: 6,
  CONNECTION_UPGRADE: 7,
  TRANSFER_ENCODING_CHUNKED: 8,
} as const;

export const METHODS_HTTP1_HEAD = {
  HEAD: 2,
} as const;

/**
 * HTTP methods as defined by RFC-9110 and other specifications.
 * @see https://httpwg.org/specs/rfc9110.html#method.definitions
 */
export const METHODS_BASIC_HTTP = {
  DELETE: 0,
  GET: 1,
  ...METHODS_HTTP1_HEAD,
  POST: 3,
  PUT: 4,
  CONNECT: 5,
  OPTIONS: 6,
  TRACE: 7,

  /**
   * @see https://www.rfc-editor.org/rfc/rfc5789.html
   */
  PATCH: 28,

  /**
   * @see https://www.rfc-editor.org/rfc/rfc10008.html
   */
  QUERY: 46,

  /* RFC-2068, section 19.6.1.2 */
  LINK: 31,
  UNLINK: 32,
} as const;

export const METHODS_WEBDAV = {
  COPY: 8,
  LOCK: 9,
  MKCOL: 10,
  MOVE: 11,
  PROPFIND: 12,
  PROPPATCH: 13,
  SEARCH: 14,
  UNLOCK: 15,
  BIND: 16,
  REBIND: 17,
  UNBIND: 18,
  ACL: 19,
} as const;

export const METHODS_SUBVERSION = {
  REPORT: 20,
  MKACTIVITY: 21,
  CHECKOUT: 22,
  MERGE: 23,
} as const;

export const METHODS_UPNP = {
  'M-SEARCH': 24,
  NOTIFY: 25,
  SUBSCRIBE: 26,
  UNSUBSCRIBE: 27,
} as const;

export const METHODS_CALDAV = {
  MKCALENDAR: 30,
} as const;

export const METHODS_NON_STANDARD = {
  /**
   * Not defined in any RFC but commonly used
   */
  PURGE: 29,
} as const;

export const METHODS_ICECAST = {
  SOURCE: 33,
} as const;

export const METHODS_AIRPLAY: Simplify<Pick<typeof METHODS_BASIC_HTTP, "GET" | "POST">> = {
  GET: 1,
  POST: 3,
} as const;

export const METHODS_RAOP = {
  FLUSH: 45,
} as const;

/* RFC-2326 RTSP */
export const METHODS_RTSP = {
  OPTIONS: METHODS_BASIC_HTTP.OPTIONS,
  DESCRIBE: 35,
  ANNOUNCE: 36,
  SETUP: 37,
  PLAY: 38,
  PAUSE: 39,
  TEARDOWN: 40,
  GET_PARAMETER: 41,
  SET_PARAMETER: 42,
  REDIRECT: 43,
  RECORD: 44,

  ...METHODS_AIRPLAY,
  ...METHODS_RAOP,
} as const;

export const METHODS_HTTP1 = {
  ...METHODS_BASIC_HTTP,
  ...METHODS_WEBDAV,
  ...METHODS_SUBVERSION,
  ...METHODS_UPNP,
  ...METHODS_CALDAV,
  ...METHODS_NON_STANDARD,

  // TODO(indutny): should we allow it with HTTP?
  ...METHODS_ICECAST,
} as const;

export const METHODS_HTTP2 = {
  /**
   * RFC-9113, section 11.6
   * @see https://www.rfc-editor.org/rfc/rfc9113.html#preface
   */
  PRI: 34,
} as const;

export const METHODS_HTTP = {
  ...METHODS_HTTP1,
  ...METHODS_HTTP2,
} as const;

export const METHODS = {
  ...METHODS_HTTP1,
  ...METHODS_HTTP2,
  ...METHODS_RTSP,
} as const;

// ALPHA: https://tools.ietf.org/html/rfc5234#appendix-B.1
export const ALPHA = [
  "A", "a", "B", "b", "C", "c", "D", "d",
  "E", "e", "F", "f", "G", "g", "H", "h",
  "I", "i", "J", "j", "K", "k", "L", "l",
  "M", "m", "N", "n", "O", "o", "P", "p",
  "Q", "q", "R", "r", "S", "s", "T", "t",
  "U", "u", "V", "v", "W", "w", "X", "x",
  "Y", "y", "Z", "z",
] as const;

export const NUM_MAP = {
  0: 0, 1: 1, 2: 2, 3: 3, 4: 4,
  5: 5, 6: 6, 7: 7, 8: 8, 9: 9,
} as const;

export const HEX_MAP = {
  0: 0, 1: 1, 2: 2, 3: 3, 4: 4,
  5: 5, 6: 6, 7: 7, 8: 8, 9: 9,
  A: 0XA, B: 0XB, C: 0XC, D: 0XD, E: 0XE, F: 0XF,
  a: 0xa, b: 0xb, c: 0xc, d: 0xd, e: 0xe, f: 0xf,
} as const;

// DIGIT: https://tools.ietf.org/html/rfc5234#appendix-B.1
export const DIGIT = [
  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
] as const;

export const ALPHANUM = [ ...ALPHA, ...DIGIT ] as const;
export const MARK = [ '-', '_', '.', '!', '~', '*', '\'', '(', ')' ] as const;
export const USERINFO_CHARS = [ ...ALPHANUM, ...MARK, '%', ';', ':', '&', '=', '+', '$', ',' ] as const;

// TODO(indutny): use RFC
export const URL_CHAR = [
  '!', '"', '$', '%', '&', '\'',
  '(', ')', '*', '+', ',', '-', '.', '/',
  ':', ';', '<', '=', '>',
  '@', '[', '\\', ']', '^', '_',
  '`',
  '{', '|', '}', '~',
  ...ALPHANUM
] as const;

export const HEX = [ ...DIGIT, 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F' ] as const;

/* Tokens as defined by rfc 2616. Also lowercases them.
 *        token       = 1*<any CHAR except CTLs or separators>
 *     separators     = "(" | ")" | "<" | ">" | "@"
 *                    | "," | ";" | ":" | "\" | <">
 *                    | "/" | "[" | "]" | "?" | "="
 *                    | "{" | "}" | SP | HT
 */
export const TOKEN = [
  '!', '#', '$', '%', '&', '\'',
  '*', '+', '-', '.',
  '^', '_', '`',
  '|', '~',
  ...ALPHANUM
] as const;

// HTAB: https://tools.ietf.org/html/rfc5234#appendix-B.1
export const HTAB = [ '\t' ] as const;

// SP: https://tools.ietf.org/html/rfc5234#appendix-B.1
export const SP = [ ' ' ] as const;

// VCHAR: https://tools.ietf.org/html/rfc5234#appendix-B.1
const VCHAR = [
  0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
  0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
  0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
  0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
  0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
  0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
  0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
  0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
  0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
  0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
  0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
  0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e,
] as const;

// OBS_TEXT: https://datatracker.ietf.org/doc/html/rfc9110#name-collected-abnf
// 0x80 - 0xff
const OBS_TEXT = [
  0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
  0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
  0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
  0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
  0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
  0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
  0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
  0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
  0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
  0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
  0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
  0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
  0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
  0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
  0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
  0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
] as const;

export const HTAB_SP_VCHAR_OBS_TEXT = [ ...HTAB, ...SP, ...VCHAR, ...OBS_TEXT ] as const;

export const HEADER_CHARS = HTAB_SP_VCHAR_OBS_TEXT;

const RELAXED_CTRL_CHARS = [
  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Before TAB
  0x0b, 0x0c,                                     // VT, FF (between TAB and CR)
  0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, // After CR/LF, before space
  0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
  0x1e, 0x1f,
  0x7f,                                           // DEL
] as const;

// Relaxed header chars includes control characters (above) that are not allowed
// by default. This excludes only NULL (0x00), CR (0x0d), LF (0x0a).
export const RELAXED_HEADER_CHARS = [ ...RELAXED_CTRL_CHARS, ...HEADER_CHARS ] as const;

// ',' = \x2c
export const CONNECTION_TOKEN_CHARS = [
  ...HTAB, ...SP,
  0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
  0x29, 0x2a, 0x2b, /* */ 0x2d, 0x2e, 0x2f, 0x30,
  0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
  0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
  0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
  0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
  0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
  0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
  0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
  0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
  0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
  0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e,
  ...OBS_TEXT
] as const;

// QDTEXT: https://datatracker.ietf.org/doc/html/rfc9110#section-5.6.4
export const QDTEXT = [
  ...HTAB, ...SP,
  0x21,
  0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
  0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32,
  0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
  0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42,
  0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
  0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52,
  0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,
  0x5b,
  0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64,
  0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
  0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
  0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
  0x7d, 0x7e,
  ...OBS_TEXT
] as const;

export const MAJOR = NUM_MAP;
export const MINOR = MAJOR;

export const SPECIAL_HEADERS = {
  'connection': HEADER_STATE.CONNECTION,
  'content-length': HEADER_STATE.CONTENT_LENGTH,
  'proxy-connection': HEADER_STATE.CONNECTION,
  'transfer-encoding': HEADER_STATE.TRANSFER_ENCODING,
  'upgrade': HEADER_STATE.UPGRADE,
} as const;

export default {
  ERROR,
  TYPE,
  FLAGS,
  LENIENT_FLAGS,
  STATUSES,
  FINISH,
  HEADER_STATE,
  ALPHA,
  NUM_MAP,
  HEX_MAP,
  DIGIT,
  ALPHANUM,
  MARK,
  USERINFO_CHARS,
  URL_CHAR,
  HEX,
  TOKEN,
  HEADER_CHARS,
  RELAXED_HEADER_CHARS,
  CONNECTION_TOKEN_CHARS,
  QDTEXT,
  HTAB_SP_VCHAR_OBS_TEXT,
  MAJOR,
  MINOR,
  SPECIAL_HEADERS,
  METHODS,
  METHODS_HTTP,
  METHODS_HTTP1_HEAD,
  METHODS_HTTP1,
  METHODS_HTTP2,
  METHODS_ICECAST,
  METHODS_RTSP,
}
