import { Subscription } from 'rxjs';
import {
    createProxyAndHandleRequests, WebExtHostCommandShape,
    Connection, ClientThreadCommandsShape, ICommandRegistry,
} from '@workbench-stack/extension-api';
import { CommandsRegistry } from '@vscode/monaco-editor/esm/vs/platform/commands/common/commands';
import { SubscriptionMap } from '../common';


export class ClientCommands implements ClientThreadCommandsShape {
    private subscriptions = new Subscription();
    private registrations = new SubscriptionMap();
    private proxy: WebExtHostCommandShape;

    private readonly _disposables = new Map<string, any>();
    private readonly _generateCommandsDocumentationRegistration;

    constructor(connection: Connection, private registry: ICommandRegistry) {
        this.subscriptions.add(this.registrations);
        this.proxy = createProxyAndHandleRequests('commands', connection, this);
        this._generateCommandsDocumentationRegistration = CommandsRegistry.registerCommand('_generateCommandsDocumentation', () => this._generateCommandsDocumentation());
    }

    public dispose() {
        this._disposables.forEach(value => value.dispose());
        this._disposables.clear();

        this._generateCommandsDocumentationRegistration.dispose();
    }

    private _generateCommandsDocumentation(): Promise<void> {
        return this.proxy.$getContributedCommandHandlerDescriptions().then(result => {
            // add local commands
            const commands = (CommandsRegistry as any).getCommands();
            for (let id in commands) {
                let { description } = commands[id];
                if (description) {
                    result[id] = description;
                }
            }

            // print all as markdown
            const all: string[] = [];
            for (let id in result) {
                all.push('`' + id + '` - ' + _generateMarkdown(result[id]));
            }
        }) as any;
    }

    public $unregister(id: number): void {
        this.registrations.remove(id);
    }

    public $registerCommand(id: number, command: string): void {
        this.registrations.add(
            id,
            this.registry.registerCommand({
                command,
                run: (editor: any): any => {
                    // https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.iactiondescriptor.html
                    // Methods -> run() | run(editor: ICodeEditor): void | Promise<void>
                    return this.proxy.$executeContributedCommand(id, []);
                },
            }),
        );
    }

    public $executeCommand(command: string, args: any[]): Promise<any> {
        // const commandService = StaticServices.instantiationService.get(ICommandService) as any;
        // commandService.executeCommand('line', () => console.log('command'));
        return this.registry.executeCommand({ command, arguments: args });
    }

    public unsubscribe(): void {
        this.subscriptions.unsubscribe();
    }

    public $getCommands(): Promise<string[]> {
        return Promise.resolve(Object.keys((CommandsRegistry as any).getCommands()));
    }
}

//TODO ICommandHandlerDescription should be in description type
function _generateMarkdown(description: string | any): string {
    if (typeof description === 'string') {
        return description;
    } else {
        let parts = [description.description];
        parts.push('\n\n');
        if (description.args) {
            for (let arg of description.args) {
                parts.push(`* _${arg.name}_ - ${arg.description || ''}\n`);
            }
        }
        if (description.returns) {
            parts.push(`* _(returns)_ - ${description.returns}`);
        }
        parts.push('\n\n');
        return parts.join('');
    }
}
