import { ExecaReturnValue } from 'execa';
import { Temporal } from '@js-temporal/polyfill';

/**
 * Create a resolver factory
 *
 * @param tsconfig - The path to the tsconfig file
 * @returns The resolver factory
 */
declare const createResolver: (rootPath?: string, tsconfig?: string, autoInstall?: boolean) => (request: string) => Promise<string>;
export { createResolver }
export { createResolver as createResolver_alias_1 }
export { createResolver as createResolver_alias_2 }

/**
 * The input types that can be used to create a DateTime object
 */
declare type DateTimeInput =
| StormDateTime
| Temporal.Instant
| Date
| string
| number
| bigint
| null
| undefined;

/**
 * The options to use when creating a new DateTime object
 */
declare interface DateTimeOptions {
    /**
     * The time zone to use. If not specified, the default time zone for the runtime is used.
     */
    timeZone?: Temporal.TimeZoneLike;

    /**
     * The calendar to use. If not specified, the default calendar for the runtime is used.
     */
    calendar?: Temporal.CalendarLike;

    /**
     * If false, the current date and time is defaulted when undefined or null is passed. If true, the current date and time is not defaulted.
     *
     * @default false
     */
    skipDefaulting?: boolean;
}

declare type ErrorCode =
| "success"
| "missing_issue_code"
| "invalid_config"
| "failed_to_load_file"
| "missing_context"
| "record_not_found"
| "required_field_missing"
| "database_query_error"
| "model_validation_error"
| "field_validation_error"
| "invalid_parameter"
| "invalid_request"
| "type_error"
| "processing_error"
| "internal_server_error"
| "user_not_logged_in"
| "unknown_cause";

declare const ErrorCode = {
    success: "success" as ErrorCode,
    missing_issue_code: "missing_issue_code" as ErrorCode,
    invalid_config: "invalid_config" as ErrorCode,
    failed_to_load_file: "failed_to_load_file" as ErrorCode,
    missing_context: "missing_context" as ErrorCode,
    record_not_found: "record_not_found" as ErrorCode,
    required_field_missing: "required_field_missing" as ErrorCode,
    database_query_error: "database_query_error" as ErrorCode,
    model_validation_error: "model_validation_error" as ErrorCode,
    field_validation_error: "field_validation_error" as ErrorCode,
    invalid_parameter: "invalid_parameter" as ErrorCode,
    invalid_request: "invalid_request" as ErrorCode,
    type_error: "type_error" as ErrorCode,
    processing_error: "processing_error" as ErrorCode,
    internal_server_error: "internal_server_error" as ErrorCode,
    user_not_logged_in: "user_not_logged_in" as ErrorCode,
    unknown_cause: "unknown_cause" as ErrorCode
};

export declare const execute: (command: string, rootPath: string) => Promise<ExecaReturnValue<string>>;

export declare const install: (name: string, rootPath: string) => Promise<void>;

/**
 * A plugin loader that can be used to load a plugin module.
 */
declare interface IPluginLoader<TContext = any, TPluginModule extends IPluginModule<TContext> = IPluginModule<TContext>> {
    load: (definition: PluginDefinition, options: Record<string, any>) => Promise<PluginInstance<TContext, TPluginModule>>;
    isValid: (module: TPluginModule) => boolean;
    process: (context: TContext, instance: PluginInstance, options: Record<string, any>) => Promise<void>;
}
export { IPluginLoader }
export { IPluginLoader as IPluginLoader_alias_1 }

/**
 * A plugin manager that can be used to manage plugins.
 */
declare interface IPluginManager {
    /**
     * Finds all the plugins based on the configuration options.
     *
     * @returns The list of definitions for the plugins in the system.
     */
    discover(): Promise<Set<PluginDefinition>>;
    /**
     * Finds the plugin and does a dynamic require to whatever the plugin exports.
     *
     * @param provider - The identifier of the plugin
     * @param options - Run-time options to configure your exports.
     * @returns An object containing the exported functionality.
     */
    instantiate(provider: string, options?: Record<string, any>): Promise<PluginInstance>;
    /**
     * Registers a plugin definition in the manager.
     *
     * This method is used to manually register a plugin at run-time. Uses for
     * this are dynamic plugins.
     *
     * @param provider - The plugin provider path/package name that provides this plugin.
     * @returns `true` if the definition was registered. `false` if there was an error.
     */
    register(provider: string): boolean;
}
export { IPluginManager }
export { IPluginManager as IPluginManager_alias_1 }

/**
 * A plugin module that can be loaded by the plugin system.
 */
declare interface IPluginModule<TContext = any> {
    hooks?: Record<string, PluginHookFn<TContext>>;
}
export { IPluginModule }
export { IPluginModule as IPluginModule_alias_1 }

declare interface IStormLog {
    /**
     * Write a success message to the logs.
     *
     * @param message - The message to print.
     * @returns Either a promise that resolves to void or void.
     */
    success: (...message: any[]) => MaybePromise<void>;

    /**
     * Write a fatal message to the logs.
     *
     * @param message - The fatal message to be displayed.
     * @returns Either a promise that resolves to void or void.
     */
    fatal: (...message: any[]) => MaybePromise<void>;

    /**
     * Write an error message to the logs.
     *
     * @param message - The message to be displayed.
     * @returns Either a promise that resolves to void or void.
     */
    error: (...message: any[]) => MaybePromise<void>;

    /**
     * Write an exception message to the logs.
     *
     * @param message - The message to be displayed.
     * @returns Either a promise that resolves to void or void.
     */
    exception: (...message: any[]) => MaybePromise<void>;

    /**
     * Write a warning message to the logs.
     *
     * @param message - The message to be printed.
     * @returns Either a promise that resolves to void or void.
     */
    warn: (...message: any[]) => MaybePromise<void>;

    /**
     * Write an informational message to the logs.
     *
     * @param message - The message to be printed.
     * @returns Either a promise that resolves to void or void.
     */
    info: (...message: any[]) => MaybePromise<void>;

    /**
     * Write a debug message to the logs.
     *
     * @param message - The message to be printed.
     * @returns Either a promise that resolves to void or void.
     */
    debug: (...message: any[]) => MaybePromise<void>;

    /**
     * Write a trace message to the logs.
     *
     * @param message - The message to be printed.
     * @returns Either a promise that resolves to void or void.
     */
    trace: (...message: any[]) => MaybePromise<void>;

    /**
     * Write an informational message to the logs.
     *
     * @param message - The message to be printed.
     * @returns Either a promise that resolves to void or void.
     */
    log: (...message: any[]) => MaybePromise<void>;

    /**
     * Start a process
     *
     * @param name - The name of the process
     */
    start: (name: string) => MaybePromise<void>;

    /**
     * Write an message to the logs specifying how long it took to complete a process
     *
     * @param name - The name of the process
     * @param startTime - The start time of the process
     */
    stopwatch: (name?: string, startTime?: StormTime) => MaybePromise<void>;

    /**
     * Create a child logger
     *
     * @param name - The name of the child logger
     * @returns The child logger
     */
    child: (options: { name: string } & Record<string, any>) => IStormLog;
}

declare type MaybePromise<T> = T | Promise<T>;

/**
 * The definition of a plugin.
 */
declare interface PluginDefinition {
    /**
     * The id of the plugin.
     */
    id: string;
    /**
     * The plugin provider path/package name that provides this plugin.
     */
    provider: string;
    /**
     * The name of the plugin.
     */
    name: string;
    /**
     * The version of the plugin.
     */
    version: string;
    /**
     * A description of the plugin.
     */
    description?: string;
    /**
     * The plugin's base path/path to the package.json file
     */
    packagePath: string;
    /**
     * The path to the plugin's configuration file.
     */
    configPath?: string;
    /**
     * An optional path to an image to use as the icon for the plugin.
     */
    imagePath?: string;
    /**
     * A list of plugin providers that this plugin depends on.
     */
    dependencies: string[];
    /**
     * A set of optional tags that describe the plugin.
     */
    tags: string[];
    /**
     * The options to pass to the plugin module when instantiating
     */
    options: any;
    /**
     * The path to the plugin's module loader.
     */
    loader: string;
}
export { PluginDefinition }
export { PluginDefinition as PluginDefinition_alias_1 }

declare type PluginDiscoveryMode = "auto" | "fallback" | "none";

declare const PluginDiscoveryMode: {
    AUTO: PluginDiscoveryMode;
    FALLBACK: PluginDiscoveryMode;
    NONE: PluginDiscoveryMode;
};
export { PluginDiscoveryMode }
export { PluginDiscoveryMode as PluginDiscoveryMode_alias_1 }

/**
 * A function that can be used to hook into the plugin system.
 */
declare type PluginHookFn<TContext = any> = (params: TContext) => MaybePromise<TContext | ((params: TContext) => MaybePromise<TContext>)>;
export { PluginHookFn }
export { PluginHookFn as PluginHookFn_alias_1 }

/**
 * An instance of a plugin that has been loaded by the plugin system.
 */
declare interface PluginInstance<TContext = any, TPluginModule extends IPluginModule<TContext> = any> {
    loader: IPluginLoader<TContext, TPluginModule>;
    definition: PluginDefinition;
    module: TPluginModule;
    options: any;
    resolvedPath: string;
    executionDateTime: StormDateTime;
}
export { PluginInstance }
export { PluginInstance as PluginInstance_alias_1 }

/**
 *   A base class to detect properly loaded plugins.
 */
declare abstract class PluginLoader<TContext = any, TPluginModule extends IPluginModule<TContext> = IPluginModule<TContext>> implements IPluginLoader<TContext, TPluginModule> {
    readonly rootPath?: string | undefined;
    readonly tsconfig?: string | undefined;
    readonly autoInstall?: boolean | undefined;
    protected resolver: (request: string) => Promise<string>;
    constructor(rootPath?: string | undefined, tsconfig?: string | undefined, autoInstall?: boolean | undefined);
    abstract process: (context: TContext, instance: PluginInstance<TContext, TPluginModule>, options: Record<string, any>) => Promise<void>;
    load: (definition: PluginDefinition, options?: Record<string, any>) => Promise<PluginInstance<TContext, TPluginModule>>;
    isValid: (module: TPluginModule) => boolean;
    protected instantiate: (definition: PluginDefinition, module: TPluginModule, resolvedPath: string, options?: Record<string, any>) => PluginInstance<TContext, TPluginModule>;
    protected resolve: (definition: PluginDefinition, _?: Record<string, any>) => Promise<any>;
}
export { PluginLoader }
export { PluginLoader as PluginLoader_alias_1 }
export { PluginLoader as PluginLoader_alias_2 }

/**
 * Discovers and instantiates plugins.
 */
declare class PluginManager<TContext = any, TPluginModule extends IPluginModule = any> {
    private _options;
    private _hasDiscovered;
    private _registry;
    private _store;
    private _hooks;
    private _logger;
    private _loaders;
    private _loaderResolver;
    static create: <TContext_1 = any, TPluginModule_1 extends IPluginModule<TContext_1> = any>(logger: IStormLog, options: Omit<Partial<PluginManagerOptions>, "defaultLoader"> & Pick<PluginManagerOptions, "defaultLoader">) => Promise<PluginManager<TContext_1, TPluginModule_1>>;
    /**
     * Creates a new plugin manager object.
     *
     * @param config - The base storm workspace configuration.
     * @param options - The plugin configuration options.
     */
    private constructor();
    discover: () => Promise<Map<string, PluginDefinition>>;
    getInstance: (provider: string, options?: Record<string, any>) => PluginInstance<TContext, TPluginModule> | undefined;
    instantiate: (provider: string, options?: Record<string, any>) => Promise<PluginInstance<TContext, TPluginModule>>;
    execute: (provider: string, context: TContext, options?: Record<string, any>, executionDateTime?: StormDateTime) => Promise<Record<string, Error | null>>;
    invokeHook: (name: string, context: TContext, handler?: ((context: TContext) => Promise<TContext> | TContext) | undefined) => Promise<TContext>;
    register: (provider: string) => Promise<PluginDefinition>;
    getRegistry(): Map<string, PluginDefinition>;
    getLoaders(): Map<string, IPluginLoader<TContext, TPluginModule>>;
    getStore(): Map<string, PluginInstance<TContext, TPluginModule>>;
    /**
     * Generates a cache ID for the plugin and the config.
     *
     * @param provider - The plugin provider.
     * @param options - The options for the plugin.
     * @returns The cache ID.
     */
    private _getCacheId;
    /**
     * Builds the globbing expression based on the configuration options.
     *
     * @returns The globbing expression.
     */
    private _globExpression;
    /**
     * Gets the loader module.
     *
     * @param loader - The loader module to retrieve.
     * @returns The loader module.
     */
    private _getLoader;
    /**
     * Gets the plugin definition from the plugin configuration file.
     *
     * @param _configPath - The path to the plugin configuration file.
     * @returns The plugin definition.
     */
    private _getDefinition;
}
export { PluginManager }
export { PluginManager as PluginManager_alias_1 }
export { PluginManager as PluginManager_alias_2 }

/**
 * The options to configure the plugin manager.
 */
declare interface PluginManagerOptions {
    /**
     * The path to the root of the application.
     *
     * @default process.env.STORM_WORKSPACE_ROOT
     */
    rootPath: string;
    /**
     * The path to the root of the application.
     *
     * @default process.env.STORM_WORKSPACE_ROOT + "/tsconfig.json"
     */
    tsconfig?: string;
    /**
     * Should node_modules be used to discover plugins?
     *
     * @default true
     */
    useNodeModules: boolean;
    /**
     * Should auto-install be used to discover plugins?
     *
     * @default true
     */
    autoInstall: boolean;
    /**
     * A mode specifying how plugins should be discovered from the local filesystem.
     *
     * @remarks
     * `auto` - Automatically discover all plugins in the rootPath
     * `fallback` - Discover plugins in the rootPath if a registered plugin provider is not found
     * `none` - Do not discover plugins in the rootPath, regardless of whether a registered plugin provider is found or not
     *
     * @default "fallback"
     */
    discoveryMode: PluginDiscoveryMode;
    /**
     * The path to the plugin's module loader or an object containing the provider string and loader instance.
     */
    defaultLoader: string | {
        provider: string;
        loader: new (_rootPath?: string, _tsconfig?: string, _autoInstall?: boolean) => IPluginLoader<any, any>;
    };
}
export { PluginManagerOptions }
export { PluginManagerOptions as PluginManagerOptions_alias_1 }

declare type PluginSystemErrorCode = ErrorCode | "module_not_found" | "plugin_not_found" | "plugin_loading_failure";

declare const PluginSystemErrorCode: {
    module_not_found: PluginSystemErrorCode;
    plugin_not_found: PluginSystemErrorCode;
    plugin_loading_failure: PluginSystemErrorCode;
    success: ErrorCode;
    missing_issue_code: ErrorCode;
    invalid_config: ErrorCode;
    failed_to_load_file: ErrorCode;
    missing_context: ErrorCode;
    record_not_found: ErrorCode;
    required_field_missing: ErrorCode;
    database_query_error: ErrorCode;
    model_validation_error: ErrorCode;
    field_validation_error: ErrorCode;
    invalid_parameter: ErrorCode;
    invalid_request: ErrorCode;
    type_error: ErrorCode;
    processing_error: ErrorCode;
    internal_server_error: ErrorCode;
    user_not_logged_in: ErrorCode;
    unknown_cause: ErrorCode;
};
export { PluginSystemErrorCode }
export { PluginSystemErrorCode as PluginSystemErrorCode_alias_1 }

/**
 * A wrapper of the and Date class used by Storm Software to provide Date-Time values
 *
 * @decorator `@Serializable()`
 */
declare @Serializable()
class StormDateTime extends Date {
    /**
     * Type-check to determine if `obj` is a `DateTime` object
     *
     * `isDateTime` returns true if the object passed to it has a `_symbol` property that is equal to
     * `DATE_TIME_SYMBOL`
     *
     * @param obj - the object to check
     * @returns The function isDateTime is returning a boolean value.
     */
    public static isDateTime(obj: unknown): obj is StormDateTime {
        return (
        isDate(obj) &&
        isSet((obj as unknown as StormDateTime)?.instant) &&
        isSet((obj as unknown as StormDateTime)?.zonedDateTime) &&
        isSetString((obj as unknown as StormDateTime)?.timeZoneId)
        );
    }

    /**
     * The current function returns a new StormDateTime object with the current date and time
     *
     * @returns A new instance of StormDateTime with the current date and time.
     */
    public static override now(): number {
        return StormDateTime.current().epochMilliseconds;
    }

    /**
     * The current function returns a new StormDateTime object with the current date and time
     *
     * @returns A new instance of StormDateTime with the current date and time.
     */
    public static current(): StormDateTime {
        return StormDateTime.create(Temporal.Now.instant());
    }

    /**
     * The maximum function returns a new StormDateTime object with the maximum date and time
     *
     * @returns A new instance of StormDateTime with the maximum date and time.
     */
    public static minimum(): StormDateTime {
        return StormDateTime.create(new Date(-8640000000000000));
    }

    /**
     * The maximum function returns a new StormDateTime object with the maximum date and time
     *
     * @returns A new instance of StormDateTime with the maximum date and time.
     */
    public static maximum(): StormDateTime {
        return StormDateTime.create(new Date(8640000000000000));
    }

    /**
     * Creates a new instance of StormDateTime from a string with a specified format.
     *
     * @param dateTime - The input value used to determine the current date and time
     * @param options - The options to use when creating the StormDateTime object
     * @returns A new instance of StormDateTime with the current date and time.
     */
    public static create = (dateTime?: DateTimeInput, options?: DateTimeOptions) =>
    new StormDateTime(dateTime, {
        timeZone:
        (StormDateTime.isDateTime(dateTime) ? dateTime.timeZoneId : options?.timeZone) ??
        process.env.STORM_TIMEZONE ??
        Temporal.Now.timeZoneId(),
        calendar: StormDateTime.isDateTime(dateTime)
        ? dateTime.calendarId
        : options?.calendar ?? new Intl.DateTimeFormat().resolvedOptions().calendar
    });

    /**
     * A private accessor that stores the `Temporal.Instant` object of the DateTime object
     */
    #instant: Temporal.Instant = Temporal.Now.instant();

    /**
     * A private accessor that stores the `Temporal.ZonedDateTime` object of the DateTime object
     */
    #zonedDateTime: Temporal.ZonedDateTime = Temporal.Now.zonedDateTime(
    new Intl.DateTimeFormat().resolvedOptions().calendar,
    process.env.STORM_TIMEZONE ?? Temporal.Now.timeZoneId()
    );

    /**
     * A private accessor that stores the input value used to create the DateTime object
     */
    #input: DateTimeInput;

    /**
     * A private accessor that stores the options used to create the DateTime object
     */
    #options: DateTimeOptions;

    public constructor(dateTime?: DateTimeInput, options?: DateTimeOptions) {
        let _dateTime = dateTime;

        const input = dateTime;
        if (!_dateTime && !options?.skipDefaulting) {
            _dateTime = Temporal.Now.instant();
        }

        const instant = !_dateTime
        ? undefined
        : StormDateTime.isDateTime(_dateTime)
        ? _dateTime.instant
        : Temporal.Instant.from(
        isDate(_dateTime)
        ? _dateTime.toJSON()
        : isObject(_dateTime) && "epochMilliseconds" in _dateTime
        ? new Date(Number(_dateTime.epochMilliseconds)).toISOString()
        : isNumber(_dateTime) || isBigInt(_dateTime)
        ? new Date(Number(_dateTime)).toISOString()
        : _dateTime
        );

        super(instant ? Number(instant.epochMilliseconds) : "MISSING_DATE");
        if (instant && this.validate(_dateTime, options)) {
            this.#instant = instant;

            const timeZone = options?.timeZone
            ? options?.timeZone
            : process.env.TZ
            ? process.env.TZ
            : Temporal.Now.timeZoneId();
            this.#zonedDateTime = options?.calendar
            ? this.#instant.toZonedDateTime({
                timeZone,
                calendar: options.calendar
            })
            : this.#instant.toZonedDateTimeISO(timeZone);
        }

        this.#input = input;
        this.#options = options ?? {};
    }

    /**
     * An accessor that returns the epoch milliseconds of the DateTime object
     */
    public get epochMilliseconds(): number {
        return this.instant.epochMilliseconds;
    }

    /**
     * An accessor that returns the `Temporal.Instant` object of the DateTime object
     */
    public get instant(): Temporal.Instant {
        return this.#instant;
    }

    /**
     * An accessor that sets the `Temporal.Instant` object of the DateTime object
     */
    protected set instant(instant: Temporal.Instant) {
        this.#instant = instant;
    }

    /**
     * An accessor that returns the `Temporal.ZonedDateTime` object of the DateTime object
     */
    public get zonedDateTime(): Temporal.ZonedDateTime {
        return this.#zonedDateTime;
    }

    /**
     * An accessor that sets the `Temporal.ZonedDateTime` object of the DateTime object
     */
    protected set zonedDateTime(zonedDateTime: Temporal.ZonedDateTime) {
        this.#zonedDateTime = zonedDateTime;
    }

    /**
     * An accessor that returns the `calendarId` string of the DateTime object
     */
    public get calendarId(): string {
        return this.#zonedDateTime.calendarId;
    }

    /**
     * An accessor that returns the `timeZoneId` string of the DateTime object
     */
    public get timeZoneId(): string {
        return this.#zonedDateTime.timeZoneId;
    }

    /**
     * An accessor that returns the `isValid` boolean of the DateTime object
     */
    public get isValid(): boolean {
        return this.validate(this.#zonedDateTime.epochMilliseconds, this.#options);
    }

    /**
     * Returns the input value used to create the DateTime object
     */
    public get input(): DateTimeInput {
        return this.#input;
    }

    /**
     * Returns the options used to create the DateTime object
     */
    public get options(): DateTimeOptions {
        return this.#options;
    }

    /**
     * Validate the input date value
     *
     * @param dateTime - The date value to validate
     * @param _options - The options to use
     * @returns A boolean representing whether the value is a valid *date-time*
     */
    protected validate(value?: DateTimeInput, _options?: DateTimeOptions): boolean {
        if (StormDateTime.isDateTime(value)) {
            return value.isValid;
        }
        if (isInstant(value)) {
            return !!value.epochMilliseconds;
        }

        let datetime: string | undefined;
        if (isDate(value) || isNumber(value) || isBigInt(value)) {
            let date!: Date;
            if (isNumber(value) || isBigInt(value)) {
                date = new Date(Number(value));
            } else {
                date = value;
            }

            if (Number.isNaN(date.getTime())) {
                return false;
            }

            datetime = date.toUTCString();
        } else {
            datetime = value === null || value === void 0 ? void 0 : value.toUpperCase();
        }

        if (!datetime) {
            return false;
        }

        // Validate the structure of the date-string
        if (!RFC_3339_DATETIME_REGEX.test(datetime)) {
            return false;
        }

        // Check if it is a correct date using the javascript Date parse() method.
        if (!Date.parse(datetime)) {
            return false;
        }

        return true;
    }

    /**
     * Returns the stored time value in milliseconds since midnight, January 1, 1970 UTC.
     */
    public override getTime(): number {
        return this.epochMilliseconds;
    }

    /**
     *  Gets the year, using local time.
     */
    public override getFullYear(): number {
        return this.#zonedDateTime.year;
    }

    /**
     *  Gets the year using Universal Coordinated Time (UTC).
     */
    public override getUTCFullYear(): number {
        return this.#instant.toZonedDateTimeISO("UTC").year;
    }

    /**
     *  Gets the month, using local time.
     */
    public override getMonth(): number {
        return this.#zonedDateTime.month;
    }

    /**
     *  Gets the month of a Date object using Universal Coordinated Time (UTC).
     */
    public override getUTCMonth(): number {
        return this.#instant.toZonedDateTimeISO("UTC").month;
    }

    /**
     *  Gets the day-of-the-month, using local time.
     */
    public override getDate(): number {
        return this.#zonedDateTime.day;
    }

    /**
     *  Gets the day-of-the-month, using Universal Coordinated Time (UTC).
     */
    public override getUTCDate(): number {
        return this.#instant.toZonedDateTimeISO("UTC").day;
    }

    /**
     *  Gets the day of the week, using local time.
     */
    public override getDay(): number {
        return this.#zonedDateTime.dayOfWeek;
    }

    /**
     *  Gets the day of the week using Universal Coordinated Time (UTC).
     */
    public override getUTCDay(): number {
        return this.#instant.toZonedDateTimeISO("UTC").dayOfWeek;
    }

    /**
     *  Gets the hours in a date, using local time.
     */
    public override getHours(): number {
        return this.#zonedDateTime.hour;
    }

    /**
     *  Gets the hours value in a Date object using Universal Coordinated Time (UTC).
     */
    public override getUTCHours(): number {
        return this.#instant.toZonedDateTimeISO("UTC").hour;
    }

    /**
     *  Gets the minutes of a Date object, using local time.
     */
    public override getMinutes(): number {
        return this.#zonedDateTime.minute;
    }

    /**
     *  Gets the minutes of a Date object using Universal Coordinated Time (UTC).
     */
    public override getUTCMinutes(): number {
        return this.#instant.toZonedDateTimeISO("UTC").minute;
    }

    /**
     *  Gets the seconds of a Date object, using local time.
     */
    public override getSeconds(): number {
        return this.#zonedDateTime.second;
    }

    /**
     *  Gets the seconds of a Date object using Universal Coordinated Time (UTC).
     */
    public override getUTCSeconds(): number {
        return this.#instant.toZonedDateTimeISO("UTC").second;
    }

    /**
     *  Gets the milliseconds of a Date, using local time.
     */
    public override getMilliseconds(): number {
        return this.#zonedDateTime.millisecond;
    }

    /**
     *  Gets the milliseconds of a Date object using Universal Coordinated Time (UTC).
     */
    public override getUTCMilliseconds(): number {
        return this.#instant.toZonedDateTimeISO("UTC").millisecond;
    }

    /**
     *  Gets the difference in minutes between the time on the local computer and Universal Coordinated Time (UTC).
     */
    public override getTimezoneOffset(): number {
        return this.#zonedDateTime.offsetNanoseconds / 1000000;
    }

    /**
     * Sets the date and time value in the Date object.
     * @param time - A numeric value representing the number of elapsed milliseconds since midnight, January 1, 1970 GMT.
     */
    public override setTime(time: number): number {
        this.#zonedDateTime = this.#zonedDateTime.add({
            milliseconds: time - this.epochMilliseconds
        });
        this.#instant = this.#zonedDateTime.toInstant();

        return super.setTime(this.#instant.epochMilliseconds);
    }

    /**
     * Sets the milliseconds value in the Date object using local time.
     * @param millisecond - A numeric value equal to the millisecond value.
     */
    public override setMilliseconds(millisecond: number): number {
        this.#zonedDateTime = this.#zonedDateTime.with({ millisecond });
        this.#instant = this.#zonedDateTime.toInstant();

        return super.setMilliseconds(this.#instant.toZonedDateTimeISO("UTC").millisecond);
    }

    /**
     * Sets the milliseconds value in the Date object using Universal Coordinated Time (UTC).
     * @param millisecond - A numeric value equal to the millisecond value.
     */
    public override setUTCMilliseconds(millisecond: number): number {
        this.#instant = this.#instant.toZonedDateTimeISO("UTC").with({ millisecond }).toInstant();
        this.#zonedDateTime = this.#instant.toZonedDateTime({
            timeZone: this.timeZoneId,
            calendar: this.calendarId
        });

        return super.setUTCMilliseconds(this.#instant.toZonedDateTimeISO("UTC").millisecond);
    }

    /**
     * Sets the seconds value in the Date object using local time.
     * @param second -  A numeric value equal to the seconds value.
     * @param millisecond - A numeric value equal to the milliseconds value.
     */
    public override setSeconds(second: number, millisecond?: number): number {
        this.#zonedDateTime = this.#zonedDateTime.with({ second, millisecond });
        this.#instant = this.#zonedDateTime.toInstant();

        return super.setSeconds(this.#zonedDateTime.second, this.#zonedDateTime.millisecond);
    }

    /**
     * Sets the seconds value in the Date object using Universal Coordinated Time (UTC).
     * @param second - A numeric value equal to the seconds value.
     * @param millisecond - A numeric value equal to the milliseconds value.
     */
    public override setUTCSeconds(second: number, millisecond?: number): number {
        this.#instant = this.#instant
        .toZonedDateTimeISO("UTC")
        .with({ second, millisecond })
        .toInstant();
        this.#zonedDateTime = this.#instant.toZonedDateTime({
            timeZone: this.timeZoneId,
            calendar: this.calendarId
        });

        const utcDateTime = this.#instant.toZonedDateTimeISO("UTC");
        return super.setUTCSeconds(utcDateTime.second, utcDateTime.millisecond);
    }

    /**
     * Sets the minutes value in the Date object using local time.
     * @param minute - A numeric value equal to the minutes value.
     * @param second - A numeric value equal to the seconds value.
     * @param millisecond - A numeric value equal to the milliseconds value.
     */
    public override setMinutes(minute: number, second?: number, millisecond?: number): number {
        this.#zonedDateTime = this.#zonedDateTime.with({
            minute,
            second,
            millisecond
        });
        this.#instant = this.#zonedDateTime.toInstant();

        return super.setMinutes(
        this.#zonedDateTime.minute,
        this.#zonedDateTime.second,
        this.#zonedDateTime.millisecond
        );
    }

    /**
     * Sets the minutes value in the Date object using Universal Coordinated Time (UTC).
     * @param minute - A numeric value equal to the minutes value.
     * @param second - A numeric value equal to the seconds value.
     * @param millisecond - A numeric value equal to the milliseconds value.
     */
    public override setUTCMinutes(minute: number, second?: number, millisecond?: number): number {
        this.#instant = this.#instant
        .toZonedDateTimeISO("UTC")
        .with({ minute, second, millisecond })
        .toInstant();
        this.#zonedDateTime = this.#instant.toZonedDateTime({
            timeZone: this.timeZoneId,
            calendar: this.calendarId
        });

        const utcDateTime = this.#instant.toZonedDateTimeISO("UTC");
        return super.setUTCMinutes(utcDateTime.minute, utcDateTime.second, utcDateTime.millisecond);
    }

    /**
     * Sets the hour value in the Date object using local time.
     *
     * @param hour - A numeric value equal to the hours value.
     * @param minute - A numeric value equal to the minutes value.
     * @param second - A numeric value equal to the seconds value.
     * @param millisecond - A numeric value equal to the milliseconds value.
     */
    public override setHours(
    hour: number,
    minute: number,
    second?: number,
    millisecond?: number
    ): number {
        this.#zonedDateTime = this.#zonedDateTime.with({
            hour,
            minute,
            second,
            millisecond
        });
        this.#instant = this.#zonedDateTime.toInstant();

        return super.setHours(
        this.#zonedDateTime.hour,
        this.#zonedDateTime.minute,
        this.#zonedDateTime.second,
        this.#zonedDateTime.millisecond
        );
    }

    /**
     * Sets the hours value in the Date object using Universal Coordinated Time (UTC).
     *
     * @param hour - A numeric value equal to the hours value.
     * @param minute - A numeric value equal to the minutes value.
     * @param second - A numeric value equal to the seconds value.
     * @param millisecond - A numeric value equal to the milliseconds value.
     */
    public override setUTCHours(
    hour: number,
    minute: number,
    second?: number,
    millisecond?: number
    ): number {
        this.#instant = this.#instant
        .toZonedDateTimeISO("UTC")
        .with({ hour, minute, second, millisecond })
        .toInstant();
        this.#zonedDateTime = this.#instant.toZonedDateTime({
            timeZone: this.timeZoneId,
            calendar: this.calendarId
        });

        const utcDateTime = this.#instant.toZonedDateTimeISO("UTC");
        return super.setUTCHours(
        utcDateTime.hour,
        utcDateTime.minute,
        utcDateTime.second,
        utcDateTime.millisecond
        );
    }

    /**
     * Sets the numeric day-of-the-month value of the Date object using local time.
     *
     * @param day - A numeric value equal to the day of the month.
     */
    public override setDate(day: number): number {
        this.#zonedDateTime = this.#zonedDateTime.with({
            day
        });
        this.#instant = this.#zonedDateTime.toInstant();

        return super.setDate(this.#zonedDateTime.day);
    }

    /**
     * Sets the numeric day of the month in the Date object using Universal Coordinated Time (UTC).
     *
     * @param day - A numeric value equal to the day of the month.
     */
    public override setUTCDate(day: number): number {
        this.#instant = this.#instant.toZonedDateTimeISO("UTC").with({ day }).toInstant();
        this.#zonedDateTime = this.#instant.toZonedDateTime({
            timeZone: this.timeZoneId,
            calendar: this.calendarId
        });

        return super.setUTCDate(this.#instant.toZonedDateTimeISO("UTC").day);
    }

    /**
     * Sets the month value in the Date object using local time.
     *
     * @param month - A numeric value equal to the month. The value for January is 0, and other month values follow consecutively.
     * @param day - A numeric value representing the day of the month. If this value is not supplied, the value from a call to the getDate method is used.
     */
    public override setMonth(month: number, day?: number): number {
        this.#zonedDateTime = this.#zonedDateTime.with({ month, day });
        this.#instant = this.#zonedDateTime.toInstant();

        return super.setMonth(this.#zonedDateTime.month, this.#zonedDateTime.day);
    }

    /**
     * Sets the month value in the Date object using Universal Coordinated Time (UTC).
     *
     * @param month - A numeric value equal to the month. The value for January is 0, and other month values follow consecutively.
     * @param day - A numeric value representing the day of the month. If it is not supplied, the value from a call to the getUTCDate method is used.
     */
    public override setUTCMonth(month: number, day?: number): number {
        this.#instant = this.#instant.toZonedDateTimeISO("UTC").with({ month, day }).toInstant();
        this.#zonedDateTime = this.#instant.toZonedDateTime({
            timeZone: this.timeZoneId,
            calendar: this.calendarId
        });

        const utcDateTime = this.#instant.toZonedDateTimeISO("UTC");
        return super.setUTCMonth(utcDateTime.month, utcDateTime.day);
    }

    /**
     * Sets the year of the Date object using local time.
     * @param year - A numeric value for the year.
     * @param month - A zero-based numeric value for the month (0 for January, 11 for December). Must be specified if numDate is specified.
     * @param day - A numeric value equal for the day of the month.
     */
    public override setFullYear(year: number, month?: number, day?: number): number {
        this.#zonedDateTime = this.#zonedDateTime.with({ year, month, day });
        this.#instant = this.#zonedDateTime.toInstant();

        return super.setFullYear(
        this.#zonedDateTime.year,
        this.#zonedDateTime.month,
        this.#zonedDateTime.day
        );
    }

    /**
     * Sets the year value in the Date object using Universal Coordinated Time (UTC).
     *
     * @param year - A numeric value equal to the year.
     * @param month - A numeric value equal to the month. The value for January is 0, and other month values follow consecutively. Must be supplied if numDate is supplied.
     * @param day - A numeric value equal to the day of the month.
     */
    public override setUTCFullYear(year: number, month?: number, day?: number): number {
        this.#instant = this.#instant.toZonedDateTimeISO("UTC").with({ year, month, day }).toInstant();
        this.#zonedDateTime = this.#instant.toZonedDateTime({
            timeZone: this.timeZoneId,
            calendar: this.calendarId
        });

        const utcDateTime = this.#instant.toZonedDateTimeISO("UTC");
        return super.setUTCFullYear(utcDateTime.year, utcDateTime.month, utcDateTime.day);
    }

    /**
     * It returns a plain date object from a DateTime object
     *
     * @returns A PlainDate object.
     */
    public getPlainDate(): StormDateTime {
        return StormDateTime.create(
        this.#zonedDateTime.toPlainDate().toZonedDateTime({
            timeZone: Temporal.Now.timeZoneId(),
            plainTime: undefined
        }).epochMilliseconds,
            {
            timeZone: this.#zonedDateTime.timeZoneId,
            calendar: this.#zonedDateTime.calendarId
        }
        );
    }

    /**
     * `getPlainTime` returns a `PlainTime` object from a `DateTime` object
     *
     * @returns A PlainTime object.
     */
    public getPlainTime(): StormDateTime {
        return StormDateTime.create(
        this.#zonedDateTime.toPlainTime().toZonedDateTime({
            timeZone: Temporal.Now.timeZoneId(),
            plainDate: Temporal.PlainDate.from({
                year: 1970,
                month: 0,
                day: 1
            })
        }).epochMilliseconds,
            {
            timeZone: this.#zonedDateTime.timeZoneId,
            calendar: this.#zonedDateTime.calendarId
        }
        );
    }

    /**
     * It returns the duration between two dates.
     *
     * @param dateTimeTo - DateTime = DateTime.current
     * @returns A duration object.
     */
    public since(dateTimeTo: StormDateTime = StormDateTime.current()): Temporal.Duration {
        return this.#instant.since(dateTimeTo.instant);
    }

    /**
     * It returns the duration between two date times.
     *
     * @param dateTimeTo - DateTime = DateTime.current
     * @returns A duration object.
     */
    public getDuration(dateTimeTo: StormDateTime = StormDateTime.current()): Temporal.Duration {
        return this.instant.since(dateTimeTo.instant);
    }
}

/**
 * A wrapper of the and Date class used by Storm Software to provide Date-Time values
 *
 * @decorator `@Serializable()`
 */
declare @Serializable()
class StormTime extends StormDateTime {
    /**
     * The current function returns a new DateTime object with the current date and time
     * @returns A new instance of DateTime with the current date and time.
     */
    public static override now(): number {
        return StormTime.current().epochMilliseconds;
    }

    /**
     * The current function returns a new DateTime object with the current date and time
     * @returns A new instance of DateTime with the current date and time.
     */
    public static override current(): StormTime {
        return StormTime.create(Temporal.Now.instant());
    }

    /**
     * Creates a new instance of DateTime from a string with a specified format.
     *
     * @param time - The input value used to determine the current time
     * @param options - The options to use
     * @returns A new instance of StormTime with the time provided in the time parameter.
     */
    public static override create = (time?: DateTimeInput, options?: DateTimeOptions) =>
    new StormTime(time, {
        timeZone:
        (StormDateTime.isDateTime(time) ? time.timeZoneId : options?.timeZone) ??
        Temporal.Now.timeZoneId(),
        calendar: StormDateTime.isDateTime(time) ? time.calendarId : options?.calendar
    });

    public constructor(dateTime?: DateTimeInput, options?: DateTimeOptions) {
        super(dateTime, options);

        const stormDateTime = StormDateTime.create(dateTime, options);
        this.instant = stormDateTime.instant
        .toZonedDateTimeISO("UTC")
        .with({
            year: 1970,
            month: 1,
            day: 1
        })
        .toInstant();
        this.zonedDateTime = stormDateTime.zonedDateTime.with({
            year: 1970,
            month: 1,
            day: 1
        });
    }

    /**
     * Validate the input time value
     *
     * @param dateTime - The date value to validate
     * @param _options - The options to use
     * @returns A boolean representing whether the value is a valid *date-time*
     */
    protected override validate(value?: DateTimeInput, _options?: DateTimeOptions): boolean {
        if (StormDateTime.isDateTime(value)) {
            return value.isValid;
        }
        if (isInstant(value)) {
            return !!value.epochMilliseconds;
        }

        let datetime: string | undefined;
        if (isDate(value) || isNumber(value) || isBigInt(value)) {
            let date!: Date;
            if (isNumber(value) || isBigInt(value)) {
                date = new Date(Number(value));
            } else {
                date = value;
            }

            if (Number.isNaN(date.getTime())) {
                return false;
            }

            datetime = date.toUTCString();
        } else {
            datetime = value === null || value === void 0 ? void 0 : value.toUpperCase();
        }

        if (!datetime) {
            return false;
        }

        return RFC_3339_TIME_REGEX.test(datetime);
    }

    /**
     *  Gets the year, using local time.
     */
    public override getFullYear(): number {
        return 1970;
    }

    /**
     *  Gets the year using Universal Coordinated Time (UTC).
     */
    public override getUTCFullYear(): number {
        return 1970;
    }

    /**
     *  Gets the month, using local time.
     */
    public override getMonth(): number {
        return 1;
    }

    /**
     *  Gets the month of a Date object using Universal Coordinated Time (UTC).
     */
    public override getUTCMonth(): number {
        return 1;
    }

    /**
     *  Gets the day-of-the-month, using local time.
     */
    public override getDate(): number {
        return 1;
    }

    /**
     *  Gets the day-of-the-month, using Universal Coordinated Time (UTC).
     */
    public override getUTCDate(): number {
        return 1;
    }

    /**
     *  Gets the day of the week, using local time.
     */
    public override getDay(): number {
        return 1;
    }

    /**
     *  Gets the day of the week using Universal Coordinated Time (UTC).
     */
    public override getUTCDay(): number {
        return 1;
    }

    /**
     * It returns the duration between two dates.
     *
     * @param dateTimeTo - DateTime = DateTime.current
     * @returns A duration object.
     */
    public override getDuration(dateTimeTo: StormTime = StormTime.current()): Temporal.Duration {
        return this.instant.since(dateTimeTo.instant);
    }
}

export { }
