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

import { Key } from '@aedart/contracts/support';
import { DecoratorResult, ClassDecoratorResult, ClassMethodDecoratorResult } from '@aedart/contracts';
import { Throwable } from '@aedart/contracts/support/exceptions';

/**
 * Meta Entry
 */
interface MetaEntry {
    /**
     * Key or path identifier
     *
     * @type {Key}
     */
    key: Key;
    /**
     * Value to store
     *
     * @type {unknown}
     */
    value: unknown;
}

/**
 * Decorator context types for any decorator
 */
type Context = DecoratorContext;
/**
 * Callback that returns a meta entry object.
 */
type MetaCallback = (target: object, context: Context, owner: object) => MetaEntry;
/**
 * Metadata Record
 */
type MetadataRecord = DecoratorMetadata;
/**
 * Reference to the owner object that contains metadata
 */
type MetaOwnerReference = WeakRef<object>;
/**
 * Initializer callback
 *
 * @see ClassDecoratorContext.addInitializer
 */
type InitializerCallback = (this: any) => void;
/**
 * A location (key or path) to a metadata entry, in a given owner object
 */
type MetaAddress = [
    MetaOwnerReference,
    Key
];

/**
 * Meta Decorator Target Context
 */
interface MetaTargetContext {
    /**
     * The class that owns the meta
     *
     * @type {object}
     */
    owner: object;
    /**
     * "This" argument
     *
     * @type {any}
     */
    thisArg: any;
    /**
     * The target class, field, method... that is being decorated
     *
     * @type {object}
     */
    target: object;
    /**
     * Decorator context
     *
     * @type {Context}
     */
    context: Context;
}

/**
 * Meta Repository
 */
interface Repository {
    /**
     * The owner class
     *
     * @type {object}
     */
    readonly owner: object;
    /**
     * Set value for given key
     *
     * **Caution**: _Method is intended to be invoked inside a decorator!_
     *
     * @param {object} target Decorator target, e.g. class, field, method...etc
     * @param {Context} context
     * @param {Key | MetaCallback} key
     * @param {any} [value] Value to be stored. Ignored if `key` argument is a callback.
     *
     * @return {DecoratorResult}
     */
    set(target: object, context: Context, key: Key | MetaCallback, value?: any): DecoratorResult;
    /**
     * Get value for given key
     *
     * @template T Return value type
     * @template D=undefined Type of default value
     *
     * @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;
    /**
     * Get all metadata
     *
     * @return {MetadataRecord}
     */
    all(): MetadataRecord;
}

/**
 * Meta Target Repository
 *
 * Responsible for associating metadata directory with a target class or class method.
 */
interface TargetRepository {
    /**
     * Set value for given key, and associates it directly with the target
     *
     * **Caution**: _Method is intended to be invoked inside a decorator!_
     *
     * @param {object} target Class or class method target
     * @param {Context} context
     * @param {Key | MetaCallback} key
     * @param {any} [value] Value to be stored. Ignored if `key` argument is a callback.
     *
     * @return {ClassDecoratorResult | ClassMethodDecoratorResult}
     *
     * @throws {MetaException}
     */
    set(target: object, context: Context, key: Key | MetaCallback, value?: any): ClassDecoratorResult | ClassMethodDecoratorResult;
    /**
     * Get value for given key
     *
     * @template T Return value type
     * @template D=undefined Type of default value
     *
     * @param {object} target Class or class method target
     * @param {Key} key
     * @param {D} [defaultValue]
     *
     * @return {T | D}
     */
    get<T, D = undefined>(target: object, key: Key, defaultValue?: D): T | D;
    /**
     * Determine if value exists for key
     *
     * @param {object} target Class or class method target
     * @param {Key} key
     *
     * @return {boolean}
     */
    has(target: object, key: Key): boolean;
    /**
     * Determine there is any metadata associated with target
     *
     * @param {object} target
     *
     * @return {boolean}
     */
    hasAny(target: object): boolean;
    /**
     * Inherit "target" meta from a base class.
     *
     * **Note**: _Method is intended to be used as a decorator for static class methods,
     * in situations where you overwrite static methods and wish to inherit
     * "target" meta from the parent method._
     *
     * @param {object} target
     * @param {Context} context
     *
     * @return {ClassMethodDecoratorResult}
     *
     * @throws {MetaException}
     */
    inherit(target: object, context: Context): ClassMethodDecoratorResult;
}

/**
 * The kind of element that is being decorated
 *
 * @see https://github.com/tc39/proposal-decorators
 */
declare enum Kind {
    class = 1,
    method = 2,
    getter = 3,
    setter = 4,
    field = 5,
    accessor = 6
}
//# sourceMappingURL=Kind.d.ts.map

/**
 * Meta Exception
 *
 * To be thrown when metadata cannot be obtained or written for an owner class
 */
interface MetaException extends Throwable {
}

/**
 * Support Meta identifier
 *
 * @type {Symbol}
 */
declare const SUPPORT_META: unique symbol;
/**
 * The well-known symbol for metadata
 * @see https://github.com/tc39/proposal-decorator-metadata
 *
 * @type {symbol}
 */
declare const METADATA: unique symbol;
/**
 * Symbol used for "target" metadata
 *
 * @type {symbol}
 */
declare const TARGET_METADATA: unique symbol;

export { type Context, type InitializerCallback, Kind, METADATA, type MetaAddress, type MetaCallback, type MetaEntry, type MetaException, type MetaOwnerReference, type MetaTargetContext, type MetadataRecord, type Repository, SUPPORT_META, TARGET_METADATA, type TargetRepository };
