import { Message, ClientEvents } from "discord.js";
import { EsModuleClass, isModuleClass } from "../util";

export type CommandType = "prefix" | "mention";

export interface ICommand extends CommandOptions {
    action : MessageCommand;
}

export interface CommandOptions {
    type: CommandType;
    pattern: string | RegExp;
    documentation?: CommandDocumentation;
    auth?: boolean;
}

export interface CommandDocumentation {
    description: string;
    example: string;
}

export type MessageCommand = (data: ICommandData) => void | Promise<void>;
export interface ICommandData {
    message: Message;
    contents: string | RegExpMatchArray;
}

export type BotEvent = (data: BotEventData, ...params: unknown[]) => void | Promise<void>;
export interface BotEventData {
    actionsDone: boolean;
}

interface CustomEventType {
    message: [string];
    indirectMention: [string];
    directMention: [string];
    postMessage: [string];
}
const customEvents : Set<keyof CustomEventType> = new Set(["directMention", "indirectMention", "message", "postMessage"]);
export type EventType = keyof ClientEvents | keyof CustomEventType;

export function isCustomEvent(s: string): s is keyof CustomEventType {
    return customEvents.has(s as keyof CustomEventType);
}

export interface IPlugin {
    PLUGIN_TYPE?: string;
    botCommands?: ICommand[];
    botEvents?: { [action in EventType]?: BotEvent };
    start?(): Promise<void>;
}

export function Command(opts: CommandOptions) : MethodDecorator {
    return function<T>(target: unknown, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>): void {
        if(!descriptor.value)
            throw new Error("The decorator value must be initialized!");
        const plg = target as IPlugin;
        if (!plg.botCommands)
            plg.botCommands = [];
        plg.botCommands.push({
            action: descriptor.value as unknown as MessageCommand,
            ...opts
        });
    };
}

export function Event(type: EventType): MethodDecorator {
    return function<T>(target: unknown, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>): void {
        if(!descriptor.value)
            throw new Error("The decorator value must be initialized!");
        const plg = target as IPlugin;
        if(!plg.botEvents)
            plg.botEvents = {};
        plg.botEvents[type] = descriptor.value as unknown as BotEvent;
    };
}

export const PLUGIN_TYPE_DESCRIPTOR = "PLUGIN_TYPE_DESCRIPTOR";
export function Plugin<T extends {new(...params: unknown[]): unknown}>(base: T): void {
    base.prototype.PLUGIN_TYPE = PLUGIN_TYPE_DESCRIPTOR;
}

export function isPlugin(obj: unknown): obj is EsModuleClass<IPlugin> {
    return isModuleClass<IPlugin>(obj) && obj.prototype.PLUGIN_TYPE == PLUGIN_TYPE_DESCRIPTOR;
}