import { BehaviorSubject, Observable, Unsubscribable, Subscribable, fromEventPattern } from 'rxjs';
import { mergeMap, map } from 'rxjs/operators';
import { ICommandEntry, IExecuteCommandParams, ICommandRegistry } from '@workbench-stack/extension-api';
import { ofType } from 'redux-observable';
import { ApolloClient } from 'apollo-client';
import { StaticServices, DynamicStandaloneServices } from '@vscode/monaco-editor/esm/vs/editor/standalone/browser/standaloneServices';
import { ICommandService } from '@vscode/monaco-editor/esm/vs/platform/commands/common/commands';
// TODO
/**  */
export function registerCommandEpic(action$, state$, { apolloClient }: { apolloClient: ApolloClient<any> }): any {

    return action$
        .pipe(
            ofType('REGISTER_COMMAND'),
            map<{ data: { extensions: { nodes: any[] } } }, { data: { extensions: { nodes: any[] } } }>(() =>
                // read command registry store
                // apolloClient.query<{ extensions: { nodes: any[] } }>({
                //     query: EXTENSION_CLIENT,
                // }),
                // apolloClient.query<{ extensions: { nodes: any[] } }>({
                //     query: EXTENSION_CLIENT,
                // }),
                null,
            ),
        );

}

/**
 * Executes the command (in the commands list) specified in params.
 *
 * Most callers should use CommandRegistry's getHover method, which uses the registered commands.
 */
export function executeCommand(commands: ICommandEntry[], params: IExecuteCommandParams): Promise<any> {
    const command = commands.find(c => c.command === params.command);
    if (!command) {
        throw new Error(`command not found: ${JSON.stringify(params.command)}`);
    }
    return Promise.resolve(command.run(...(params.arguments || [])));
}


// export function commandService(apolloClient: ApolloClient<any>, reduxStore): ICommandRegistry {
//     return {
//         registerCommand: (entry): Unsubscribable => {
//             // Enforce uniqueness of command IDs.
//             for (const e of this.entries.value) {
//                 if (e.command === entry.command) {
//                     throw new Error(`command is already registered: ${JSON.stringify(entry.command)}`)
//                 }
//             }

//             this.entries.next([...this.entries.value, entry])
//             return {
//                 unsubscribe: () => {
//                     this.entries.next(this.entries.value.filter(e => e !== entry))
//                 },
//             };
//         },
//         executeCommand: (params): Promise<any> => {
//             return executeCommand(this.commandsSnapshot, params);
//         },
//     };
// }

/** Manages and executes commands from all extensions. */
export class CommandRegistry implements ICommandRegistry {
    private entries = new BehaviorSubject<any[]>([]);

    public registerCommand(entry: any): Unsubscribable {
        // Enforce uniqueness of command IDs.
        for (const e of this.entries.value) {
            if (e.command === entry.command) {
                throw new Error(`command is already registered: ${JSON.stringify(entry.command)}`)
            }
        }

        this.entries.next([...this.entries.value, entry]);
        return {
            unsubscribe: () => {
                this.entries.next(this.entries.value.filter(e => e !== entry));
            },
        };
    }

    public executeCommand(params: any): Promise<any> {
        return executeCommand(this.commandsSnapshot, params);
    }

    /** All commands, emitted whenever the set of registered commands changed. */
    public readonly commands: Observable<any[]> = this.entries;

    /**
     * The current set of commands. Used by callers that do not need to react to commands being registered or
     * unregistered.
     */
    public get commandsSnapshot(): any[] {
        return this.entries.value;
    }
}



// export class CommandService implements ICommandService {
//     private commandService;
//     constructor() {
//         this.commandService = StaticServices.instantiationService.get(ICommandService) as any;
//     }

//     public onWillExecuteCommand = fromEventPattern(this.commandService.onWillExecuteCommand)

//     public _serviceBrand = this.commandService._serviceBrand;
//     public executeCommand(id: string, ...args: any[]) {
//         return this.commandService.executeCommand(id, ...args);
//     }
// }
