import { Query } from "sift";
import { v4 as guid } from "uuid";
import { KnownRecord } from "../record";
import { Guid } from "../string";
import { Model } from "../model";

export type K = { _id: Guid };

export const entity = <T extends {}>(record: T): Entity<T> => ({
  _id: guid(),
  ...record,
});
export const collonize = <T extends {}>(
  records: T[]
): Record<Guid, Entity<T>> =>
  records.reduce((coll, record) => {
    const ntt = entity(record);
    return Object.assign(coll, { [ntt._id]: ntt });
  }, {});

export type Entity<T extends {}> = {
  _id: string;
} & T;

export type StoreResult<T extends {} = KnownRecord> = K & {
  data?: T | null;
  rescode?: ResultCode;
  message?: string;
};

export interface Repository<T extends {} = KnownRecord> {
  readonly model: Model<T>;

  count: Promise<number>;

  create(record: T): Promise<StoreResult<T>>;

  delete(_id: string): Promise<StoreResult<T>>;

  find(_id: string): Promise<StoreResult<T>>;

  select(query?: Query<T>): Promise<AsyncIterable<Entity<T>>>;

  update(entity: Entity<Partial<T>>): Promise<StoreResult<T>>;
}

export interface BatchRepository<T extends {} = KnownRecord> {
  readonly model: Model<T>;

  count: Promise<number>;

  batchCreate(records: T[]): Promise<StoreResult<T>[]>;

  batchDelete(ids: string[]): Promise<StoreResult<T>[]>;

  batchFind(ids: string[]): Promise<AsyncIterable<Entity<T>>>;

  batchUpdate(entities: Entity<Partial<T>>[]): Promise<StoreResult<T>[]>;
}

/*** 2×× SUCCESS ***/
export const RESULT_CODE__OK = 200;
export const RESULT_CODE__CREATED = 201;
export const RESULT_CODE__ACCEPTED = 202;
export const HTTP_RESULT_CODE_203_NON_AUTHORITATIVE_INFORMATION = 203;
export const HTTP_RESULT_CODE_204_NO_CONTENT = 204;
export const HTTP_RESULT_CODE_205_RESET_CONTENT = 205;
export const HTTP_RESULT_CODE_206_PARTIAL_CONTENT = 206;
export const HTTP_RESULT_CODE_207_MULTI_STATUS = 207;
export const HTTP_RESULT_CODE_208_ALREADY_REPORTED = 208;
export const HTTP_RESULT_CODE_226_IM_USED = 226;
/*** 3×× REDIRECTION ***/
export const HTTP_RESULT_CODE_300_MULTIPLE_CHOICES = 300;
export const HTTP_RESULT_CODE_301_MOVED_PERMANENTLY = 301;
export const HTTP_RESULT_CODE_302_FOUND = 302;
export const HTTP_RESULT_CODE_303_SEE_OTHER = 303;
export const HTTP_RESULT_CODE_304_NOT_MODIFIED = 304;
export const HTTP_RESULT_CODE_305_USE_PROXY = 305;
export const HTTP_RESULT_CODE_307_TEMPORARY_REDIRECT = 307;
export const HTTP_RESULT_CODE_308_PERMANENT_REDIRECT = 308;
/*** 4×× CLIENT ERROR ***/
export const RESULT_CODE__BAD_REQUEST = 400;
export const RESULT_CODE__UNAUTHORIZED = 401;
export const HTTP_RESULT_CODE_402_PAYMENT_REQUIRED = 402;
export const RESULT_CODE__FORBIDDEN = 403;
export const RESULT_CODE__NOT_FOUND = 404;
export const HTTP_RESULT_CODE_405_METHOD_NOT_ALLOWED = 405;
export const RESULT_CODE__NOT_ACCEPTABLE = 406;
export const HTTP_RESULT_CODE_407_PROXY_AUTHENTICATION_REQUIRED = 407;
export const HTTP_RESULT_CODE_408_REQUEST_TIMEOUT = 408;
export const RESULT_CODE_CONFLICT = 409;
export const HTTP_RESULT_CODE_410_GONE = 410;
export const HTTP_RESULT_CODE_411_LENGTH_REQUIRED = 411;
export const HTTP_RESULT_CODE_412_PRECONDITION_FAILED = 412;
export const HTTP_RESULT_CODE_413_PAYLOAD_TOO_LARGE = 413;
export const HTTP_RESULT_CODE_414_REQUEST_URI_TOO_LONG = 414;
export const HTTP_RESULT_CODE_415_UNSUPPORTED_MEDIA_TYPE = 415;
export const HTTP_RESULT_CODE_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
export const HTTP_RESULT_CODE_417_EXPECTATION_FAILED = 417;
export const HTTP_RESULT_CODE_418_IM_A_TEAPOT = 418;
export const HTTP_RESULT_CODE_421_MISDIRECTED_REQUEST = 421;
export const RESULT_CODE__UNPROCESSABLE_ENTITY = 422;
export const RESULT_CODE__LOCKED = 423;
export const HTTP_RESULT_CODE_424_FAILED_DEPENDENCY = 424;
export const HTTP_RESULT_CODE_426_UPGRADE_REQUIRED = 426;
export const RESULT_CODE__PRECONDITION_REQUIRED = 428;
export const RESULT_CODE__TOO_MANY_REQUESTS = 429;
export const HTTP_RESULT_CODE_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
export const HTTP_RESULT_CODE_444_CONNECTION_CLOSED_WITHOUT_RESPONSE = 444;
export const HTTP_RESULT_CODE_451_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
export const HTTP_RESULT_CODE_499_CLIENT_CLOSED_REQUEST = 499;
/*** 5×× SERVER ERROR ***/
export const HTTP_RESULT_CODE_500_INTERNAL_SERVER_ERROR = 500;
export const RESULT_CODE__NOT_IMPLEMENTED = 501;
export const HTTP_RESULT_CODE_502_BAD_GATEWAY = 502;
export const RESULT_CODE__SERVICE_UNAVAILABLE = 503;
export const HTTP_RESULT_CODE_504_GATEWAY_TIMEOUT = 504;
export const HTTP_RESULT_CODE_505_HTTP_VERSION_NOT_SUPPORTED = 505;
export const HTTP_RESULT_CODE_506_VARIANT_ALSO_NEGOTIATES = 506;
export const RESULT_CODE__INSUFFICIENT_STORAGE = 507;
export const HTTP_RESULT_CODE_508_LOOP_DETECTED = 508;
export const HTTP_RESULT_CODE_510_NOT_EXTENDED = 510;
export const HTTP_RESULT_CODE_511_NETWORK_AUTHENTICATION_REQUIRED = 511;
export const HTTP_RESULT_CODE_599_NETWORK_CONNECTTIMEOUT_ERROR = 599;

export const RESULT_CODES = [
  /*** 2×× SUCCESS ***/
  RESULT_CODE__OK,
  RESULT_CODE__CREATED,
  RESULT_CODE__ACCEPTED,
  HTTP_RESULT_CODE_203_NON_AUTHORITATIVE_INFORMATION,
  HTTP_RESULT_CODE_204_NO_CONTENT,
  HTTP_RESULT_CODE_205_RESET_CONTENT,
  HTTP_RESULT_CODE_206_PARTIAL_CONTENT,
  HTTP_RESULT_CODE_207_MULTI_STATUS,
  HTTP_RESULT_CODE_208_ALREADY_REPORTED,
  HTTP_RESULT_CODE_226_IM_USED,
  /*** 3×× REDIRECTION ***/
  HTTP_RESULT_CODE_300_MULTIPLE_CHOICES,
  HTTP_RESULT_CODE_301_MOVED_PERMANENTLY,
  HTTP_RESULT_CODE_302_FOUND,
  HTTP_RESULT_CODE_303_SEE_OTHER,
  HTTP_RESULT_CODE_304_NOT_MODIFIED,
  /*** 4×× CLIENT ERROR ***/
  RESULT_CODE__BAD_REQUEST,
  RESULT_CODE__UNAUTHORIZED,
  RESULT_CODE__FORBIDDEN,
  RESULT_CODE__NOT_FOUND,
  HTTP_RESULT_CODE_405_METHOD_NOT_ALLOWED,
  RESULT_CODE__NOT_ACCEPTABLE,
  HTTP_RESULT_CODE_407_PROXY_AUTHENTICATION_REQUIRED,
  HTTP_RESULT_CODE_408_REQUEST_TIMEOUT,
  RESULT_CODE_CONFLICT,
  HTTP_RESULT_CODE_410_GONE,
  HTTP_RESULT_CODE_411_LENGTH_REQUIRED,
  HTTP_RESULT_CODE_412_PRECONDITION_FAILED,
  HTTP_RESULT_CODE_413_PAYLOAD_TOO_LARGE,
  HTTP_RESULT_CODE_414_REQUEST_URI_TOO_LONG,
  HTTP_RESULT_CODE_415_UNSUPPORTED_MEDIA_TYPE,
  HTTP_RESULT_CODE_416_REQUESTED_RANGE_NOT_SATISFIABLE,
  HTTP_RESULT_CODE_417_EXPECTATION_FAILED,
  HTTP_RESULT_CODE_418_IM_A_TEAPOT,
  HTTP_RESULT_CODE_421_MISDIRECTED_REQUEST,
  RESULT_CODE__UNPROCESSABLE_ENTITY,
  RESULT_CODE__LOCKED,
  HTTP_RESULT_CODE_424_FAILED_DEPENDENCY,
  HTTP_RESULT_CODE_426_UPGRADE_REQUIRED,
  RESULT_CODE__PRECONDITION_REQUIRED,
  RESULT_CODE__TOO_MANY_REQUESTS,
  HTTP_RESULT_CODE_431_REQUEST_HEADER_FIELDS_TOO_LARGE,
  HTTP_RESULT_CODE_444_CONNECTION_CLOSED_WITHOUT_RESPONSE,
  HTTP_RESULT_CODE_451_UNAVAILABLE_FOR_LEGAL_REASONS,
  HTTP_RESULT_CODE_499_CLIENT_CLOSED_REQUEST,
  /*** 5×× SERVER ERROR ***/
  HTTP_RESULT_CODE_500_INTERNAL_SERVER_ERROR,
  RESULT_CODE__NOT_IMPLEMENTED,
  HTTP_RESULT_CODE_502_BAD_GATEWAY,
  RESULT_CODE__SERVICE_UNAVAILABLE,
  HTTP_RESULT_CODE_504_GATEWAY_TIMEOUT,
  HTTP_RESULT_CODE_505_HTTP_VERSION_NOT_SUPPORTED,
  HTTP_RESULT_CODE_506_VARIANT_ALSO_NEGOTIATES,
  RESULT_CODE__INSUFFICIENT_STORAGE,
  HTTP_RESULT_CODE_508_LOOP_DETECTED,
  HTTP_RESULT_CODE_510_NOT_EXTENDED,
  HTTP_RESULT_CODE_511_NETWORK_AUTHENTICATION_REQUIRED,
  HTTP_RESULT_CODE_599_NETWORK_CONNECTTIMEOUT_ERROR,
] as const;

export type ResultCode = typeof RESULT_CODES[number];

export type HttpResultCodeMap<T> = Record<ResultCode, T>;

export type HttpResultCodeSet<T> = Partial<HttpResultCodeMap<T>>;
