/**
 * Copyright (c) OpenLens Authors. All rights reserved.
 * Licensed under MIT License. See LICENSE in root directory for more information.
 */
import {
  Disposer,
  disposer,
  ExtendableDisposer,
} from "../../../../common/utils";
import { Notifications } from "../../notifications";
import { Button } from "../../button";
import type { ExtensionLoader } from "../../../../extensions/extension-loader";
import type { LensExtensionId } from "../../../../extensions/lens-extension";
import React from "react";
import fse from "fs-extra";
import { shell } from "electron";
import type { InstallRequestValidated } from "./create-temp-files-and-validate/create-temp-files-and-validate";
import type { InstallRequest } from "./install-request";
import {
  ExtensionInstallationState,
  ExtensionInstallationStateStore,
} from "../../../../extensions/extension-installation-state-store/extension-installation-state-store";

interface Dependencies {
  extensionLoader: ExtensionLoader;
  uninstallExtension: (id: LensExtensionId) => Promise<boolean>;

  unpackExtension: (
    request: InstallRequestValidated,
    disposeDownloading: Disposer,
  ) => Promise<void>;

  createTempFilesAndValidate: (
    installRequest: InstallRequest,
  ) => Promise<InstallRequestValidated | null>;

  getExtensionDestFolder: (name: string) => string

  extensionInstallationStateStore: ExtensionInstallationStateStore
}

export const attemptInstall =
  ({
    extensionLoader,
    uninstallExtension,
    unpackExtension,
    createTempFilesAndValidate,
    getExtensionDestFolder,
    extensionInstallationStateStore,
  }: Dependencies) =>
    async (request: InstallRequest, d?: ExtendableDisposer): Promise<void> => {
      const dispose = disposer(
        extensionInstallationStateStore.startPreInstall(),
        d,
      );

      const validatedRequest = await createTempFilesAndValidate(request);

      if (!validatedRequest) {
        return dispose();
      }

      const { name, version, description } = validatedRequest.manifest;
      const curState = extensionInstallationStateStore.getInstallationState(
        validatedRequest.id,
      );

      if (curState !== ExtensionInstallationState.IDLE) {
        dispose();

        return void Notifications.error(
          <div className="flex column gaps">
            <b>Extension Install Collision:</b>
            <p>
            The <em>{name}</em> extension is currently {curState.toLowerCase()}.
            </p>
            <p>Will not proceed with this current install request.</p>
          </div>,
        );
      }

      const extensionFolder = getExtensionDestFolder(name);
      const folderExists = await fse.pathExists(extensionFolder);

      if (!folderExists) {
      // install extension if not yet exists
        await unpackExtension(validatedRequest, dispose);
      } else {
        const {
          manifest: { version: oldVersion },
        } = extensionLoader.getExtension(validatedRequest.id);

        // otherwise confirmation required (re-install / update)
        const removeNotification = Notifications.info(
          <div className="InstallingExtensionNotification flex gaps align-center">
            <div className="flex column gaps">
              <p>
              Install extension{" "}
                <b>
                  {name}@{version}
                </b>
              ?
              </p>
              <p>
              Description: <em>{description}</em>
              </p>
              <div
                className="remove-folder-warning"
                onClick={() => shell.openPath(extensionFolder)}
              >
                <b>Warning:</b> {name}@{oldVersion} will be removed before
              installation.
              </div>
            </div>
            <Button
              autoFocus
              label="Install"
              onClick={async () => {
                removeNotification();

                if (await uninstallExtension(validatedRequest.id)) {
                  await unpackExtension(validatedRequest, dispose);
                } else {
                  dispose();
                }
              }}
            />
          </div>,
          {
            onClose: dispose,
          },
        );
      }
    };
