import * as axios from 'axios';
import { CreateAxiosDefaults, Method } from 'axios';
import { IOptions, Etcd3 } from 'etcd3';

/* eslint-disable @typescript-eslint/no-explicit-any */


interface ConnectionConfig extends CreateAxiosDefaults {}
interface EtcdConnectionConfig extends IOptions {}
type QueryObjectSchema = Record<string, string | number | undefined>;
interface ServerResponse<ResponseType> {
  data: ResponseType;
}

interface BaseInfo {
  id: any;
  create_time: string;
  update_time: string;
}
interface PaginationResponse<T> {
  list: T[];
  total: number;
}

interface GetResponse<T> {
  modifiedIndex: number;
  value: T;
  key: string;
  createdIndex: number;
}
interface CreateResponse<T> {
  value: T;
  key: string;
}

interface PaginationRequest<T> {
  page?: T;
  page_size?: T;
}

/* eslint-disable @typescript-eslint/no-explicit-any */



interface Node {
  host?: string;
  port?: number;
  weight: number;
  metadata?: any;
  priority?: number;
}

type TimeoutValue = number;
interface Timeout {
  connect?: TimeoutValue;
  send?: TimeoutValue;
  read?: TimeoutValue;
}

interface Healthy {
  interval?: number;
  http_statuses?: number[];
  successes?: number;
}

interface UnHealthy {
  interval?: number;
  http_statuses?: number[];
  tcp_failures?: number;
  timeouts?: number;
  http_failures?: number;
}

type HealthCheckerType = 'http' | 'https' | 'tcp';

interface Passive {
  type?: HealthCheckerType;
  healthy?: Healthy;
  unhealthy?: UnHealthy;
}

interface Active {
  type?: HealthCheckerType;
  timeout?: number;
  concurrency?: number;
  host?: string;
  port?: number;
  http_path?: string;
  https_verify_certificate?: boolean;
  healthy?: Healthy;
  unhealthy?: UnHealthy;
  req_headers?: string[];
}

interface HealthChecker {
  active?: Active;
  passive?: Passive;
}

interface UpstreamTLS {
  client_cert?: string;
  client_key?: string;
  tls_verify?: boolean;
  verify?: boolean;
}

interface UpstreamKeepalivePool {
  idle_timeout?: TimeoutValue;
  requests?: number;
  size: number;
}

interface DiscovertArgs {
  group_name?: string;
  namespace_id?: string;
}

type UpstreamType = 'roundrobin' | 'chash' | 'ewma' | 'least_conn';
type UpstreamSchema = 'http' | 'https' | 'grpc' | 'grpcs' | 'tcp' | 'tls' | 'udp' | 'kafka';
type UpstreamPassHost = 'node' | 'pass' | 'rewrite';
type HashOnType = 'vars' | 'header' | 'cookie' | 'consumer' | 'vars_combinations';
type Key =
  | 'remote_addr'
  | 'host'
  | 'uri'
  | 'server_name'
  | 'server_addr'
  | 'request_uri'
  | 'query_string'
  | 'remote_port'
  | 'hostname'
  | 'arg_id';

interface UpstreamDef {
  type?: UpstreamType;
  nodes?: Node[];
  service_name?: string;
  discovery_type?: string;
  hash_on?: HashOnType;
  key?: Key;
  checks?: HealthChecker;
  retries?: number;
  retry_timeout?: TimeoutValue;
  timeout?: Timeout;
  name?: string;
  desc?: string;
  pass_host?: UpstreamPassHost;
  upstream_host?: string;
  scheme?: UpstreamSchema;
  labels?: { [key: string]: string };
  discovery_args?: DiscovertArgs;
  tls?: UpstreamTLS;
  keepalive_pool?: UpstreamKeepalivePool;
}

interface UpstreamGetListRequest extends PaginationRequest<string> {
  id?: string;
  desc?: string;
  name?: string;
}

interface Upstream extends UpstreamDef, BaseInfo {}

declare class UpstreamService {
    private readonly client;
    private readonly validationService;
    constructor(client: ApisixAdminClient);
    /**
     * Fetches specified Upstream by id.
     * @param id unique id of upstream
     * @returns {GetResponse<Upstream>}
     */
    get: (id: string) => Promise<GetResponse<Upstream>>;
    /**
     * Fetch a list of all configured Upstreams.
     * @param id unique id of upstream
     * @param desc description of upstream
     * @param name name of upstream
     * @param page offset
     * @param page_size limit
     * @returns {PaginationResponse<GetUpstreamResponse>}
     */
    getList: (id?: string, desc?: string, name?: string, page?: string, page_size?: string) => Promise<PaginationResponse<GetResponse<Upstream>>>;
    /**
     * Creates an Upstream and assigns a random id.
     * @param config {UpstreamDef}
     * @returns {CreateResponse<Upstream>}
     */
    create: (def: UpstreamDef, validate?: boolean) => Promise<CreateResponse<Upstream>>;
    /**
     * Creates an Upstream with the specified id.
     * @param id unique id of upstream
     * @param config {UpstreamDef}
     * @returns {CreateResponse<Upstream>}
     */
    upsert: (id: string, config: UpstreamDef, validate?: boolean) => Promise<CreateResponse<Upstream>>;
    /**
     * Updates the selected attributes of the specified, existing Upstream. To delete an attribute, set value of attribute set to null.
     * @param id unique id of upstream
     * @param config {UpstreamDef}
     * @returns {CreateResponse<Upstream>}
     */
    update: (id: string, config: UpstreamDef, validate?: boolean) => Promise<CreateResponse<Upstream>>;
    /**
     * Updates the attribute specified in the path. The values of other attributes remain unchanged.
     * @param id unique id of upstream
     * @param path like node
     * @param config {UpstreamDef}
     * @returns {CreateResponse<Upstream>}
     */
    updateByPath: (id: string, path: string, config: UpstreamDef, validate?: boolean) => Promise<CreateResponse<Upstream>>;
    /**
     * Removes the Upstream with the specified id.
     * @param id unique id of upstream
     * @returns {void}
     */
    delete: (id: string) => Promise<void>;
    /**
     * Is upstream exists ?
     * @param id unique id of upstream
     * @returns {boolean}
     */
    isExists: (id: string) => Promise<boolean>;
}

/* eslint-disable @typescript-eslint/no-explicit-any */


interface RouteDef {
  uri?: string;
  uris?: string[];
  name: string;
  desc?: string;
  priority?: number;
  methods?: ('GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE' | 'PURGE')[];
  host?: string;
  hosts?: string[];
  remote_addr?: string;
  remote_addrs?: string[];
  vars?: any[];
  filter_func?: string;
  script?: any;
  script_id?: any; // For debug and optimization(cache), currently same as Route's ID
  plugins?: Record<string, Record<string, any>>;
  plugin_config_id?: string;
  upstream?: UpstreamDef;
  service_id?: string;
  upstream_id?: string | number;
  service_protocol?: string;
  labels?: { [key: string]: string };
  enable_websocket?: boolean;
  status: 0 | 1;
}

interface Route extends RouteDef, BaseInfo {}

interface GetListRoute extends PaginationRequest<string> {
  name?: string;
  uri?: string;
  label?: string;
  status?: string;
  host?: string;
  id?: string;
  desc?: string;
}

declare class RouteService {
    private readonly client;
    private readonly validationService;
    constructor(client: ApisixAdminClient);
    private readonly isJson;
    /**
     * Fetches specified Route by id.
     * @param id
     * @returns
     */
    get: (id: string) => Promise<GetResponse<Route>>;
    getByPath: (path: string) => Promise<PaginationResponse<GetResponse<Route>>>;
    /**
     * Fetches a list of all configured Routes.
     * @param name
     * @param uri
     * @param label
     * @param status
     * @param host
     * @param id
     * @param desc
     * @param page
     * @param page_size
     * @returns
     */
    getList: (name?: string, uri?: string, label?: string, status?: string, host?: string, id?: string, desc?: string, page?: string, page_size?: string) => Promise<PaginationResponse<GetResponse<Route>>>;
    filter: (upstream_id?: string, name?: string, uri?: string, status?: "0" | "1", host?: string, id?: string, desc?: string, labels?: string, page?: string, page_size?: string) => Promise<{
        list: RouteDef[];
        total: number;
    }>;
    /**
     * Creates a Route and assigns a random id.
     * @param config
     * @returns
     */
    create: (config: RouteDef, validate?: boolean) => Promise<CreateResponse<Route>>;
    /**
     * Creates a Route with the specified id.
     * @param id
     * @param ttl
     * @param config
     * @returns
     */
    upsert: (id: string, config: RouteDef, ttl?: string, validate?: boolean) => Promise<CreateResponse<Route>>;
    /**
     * Updates the selected attributes of the specified, existing Route. To delete an attribute, set value of attribute set to null.
     * @param id
     * @param config
     * @returns
     */
    update: (id: string, config: RouteDef, validate?: boolean) => Promise<CreateResponse<Route>>;
    /**
     * Updates the attribute specified in the path. The values of other attributes remain unchanged.
     * @param id
     * @param path
     * @param config
     * @returns
     */
    updateByPath: (id: string, path: string, config: RouteDef, validate?: boolean) => Promise<CreateResponse<Route>>;
    /**
     * Removes the Route with the specified id.
     * @param id
     */
    delete: (id: string) => Promise<void>;
    isExists: (id: string) => Promise<boolean>;
}

declare class PluginService {
    private readonly client;
    constructor(client: ApisixAdminClient);
    /**
     * Fetches a list of all Plugins.
     * @returns {PluginList}
     */
    getList: () => Promise<string[]>;
    /**
     * Get properties for config a plugin
     * @param pluginName
     * @returns {any}
     */
    getProperties: (pluginName: string) => Promise<any>;
    /**
     * Reloads the plugin according to the changes made in code
     * @returns {void}
     */
    reload: () => Promise<void>;
    isExists: (name: string) => Promise<boolean>;
}

type ValidationResource =
  | 'routes'
  | 'services'
  | 'upstreams'
  | 'consumers'
  | 'consumer_groups'
  | 'ssls'
  | 'gloabl_rules'
  | 'plugins'
  | 'plugin_metadata'
  | 'plugin_configs'
  | 'stream_routes'
  | 'secrets'
  | 'protos';

declare class ValidationService {
    private readonly client;
    constructor(client: ApisixAdminClient);
    validate: (resource: ValidationResource, schema: any) => Promise<any>;
}

declare class ApisixAdminClient {
    private connectionConfig;
    private etcdConnectionConfig;
    private readonly client;
    readonly etcd: Etcd3;
    upstream: UpstreamService;
    route: RouteService;
    plugin: PluginService;
    validation: ValidationService;
    constructor(connectionConfig: ConnectionConfig, etcdConnectionConfig: EtcdConnectionConfig);
    request: <Request, Response>(method: Method, url: string, requestBody?: Request, query?: Request) => Promise<axios.AxiosResponse<Response, any>>;
}

export { type Active, type BaseInfo, type ConnectionConfig, type CreateResponse, type DiscovertArgs, type EtcdConnectionConfig, type GetListRoute, type GetResponse, type HashOnType, type HealthChecker, type Healthy, type Key, type Node, type PaginationRequest, type PaginationResponse, type Passive, type QueryObjectSchema, type Route, type RouteDef, type ServerResponse, type Timeout, type UnHealthy, type Upstream, type UpstreamDef, type UpstreamGetListRequest, type UpstreamKeepalivePool, type UpstreamPassHost, type UpstreamSchema, type UpstreamTLS, type UpstreamType, ApisixAdminClient as default };
