import { ILoggerLike } from '@avanio/logger-like';
import { IResult } from '@luolapeikko/result-option';
import { Writable, WritableOptions } from 'stream';
import { SrvRecord } from 'dns';

/**
 * Object with hostname and port
 * @example
 * {hostname: 'localhost', port: 25565}
 */
interface IAddress {
    /**
     * Hostname of the server
     */
    hostname: string;
    /**
     * Port of the server
     */
    port: number;
}
interface IHandshakeDescriptionData extends Record<string, unknown> {
    text: string;
    extra?: {
        color: string;
        text: string;
        bold: boolean;
        strikethrough: boolean;
        extra: {
            color: string;
            text: string;
        }[];
    }[];
}
/**
 * Server handshake player data
 */
interface IHandshakePlayerData extends Record<string, unknown> {
    online: number;
    max: number;
    sample?: {
        name: string;
        id: string;
    }[];
}
/**
 * Server handshake JSON payload
 */
interface IHandshakeData extends Record<string, unknown> {
    description: IHandshakeDescriptionData;
    players: IHandshakePlayerData;
    version: {
        name: string;
        protocol: number;
    };
    ping: number;
    modinfo?: {
        type: string;
        modList: string[];
    };
    favicon?: string;
}
interface IMinecraftData extends IHandshakeData {
    ping: number;
}

declare enum MinecraftPackageType {
    HANDSHAKE = 0,
    PING = 1
}
/**
 * Create Minecraft handshake packet
 * @param {IAddress} address
 * @returns {Buffer} handshake packet buffer data
 */
declare function createHandshakePacket(address: IAddress): Buffer;
/**
 * Create ping packet
 * @param {bigint} timestamp
 * @returns {Buffer} ping packet buffer data
 */
declare function createPingPacket(timestamp: bigint): Buffer;

interface IPacketHeader {
    id: MinecraftPackageType;
    length: number;
    offset: number;
}
declare class PacketDecoder extends Writable {
    private packetInfo;
    private buffer;
    constructor(options?: WritableOptions);
    /**
     * Promise to wait specific packet to be received
     */
    oncePromise<T>(event: string): Promise<T>;
    _write(chunk: Buffer, _encoding: BufferEncoding, callback: (error?: Error | null) => void): void;
    private decodeHeader;
    private getPayload;
    /**
     * Decodes the handshake JSON data
     */
    private decodeHandshake;
    /**
     * Decodes the pong data
     */
    private decodePong;
}

/**
 * Resolves the SRV records for the given service name.
 * @param {string} srv The service name to resolve.
 * @returns {Promise<Result<SrvRecord[]>>} A promise of Result that resolves to an array of SRV records.
 * @example
 * const res = await srvRecordsResult('_minecraft._tcp.example.com');
 * if (res.isOk) {
 *   console.log(res.ok()); // [ { name: 'example.com', port: 25565, priority: 0, weight: 5 } ]
 * } else {
 *   console.error(res.err());
 * }
 */
declare function srvRecordsResult(srv: string): Promise<IResult<[SrvRecord, ...SrvRecord[]], NodeJS.ErrnoException>>;
/**
 * Resolves the first SRV record for the given service name.
 * @param {string} srv The service name to resolve.
 * @returns {Promise<Result<SrvRecord>>} A promise of Result that resolves to the first SRV record, or Err if no records were found.
 * @example
 * const res = await srvRecordResult('_minecraft._tcp.example.com');
 * if (res.isOk) {
 *   console.log(res.ok()); // { name: 'example.com', port: 25565, priority: 0, weight: 5 }
 * } else {
 *   console.error(res.err());
 * }
 */
declare function srvRecordResult(srv: string): Promise<IResult<SrvRecord, NodeJS.ErrnoException>>;

/**
 * Type to get value directly, from promise or from function
 * @template T type of value
 */
type Loadable<T> = T | Promise<T> | (() => T | Promise<T>);
/**
 * Build Loadable Address type
 */
type LoadableAddress = Loadable<Partial<IAddress>>;
/**
 * Build Loadable URL type
 */
type LoadableUrl = Loadable<URL | string>;
/**
 * Options for ping
 * @interface Options
 * @property {number=} timeout timeout in milliseconds
 */
interface CommonOptions {
    /** connection timeout in milliseconds, default is 10000 */
    timeout?: number;
    logger?: ILoggerLike;
}
/**
 * ping with URI
 * @param {LoadableUrl} uriArg minecraft://server[:port]
 * @param {CommonOptions=} options options
 * @return {Promise<IMinecraftData>}
 */
declare function pingUri(uriArg: LoadableUrl, options?: CommonOptions): Promise<IMinecraftData>;
/**
 * ping with URI, return result object
 * @param {LoadableUrl} uriArg minecraft://server[:port]
 * @param {CommonOptions=} options options
 * @return {Promise<Result<IMinecraftData, Error>>}
 */
declare function pingUriResult(uriArg: LoadableUrl, options?: CommonOptions): Promise<IResult<IMinecraftData, Error>>;
/**
 * ping with hostname and port
 * @param {LoadableAddress=} addressArg address object (defaults {hostname: 'localhost', port: 25565})
 * @param {CommonOptions=} options options
 * @returns {Promise<IMinecraftData>}
 */
declare function ping(addressArg?: LoadableAddress, options?: CommonOptions): Promise<IMinecraftData>;
/**
 * ping with hostname and port, return result object
 * @param {LoadableAddress=} addressArg address object (defaults {hostname: 'localhost', port: 25565})
 * @param {CommonOptions=} options options
 * @returns {Promise<Result<IMinecraftData>>}
 */
declare function pingResult(addressArg?: LoadableAddress, options?: CommonOptions): Promise<IResult<IMinecraftData, Error>>;

export { type CommonOptions, type IAddress, type IHandshakeData, type IHandshakeDescriptionData, type IHandshakePlayerData, type IMinecraftData, type IPacketHeader, type Loadable, type LoadableAddress, type LoadableUrl, MinecraftPackageType, PacketDecoder, createHandshakePacket, createPingPacket, ping, pingResult, pingUri, pingUriResult, srvRecordResult, srvRecordsResult };
