import { type Readable } from 'stream';
import type { WebSocket, CloseEvent, ErrorEvent } from 'ws';
import type { Cookie, CookieJar, MemoryCookieStore } from 'tough-cookie';
import { type Message } from './io.common.ts';
import type { Encoding } from './file.ts';
import { inspect, Lock } from './utils.ts';
import { type BasicAuth, type BearerAuth, type RemoteKeeperOptions } from './net.common.ts';
export * from './net.common.ts';
export type { WebSocket };
export declare const WebSocketConnecting = 0;
export declare const WebSocketOpen = 1;
export declare const WebSocketClosing = 2;
export declare const WebSocketClosed = 3;
export declare const websocket_states: readonly ["connecting", "open", "closing", "closed"];
export declare enum MyProxy {
    socks5 = "http://127.0.0.1:10080",
    whistle = "http://localhost:8899",
    work = "http://localhost:10090",
    /** 需要先启动 server.start_tunnel_server('ddb.test.proxy') */
    test = "http://localhost:10091"
}
export declare const byproxy: {
    readonly proxy: true;
};
export declare const cookies: {
    store: MemoryCookieStore;
    jar: CookieJar;
    init(): Promise<void>;
};
export type { Cookie };
export interface RawResponse {
    status: number;
    headers: Record<string, string>;
    body: Readable;
}
export interface FullResponse<TBody extends Buffer | string> {
    status: number;
    headers: Record<string, string>;
    body: TBody;
}
export interface RequestOptions {
    method?: 'GET' | 'POST' | 'PUT' | 'HEAD' | 'DELETE' | 'PATCH';
    queries?: Record<string, any>;
    headers?: Record<string, string>;
    body?: string | Record<string, any> | Uint8Array | URLSearchParams | Readable;
    type?: 'application/json' | 'application/x-www-form-urlencoded' | 'multipart/form-data';
    proxy?: boolean | MyProxy | string;
    encoding?: Encoding | 'binary';
    retries?: true | number;
    timeout?: number;
    auth?: BasicAuth | BearerAuth;
    cookies?: Record<string, string>;
    redirect?: 'follow' | 'manual';
    decode?: boolean;
    print?: {
        retry: boolean;
        timeout: boolean;
    };
}
export interface RequestFullOptions extends RequestOptions {
    full: true;
}
export interface RequestRawOptions extends RequestOptions {
    raw: true;
}
export interface RequestError<TBody extends string | Buffer = string> extends Error {
    url: URL;
    options: RequestOptions | RequestFullOptions | RequestRawOptions;
    response?: FullResponse<TBody>;
    [inspect.custom]: Function;
}
export interface StatusCodeError {
    status: number;
}
export declare class StatusCodeError extends Error {
    name: "StatusCodeError";
    constructor(status: number, url: string);
}
/**
    - url: 必须是完整 url
    - options?:
        - method?: `有 body 时为 POST, 否则为 GET` 'GET' | 'POST' | ···
        - queries?: 添加到 url 上的参数，是 Record<string, any>，true/false 会被转换为 0/1
        - headers?: http 请求头，是 Record<string, string>，其中 key 必须是小写的
        - body?: http 请求体，可以是:
            - string
            - Uint8Array
            - Record<string, any>: 会自动 JSON.stringify
            - URLSearchParams: 仅限 type 为 x-www-form-urlencoded
            - Record<string, string | Blob | File> 自动转为 FormData: 仅限 type 为 form-data, 可以传 File 对象设置文件名
        - type?: `'application/json'` 有 body 时设置 http 请求头中的 content-type 头
        - proxy?: `false`  通过代理发送请求
            - 为 true 时使用 MyProxy.socks5
            - 为非空 string 作为代理地址
            - 为 falsy 值时设为 false
        - encoding?: `根据网页 content-type: charset=gb18030 提取 || 'utf-8'`  传入 'binary' 时返回 Buffer
             (只对非 raw 的请求起作用)
        - retries?: `false` 可以传入 true (默认 2 次) 或 重试次数
        - timeout?: `5 * 1000` 超时后会抛出 error.name 为 'TimeoutError' 的 error (但类型不是 utils.ts 中的 TimeoutError), 可以传入 0 来禁用超时
        - auth?: BasicAuth | BearerAuth
        - cookies?: 需要额外添加到请求的 cookies, 默认情况下携带了 MemoryCookieStore 中保存的之前 http 响应中的 cookies
        - raw?: `false` 传入后返回整个 response (RawResponse)，body 为 Readable
        - full?: `false` 传入后返回整个 response (FullResponse)，body 根据 encoding 对应为 string 或 Buffer
        - decode?: `true` 根据 content-encoding: br 等解码压缩后的 response.body
        - print?:
            - retry: `true` 是否打印 "等待 1 秒后重试 request" 提示信息
            - timeout: `true` 是否打印最后一次超时重试失败时的错误  */
export declare function request(url: string | URL): Promise<string>;
export declare function request(url: string | URL, options: RequestRawOptions): Promise<RawResponse>;
export declare function request(url: string | URL, options: RequestFullOptions & {
    encoding: 'binary';
}): Promise<FullResponse<Buffer>>;
export declare function request(url: string | URL, options: RequestFullOptions): Promise<FullResponse<string>>;
export declare function request(url: string | URL, options: RequestOptions & {
    encoding: 'binary';
}): Promise<Buffer>;
export declare function request(url: string | URL, options: RequestOptions): Promise<string>;
/** 发起 http 请求并将响应体作为 json 解析 */
export declare function request_json<T = any>(url: string | URL, options?: RequestOptions): Promise<T>;
export declare class WebSocketConnectionError extends Error {
    name: "WebSocketConnectionError";
    url: string;
    protocols?: string[];
    event: CloseEvent | ErrorEvent;
    type: 'close' | 'error';
    address?: string;
    errno?: number;
    port?: number;
    syscall?: string;
    /** close 事件时为 close code, error 事件为 error code */
    code?: string | number;
    reason?: string;
    constructor(url: string, protocols: string[] | undefined, event: CloseEvent | ErrorEvent, message?: string);
}
/** 连接 websocket url, 设置各种事件监听器。在 open 事件后 resolve, 返回 websocket
    遇到 error 时会创建 WebSocketConnectionError:
        - reject 掉返回的 promise (若此时未 settle)
        - 作为参数调用 on_error (已 settle 且有 on_error 回调)
    可以用 WebSocket.bufferedAmount 来显示大消息的发送进度
    https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/bufferedAmount
    - url
    - options:
        - protocols?
        - max_payload?: `8 GB`
        - on_message: 根据 websocket frame 的 opcode 不同 (text frame 或 binary frame)，event 中的 data 对应为 ArrayBuffer 或者 string
          https://datatracker.ietf.org/doc/html/rfc6455#section-5.2
        - on_error?: 在 websocket 出错和非正常关闭 (close, error 事件) 时都调用，可以根据 error.type 来区分，error 的类型是 WebSocketConnectionError，
            type 为 'close' 时有 code 和 reason 属性
        - on_close?: 和 websocket 的 'close' 事件不相同，只在正常关闭 (close code 为 1000) 时才调用，否则都会调用 on_error
            https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes
        - print?: 是否打印连接、关闭信息 */
export declare function connect_websocket(url: string | URL, { protocols, max_payload, // 8 GB
on_message, on_error, on_close, proxy, print, }: {
    protocols?: string[];
    max_payload?: number;
    on_message(data: ArrayBuffer | string, websocket: WebSocket): any;
    on_error?(error: WebSocketConnectionError, websocket: WebSocket): any;
    on_close?(event: CloseEvent, websocket: WebSocket): any;
    proxy?: string;
    print?: boolean;
}): Promise<WebSocket>;
/** 接收到消息后的处理函数
    返回值会自动被 await，然后可能被封装为 message 回传 */
export type MessageHandler<TData = any> = (message: Message<TData>, websocket?: WebSocket) => void | any | Promise<void | any>;
/** 通过创建 remote 对象对 websocket rpc 进行抽象
    创建 remote 对象时传入 funcs 注册处理函数，使得对端能通过 (rpc message).func 调用
    使用 remote.handle 方法处理对端发来的 websocket message，对于 websocket 连接接收方需要手动绑定 websocket message 事件到 remote.handle
    使用 remote.call 进行一元 rpc
    使用 remote.send 结合 message.id 进行复杂 rpc
    创建后等到首个 remote.call 或 remote.send 时自动建立实际 websocket 连接
    rpc 状态与底层连接的状态无关，如果是传入 url 创建的 remote 实例，remote.send 时检测到断线会自动建立新的 websocket 连接
    
    @example
    // Zero 继承自 Remote 并通过 call 实现了一些方法
    let zero = new Zero({ local: true })
    
    // 一元 rpc
    await zero.repl_ts('1234')
    
    // 订阅流
    const id = genid()
    
    zero.handlers.set(id, ({ data: [chunk] }: Message<[Uint8Array]>) => {
        term.write(chunk)
    })
    
    zero.send({ id, func: 'subscribe_stdio' }) */
export declare class Remote {
    /** 在构造 Remote 时, this.initiator = Boolean(url || websocket)
        - true:  作为 websocket 连接发起方
        - false: 作为 websocket 连接接收方 */
    initiator: boolean;
    /** 作为 websocket 连接发起方，对端的 url 地址 */
    url?: string;
    /** 作为 websocket 连接发起方有 websocket lock */
    lwebsocket?: Lock<WebSocket>;
    /** websocket 连接发起方，接收方，都能被对端通过 (rpc message).func 调用的 rpc 函数 */
    funcs: Record<string, MessageHandler>;
    /** map<id, message handler>: 通过 (rpc message).id 找到对应的 handler
        一元 rpc 接收方不需要设置 handlers, 发送方需要 */
    handlers: Map<number, MessageHandler<any>>;
    keeper?: RemoteKeeperOptions;
    /** `true` 是否打印连接信息、错误信息 */
    print: boolean;
    /** `false` 打印所有交互的 rpc messages */
    verbose: boolean;
    first_error: boolean;
    keeping: boolean;
    reconnecting: boolean;
    disconnected: boolean;
    /** 作为 websocket 连接发起方，传入 url 或 websocket，定义远程 Remote
        作为 websocket 连接接收方，不传 url 和 websocket，定义本地 Remote */
    constructor({ url, funcs, print, verbose, websocket, keeper, on_error, }?: {
        url?: string;
        funcs?: Remote['funcs'];
        print?: boolean;
        verbose?: boolean;
        websocket?: WebSocket;
        keeper?: RemoteKeeperOptions;
        on_error?(error: WebSocketConnectionError | Error, websocket?: WebSocket): void;
    });
    /** 统一处理首次连接和连接后的 websocket 错误 */
    _on_error: (error: WebSocketConnectionError, websocket?: WebSocket) => void;
    _on_message: (data: ArrayBuffer, websocket: WebSocket) => void;
    /** 使用者自定义的在 websocket 连接出错时，或者 handlers 出错时的处理 */
    on_error(error: WebSocketConnectionError | Error, websocket?: WebSocket): void;
    /** 幂等，保证 websocket 已连接，否则抛出异常
        一般情况不需要手动调用，在其它方法中会自动调用这个方法，除非需要手动建立 websocket 连接并确保成功
        连接断开后，通过传入 url 参数构造的 remote 实例会自动创建新的 websocket 连接，而通过传入 websocket 参数构造的实例只会检查连接状态，在断开时抛出异常
        作为 websocket 连接发起方，不需要传入 websocket
        作为 websocket 连接接收方，需要传入使用的 websocket 连接，确保这个这个连接的状态 */
    connect(websocket?: WebSocket): Promise<void>;
    /** 作为 websocket 连接发起方手动关闭到对端的 websocket 连接 */
    disconnect(): void;
    /** 发送 message 到对端 remote
        作为 websocket 连接发起方，不需要传入 websocket
        作为 websocket 连接接收方，必传 websocket 参数
        发送或连接出错时自动清理 message.id 对应的 handler */
    send(message: Message, websocket?: WebSocket): Promise<void>;
    /** 处理接收到的 websocket message 并解析, 根据 message.id 或 message.func 分发到对应的 handler 进行处理，
        handler 处理完成后:
        - 传了 func: 调用函数的情况下 (通常是一元 rpc)，总是将返回值包装为 message 回传
        - 未传 func: 通过 id 调用，如果 handler 返回非 undefined 的值，也包装为 message 回传
        
        如果 message.done == true 则对端指示当前 remote 可以清理 handler
        使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214
        这个方法一般不会抛出错误，也不需要 await，一般在 websocket on_message 时使用 */
    handle(data: Uint8Array, websocket: WebSocket): Promise<void>;
    /** 调用对端 remote 中的 func, 只适用于最简单的一元 rpc (请求, 响应)
        作为 websocket 连接发起方，不需要传入 websocket
        作为 websocket 连接接收方，必传 websocket 参数  */
    call<TReturn = any>(func: string, args?: any[], websocket?: WebSocket): Promise<TReturn>;
    /** 调用对端 remote 中的 func, 开始订阅并接收连续的消息 (订阅流)
        - func: 订阅处理函数
        - on_data: 接收开始订阅后的数据
        - options?:
            - on_error?: 处理开始订阅后的错误
            - websocket?: 作为 websocket 连接接收方，必传 websocket 参数 */
    subscribe<TData, TSubscribed = void>(func: string, on_data: (data: TData) => void, { on_error, websocket }?: RemoteSubscribeOptions): Promise<{
        id: number;
        data: TSubscribed;
    }>;
}
export interface RemoteSubscribeOptions {
    on_error?(error: Error): void;
    websocket?: WebSocket;
}
/** 为连接到 server 的 client 创建 RemoteClient，以便后续调用 client 中方法 */
export declare class RemoteClient {
    remote: Remote;
    websocket: WebSocket;
    constructor(remote: Remote, websocket: WebSocket);
    /** 调用 client 中的 func, 只适用于最简单的一元 rpc (请求, 响应) */
    call<TReturn = any>(func: string, args?: any[]): Promise<TReturn>;
    /** 发送 message 到 client
        发送或连接出错时自动清理 message.id 对应的 handler */
    send(message: Message): Promise<void>;
    [inspect.custom](): {
        remote: string;
        websocket: string;
    } & Omit<this, typeof import("util").inspect.custom | "call" | "websocket" | "remote" | "send">;
}
