/**
 * @aedart/contracts
 * 
 * BSD-3-Clause, Copyright (c) 2023-present Alin Eugen Deac <aedart@gmail.com>.
 */

import { Callback } from '@aedart/contracts';

/**
 * Arrayable
 *
 * This interface is an adaptation of Laravel's Arrayable interface.
 * License MIT, Copyright (c) Taylor Otwell.
 *
 * @see https://github.com/laravel/framework/blob/master/src/Illuminate/Contracts/Support/Arrayable.php
 *
 * @template T
 */
interface Arrayable<T = unknown> {
    /**
     * Returns an array representation of this object
     *
     * @returns {T[]}
     */
    toArray(): T[];
}

/**
 * Callback Wrapper
 */
interface CallbackWrapper {
    /**
     * The callback
     *
     * @type {Callback}
     *
     * @readonly
     */
    readonly callback: Callback;
    /**
     * "This" value that callback is bound to
     *
     * @type {object | undefined}
     *
     * @readonly
     */
    readonly binding: object | undefined;
    /**
     * Arguments to be passed on to the callback
     * when invoked.
     *
     * @type {any[]}
     */
    arguments: any[];
    /**
     * Add arguments to be passed on to the callback
     * when it is invoked.
     *
     * @param {...any} args
     *
     * @return {this}
     */
    with(...args: any[]): this;
    /**
     * Determine if callback has any arguments
     *
     * @return {boolean}
     */
    hasArguments(): boolean;
    /**
     * Bind callback to given "this" value
     *
     * @param {object} thisArg
     *
     * @return {this}
     *
     * @throws {TypeError}
     */
    bind(thisArg: object): this;
    /**
     * Determine if a binding has been set
     *
     * @return {boolean}
     */
    hasBinding(): boolean;
    /**
     * Invoke the callback
     *
     * @return {any}
     *
     * @throws {Error}
     */
    call(): any;
}

/**
 * Key or path identifier
 */
type Key = OneOrMany<PropertyKey>;
/**
 * One or many
 */
type OneOrMany<T> = T | ReadonlyArray<T>;

/**
 * Has Arbitrary Data
 */
interface HasArbitraryData {
    /**
     * Set value for key
     *
     * @param {Key} key
     * @param {any} value
     *
     * @return {this}
     */
    set(key: Key, value: any): this;
    /**
     * Get value for key, or default if key does not exist
     *
     * @template T
     * @template D=undefined
     *
     * @param {Key} key
     * @param {D} [defaultValue]
     *
     * @return {T | D}
     */
    get<T, D = undefined>(key: Key, defaultValue?: D): T | D;
    /**
     * Determine if value exists for key
     *
     * @param {Key} key
     *
     * @return {boolean}
     */
    has(key: Key): boolean;
    /**
     * Delete value for key
     *
     * @param {Key} key
     *
     * @return {boolean}
     */
    forget(key: Key): boolean;
    /**
     * Returns all arbitrary data
     *
     * @return {Record<PropertyKey, any>}
     */
    all(): Record<PropertyKey, any>;
    /**
     * Flush all arbitrary data
     *
     * @return {void}
     */
    flush(): void;
}

/**
 * Support identifier
 *
 * @type {Symbol}
 */
declare const SUPPORT: unique symbol;

export { type Arrayable, type CallbackWrapper, type HasArbitraryData, type Key, type OneOrMany, SUPPORT };
