/// <reference path="type.d.ts" />

import type { BinaryToTextEncoding } from "node:crypto";

interface Asset extends MimeTypeAction {
    uri?: string;
}

interface TextAsset extends Asset, LocationUri {
    content?: string;
}

interface ResourceAction extends TaskAction, WatchAction {
    wbnUri?: string;
}

interface MultipartAction {
    formData?: BlobValue;
}

interface BlobValue extends KeyValue<string, unknown> {
    filename?: string;
}

export interface KeyValue<T, out U> {
    key: T;
    value: U;
}

export interface FileAsset extends TextAsset, OutputAction, DocumentAction, MultipartAction, ResourceAction, IncrementalAction, ChecksumAction {
    format?: string | string[];
    dataView?: NodeJS.ArrayBufferView;
    base64?: string;
    imported?: boolean | string[];
    flags?: number;
}

export interface DataSource<T = unknown> extends DocumentAction {
    source: string;
    uri?: string;
    index?: number;
    limit?: number;
    query?: T;
    postQuery?: string | FunctionType;
    preRender?: string | FunctionType;
    whenEmpty?: string | FunctionType;
    removeEmpty?: boolean;
    ignoreCache?: IntBool | FirstOf<number, number>;
    transactionState?: number;
    transactionFail?: boolean;
}

export interface DbDataSource<T = unknown, U = unknown, V = unknown, W = unknown, X = unknown> extends DataSource<T> {
    name?: string;
    table?: string;
    credential?: string | W;
    options?: U;
    update?: V;
    withCommand?: string | [string, string];
    parallel?: boolean;
    flat?: number;
    willAbort?: boolean;
    streamRow?: boolean | string | ((row: unknown) => Error | false | void) | null;
    usePool?: boolean | X;
}

export interface OutputAction {
    moveTo?: string;
    commands?: string[];
    compress?: CompressFormat[];
    willChange?: boolean;
}

export interface MimeTypeAction {
    mimeType?: string;
}

export interface TaskAction {
    tasks?: TaskCommand[];
}

export interface WatchAction {
    watch?: WatchValue;
}

export interface WorkerAction {
    worker?: boolean | number;
}

export interface BundleAction<T = unknown> {
    bundleId?: number | string;
    bundleIndex?: number;
    bundleReplace?: T;
    bundleQueue?: Promise<unknown>[];
    trailingContent?: string | string[];
    exported?: boolean;
}

export interface ChecksumAction {
    checksum?: ChecksumValue;
    checksumOutput?: ChecksumValue;
}

export interface EncodingAction<T = BufferEncoding> {
    encoding?: T;
}

export interface DocumentAction<T = BufferEncoding> extends EncodingAction<T> {
    document?: string | string[];
}

export interface AttributeAction<T = AttributeMap> {
    attributes?: T;
}

export interface HashAction {
    hash?: string;
}

export interface TextAction {
    textContent?: string;
}

export interface ExcludeAction {
    exclude?: boolean;
}

export interface StorageAction<T = unknown> {
    cloudStorage?: T[];
}

export interface ElementAction<T = XmlTagNode> {
    element?: T;
}

export interface MetadataAction<T = unknown> {
    metadata?: T;
}

export interface ImportAction<T = unknown> {
    imports?: T;
}

export interface IncrementalAction {
    incremental?: boolean | IncrementalMatch;
}

export interface FromAction {
    from?: string[];
}

export interface ExpiresAction<T = number | string> {
    expires?: T;
}

export interface TaskCommand<T = PlainObject> {
    handler?: string;
    task?: string | T;
    preceding?: boolean;
}

export interface TagData {
    tagName: string;
    tagCount?: number;
    tagIndex?: number;
}

export interface TagAppend extends TagData, TextAction {
    order: number;
    id?: string;
    prepend?: boolean;
    nextSibling?: number;
}

export interface TagLocator {
    id: string;
    index?: number;
    count?: number;
}

export interface XmlNode extends AttributeAction {
    index?: number;
    outerXml?: string;
    innerXml?: string;
    dynamic?: string;
    ignoreCase?: boolean;
}

export interface XmlTagNode extends XmlNode, TagData, TextAction {
    id?: StringMap;
    locator?: TagLocator;
    append?: TagAppend;
    removed?: boolean;
}

export interface WebSocketClient {
    socketId?: string;
    port?: number;
    secure?: boolean;
}

export interface WebSocketConnection extends Required<WebSocketClient> {
    expired: number;
}

export interface LocationUri {
    pathname: string;
    filename: string;
}

export interface ViewEngine<T = unknown, U = PlainObject> extends ExpiresAction {
    name?: string;
    singleRow?: boolean;
    outputEmpty?: boolean;
    options?: {
        compile?: T;
        output?: U;
    };
}

export interface CompressLevel {
    algorithm?: string;
    level?: number;
    chunkSize?: number;
}

export interface CompressFormat extends CompressLevel, MimeTypeAction, WorkerAction {
    format?: string;
    condition?: string;
    plugin?: string;
    timeout?: number;
    options?: PlainObject;
    metadata?: PlainObject & MimeTypeAction;
}

export interface WatchInterval<T = FileAsset> extends TaskBase {
    id?: string;
    main?: boolean;
    reload?: WatchReload;
    assets?: T[];
    recursive?: boolean | string | string[];
}

export interface WatchReload extends WebSocketClient {
    module?: boolean;
    always?: boolean;
}

export interface WebSocketResponse {
    event?: string;
    socketId?: string | string[];
    type?: string;
    encoding?: BufferEncoding;
    value?: unknown;
    timeStamp?: number;
    status?: LogStatus[];
    errors?: string[];
}

export interface WatchResponse extends WebSocketResponse {
    event: "modified" | "broadcast" | WebSocketEvent;
    action?: string;
    url?: {
        pathname: string;
        search?: string;
    };
    always?: boolean;
}

export interface BroadcastResponse extends WebSocketResponse {
    event: "broadcast" | WebSocketEvent;
    options?: unknown;
}

export interface FileInfo {
    name: string;
    size: number | string;
}

export interface RequestBase<T = PlainObject> {
    baseUrl?: string;
    priority?: number;
    threads?: number;
    broadcastId?: string | string[];
    log?: boolean | string | string[] | T;
    error?: { abort?: boolean | string | string[]; fatal?: boolean };
    ignoreExtensions?: boolean | string | string[];
}

export interface RequestData extends RequestBase, ImportAction<StringMap>, IncrementalAction {
    assets?: FileAsset[];
    dataSource?: DataSource[];
    document?: string[];
    task?: string[];
    modules?: string[];
    update?: WatchInterval;
    checksum?: string | boolean | 1 | ChecksumOutput;
    cache?: boolean | AnyObject;
}

export interface LogStatus<T = number> {
    name: string;
    type: T;
    value: string;
    timeStamp: number;
    from: string;
    sessionId: string;
    duration?: number;
    source?: string;
}

export interface ResponseData<T = unknown> {
    success: boolean;
    data?: T;
    filename?: string;
    downloadKey?: string;
    downloadUrl?: string;
    bytes?: number;
    files?: FileInfo[] | string[];
    status?: LogStatus[];
    error?: ResponseError;
}

export interface ResponseError {
    message: string;
    hint?: string;
}

export interface RequestObserve extends WebSocketClient, ExpiresAction {
    action?: string;
}

export interface ChecksumBase<T = BinaryToTextEncoding> {
    algorithm?: string;
    /** @deprecated digestEncoding */
    digest?: T;
    digestEncoding?: T;
}

export interface ChecksumOutput<T = BinaryToTextEncoding> extends ChecksumBase<T> {
    filename?: string;
    include?: string | string[];
    exclude?: string | string[];
    recursive?: boolean | 1;
}

export interface TaskBase extends ExpiresAction {
    interval?: number | string;
    start?: number | string;
}

export type WebSocketEvent = "close" | "error";
export type IncrementalMatch = "none" | "staging" | "etag" | "exists";
export type TextEncoding = "utf-8" | "utf-16" | "utf-16be" | "utf-16le" | "latin1";
export type AttributeMap = ObjectMap<unknown>;
export type WatchValue = boolean | WatchInterval;
export type ChecksumValue = string | ChecksumBase & { value?: string };