import { onEditCallback, onOpenCallback } from "@ast/vencord/diagnostics";
import { I18nHover } from "@ast/vencord/hover";
import { PatchCodeLensProvider, PluginDefCodeLensProvider, WebpackCodeLensProvider } from "@ast/vencord/lenses";
import { PartialModuleJumpCodeLensProvider } from "@ast/webpack/lenses";
import { DefinitionProvider, ReferenceProvider } from "@ast/webpack/lsp";
import { outputChannel } from "@modules/logging";
import { PatchHelper } from "@modules/PatchHelper";
import { handleDiffPayload, handleExtractPayload, moduleCache, sendAndGetData, startWebSocketServer, stopWebSocketServer } from "@server";
import { treeDataProvider } from "@sidebar";
import { SourcePatch } from "@type/ast";
import { Discriminate } from "@type/server";
import { DisablePluginData, FindData, OutgoingMessage, PatchData } from "@type/server/send";

import { startReporter } from "./reporter";

import { commands, ExtensionContext, languages, QuickPickItem, TextDocument, Uri, window as vscWindow, window, workspace } from "vscode";

export let extensionUri: Uri;
export let extensionPath: string;


export function activate(context: ExtensionContext) {
    extensionUri = context.extensionUri;
    extensionPath = context.extensionPath;
    startWebSocketServer();
    context.subscriptions.push(
        window.registerTreeDataProvider("equicordSettings", new treeDataProvider()),
        workspace.onDidChangeTextDocument(onEditCallback),
        workspace.onDidOpenTextDocument(onOpenCallback),
        workspace.onDidCloseTextDocument(PatchHelper.onCloseDocument),
        workspace.onDidChangeTextDocument(PatchHelper.changeDocument),
        window.onDidChangeActiveTextEditor(PatchHelper.changeActiveEditor),
        window.tabGroups.onDidChangeTabs(PatchHelper.onTabClose),
        languages.registerCodeLensProvider(
            { pattern: "**/{plugins,userplugins,equicordplugins,plugins/_*,equicordplugins/_*}/{*.ts,*.tsx,**/index.ts,**/index.tsx}" },
            new PluginDefCodeLensProvider(),
        ),
        languages.registerCodeLensProvider(
            { pattern: "**/{plugins,userplugins,equicordplugins,plugins/_*,equicordplugins/_*}/{*.ts,*.tsx,**/index.ts,**/index.tsx}" },
            new PatchCodeLensProvider(),
        ),
        languages.registerDefinitionProvider({ language: "javascript" }, new DefinitionProvider()),
        languages.registerReferenceProvider({ language: "javascript" }, new ReferenceProvider()),

        languages.registerCodeLensProvider({ language: "typescript" }, WebpackCodeLensProvider),
        languages.registerCodeLensProvider({ language: "typescriptreact" }, WebpackCodeLensProvider),
        languages.registerCodeLensProvider({ language: "javascript" }, new PartialModuleJumpCodeLensProvider()),
        languages.registerHoverProvider({ language: "typescript" }, new I18nHover()),
        languages.registerHoverProvider({ language: "typescriptreact" }, new I18nHover()),
        workspace.registerTextDocumentContentProvider("equicord-patchhelper", PatchHelper),
        workspace.registerTextDocumentContentProvider("equicord-companion", {
            provideTextDocumentContent(uri) {
                // FIXME: full uri shows up in title bar
                const newLocal = Buffer.from(uri.path.substring(1, uri.path.lastIndexOf("/")), "base64url");

                return newLocal.toString();
            },
        }),
        commands.registerCommand("equicord-companion.openPatchHelper", async (doc: TextDocument, patch: SourcePatch) => {
            if (!doc) {
                outputChannel.warn("Could not find source document");
                window.showErrorMessage("Could not find source document");
                return;
            }

            const helper = await PatchHelper.create(doc, patch);

            helper.openModuleWindow();
        }),
        commands.registerCommand("equicord-companion.runReporter", startReporter),
        commands.registerCommand("equicord-companion.diffModule", async (args) => {
            if (args) {
                try {
                    const r = await sendAndGetData<"diff">({
                        type: "diff",
                        data: {
                            extractType: "id",
                            idOrSearch: args,
                        },
                    });

                    handleDiffPayload(r);
                } catch (e) {
                    outputChannel.error(String(e));
                    window.showErrorMessage(String(e));
                }
                return;
            }

            // FIXME: refactor to generic quicpick class with these features
            const quickPick = window.createQuickPick();

            quickPick.placeholder = "module ID";
            quickPick.canSelectMany = false;

            const items: QuickPickItem[] = [
                {
                    label: "",
                    alwaysShow: false,
                },
                {
                    label: "",
                    kind: -1,
                },
                ...moduleCache.map((m) => ({ label: m })),
            ];

            quickPick.items = items;
            quickPick.onDidChangeValue(() => {
                if (!moduleCache.includes(quickPick.value)) {
                    items[0].label = quickPick.value;
                    items[0].alwaysShow = true;
                } else {
                    items[0].alwaysShow = false;
                }
                quickPick.items = items;
            });
            quickPick.show();
            quickPick.onDidAccept(async () => {
                const modId = quickPick.value;

                quickPick.dispose();
                if (!modId || isNaN(+modId))
                    return vscWindow.showErrorMessage("No Module ID provided");
                try {
                    const r = await sendAndGetData<"diff">({
                        type: "diff",
                        data: {
                            extractType: "id",
                            idOrSearch: +modId,
                        },
                    });

                    handleDiffPayload(r);
                } catch (error) {
                    outputChannel.error(String(error));
                    vscWindow.showErrorMessage(String(error));
                }
            });
        }),
        commands.registerCommand("equicord-companion.diffModuleSearch", async (args: string, findType: "string" | "regex") => {
            if (args) {
                try {
                    const r = await sendAndGetData<"diff">({
                        type: "diff",
                        data: {
                            extractType: "search",
                            findType,
                            idOrSearch: args,
                        },
                    });

                    handleDiffPayload(r);
                } catch (e) {
                    outputChannel.error(String(e));
                    window.showErrorMessage(String(e));
                }
                return;
            }

            const input = await window.showInputBox();

            if (!input) {
                outputChannel.warn("No Input Provided");
                return window.showErrorMessage("No Input Provided");
            }
            try {
                const r = await sendAndGetData<"diff">({
                    type: "diff",
                    data: {
                        extractType: "search",
                        findType: "string",
                        idOrSearch: input,
                    },
                });

                handleDiffPayload(r);
            } catch (e) {
                outputChannel.error(String(e));
                vscWindow.showErrorMessage(String(e));
            }
        }),
        commands.registerCommand("equicord-companion.extractFind", async (args: Discriminate<OutgoingMessage, "extract">) => {
            if (!args) {
                outputChannel.warn("No Data Provided");
                vscWindow.showErrorMessage("No Data Provided");
                return;
            }
            try {
                const r = await sendAndGetData<"extract">(args);

                handleExtractPayload(r);
            } catch (e) {
                outputChannel.error(String(e));
                vscWindow.showErrorMessage(String(e));
            }
        }),
        commands.registerCommand("equicord-companion.extract", async (args: number) => {
            if (args) {
                try {
                    const r = await sendAndGetData<"extract">({
                        type: "extract",
                        data: {
                            extractType: "id",
                            idOrSearch: args,
                            usePatched: null,
                        },
                    });

                    handleExtractPayload(r);
                } catch (e) {
                    outputChannel.error(String(e));
                    window.showErrorMessage(String(e));
                }
                return;
            }

            const quickPick = window.createQuickPick();

            quickPick.placeholder = "module ID";
            quickPick.canSelectMany = false;

            const items: QuickPickItem[] = [
                {
                    label: "",
                    alwaysShow: false,
                },
                {
                    label: "",
                    kind: -1,
                },
                ...moduleCache.map((m) => {
                    return { label: m };
                }),
            ];

            quickPick.items = items;
            quickPick.onDidChangeValue(() => {
                if (!moduleCache.includes(quickPick.value)) {
                    items[0].label = quickPick.value;
                    items[0].alwaysShow = true;
                } else {
                    items[0].alwaysShow = false;
                }
                quickPick.items = items;
            });
            quickPick.show();
            quickPick.onDidAccept(async () => {
                const modId = quickPick.value;

                quickPick.dispose();
                if (!modId || isNaN(+modId))
                    return vscWindow.showErrorMessage("No Module ID provided");
                try {
                    const r = await sendAndGetData<"extract">({
                        type: "extract",
                        data: {
                            extractType: "id",
                            idOrSearch: +modId,
                            usePatched: null,
                        },
                    });

                    handleExtractPayload(r);
                } catch (e) {
                    outputChannel.error(String(e));
                    vscWindow.showErrorMessage(String(e));
                }
            });
        }),
        commands.registerCommand("equicord-companion.extractSearch", async (args: string, findType: "string" | "regex") => {
            if (args) {
                try {
                    const r = await sendAndGetData<"extract">({
                        type: "extract",
                        data: {
                            extractType: "search",
                            findType,
                            idOrSearch: args,
                            usePatched: null,
                        },
                    });

                    handleExtractPayload(r);
                } catch (e) {
                    outputChannel.error(String(e));
                    window.showErrorMessage(String(e));
                }
                return;
            }

            const input = await window.showInputBox();

            if (!input)
                return window.showErrorMessage("No Input Provided");
            try {
                const r = await sendAndGetData<"extract">({
                    type: "extract",
                    data: {
                        extractType: "search",
                        findType: "string",
                        idOrSearch: input,
                        usePatched: null,
                    },
                });

                handleExtractPayload(r);
            } catch (e) {
                outputChannel.error(String(e));
                vscWindow.showErrorMessage(String(e));
            }
        }),
        commands.registerCommand("equicord-companion.disablePlugin", async (data: DisablePluginData) => {
            try {
                if (!data)
                    throw new Error("No args passed.");
                await sendAndGetData({
                    type: "disable",
                    data,
                });
            } catch (e) {
                outputChannel.error(String(e));
                vscWindow.showErrorMessage(String(e));
            }
        }),
        commands.registerCommand("equicord-companion.testPatch", async (patch: PatchData) => {
            try {
                await sendAndGetData({
                    type: "testPatch",
                    data: patch,
                });
                vscWindow.showInformationMessage("Patch OK!");
            } catch (e) {
                outputChannel.info(`Patch failed: ${e}`);
                vscWindow.showErrorMessage(`Patch failed: ${e}`);
            }
        }),

        commands.registerCommand("equicord-companion.testFind", async (find: FindData) => {
            try {
                await sendAndGetData({
                    type: "testFind",
                    data: find,
                });
                vscWindow.showInformationMessage("Find OK!");
            } catch (e) {
                outputChannel.info(`Find bad: ${e}`);
                vscWindow.showErrorMessage(`Find bad: ${e}`);
            }
        }),
    );
    if (window.activeTextEditor) {
        onOpenCallback(window.activeTextEditor.document);
    }
}

export function deactivate() {
    stopWebSocketServer();
}
export {
    outputChannel,
};
