import { Effect, Option } from "effect";

import { Config } from "../config/Config.ts";
import { installPackages } from "../lib/installPackages.ts";
import { VsCode } from "../platform/VsCode.ts";
import { findVenvPath } from "../python/findVenvPath.ts";
import { PythonEnvInvalidation } from "../python/PythonEnvInvalidation.ts";
import type { MarimoNotebookDocument } from "../schemas/MarimoNotebookDocument.ts";
import type { NotificationOf } from "../types.ts";
import type { NotebookController } from "./NotebookRuntime.ts";

export const handleMissingPackageAlert = Effect.fn("handleMissingPackageAlert")(
  function* (
    operation: NotificationOf<"missing-package-alert">,
    notebook: MarimoNotebookDocument,
    controller: NotebookController,
  ) {
    const code = yield* VsCode;
    const config = yield* Config;
    const envInvalidation = yield* PythonEnvInvalidation;

    if (operation.packages.length === 0) {
      // Nothing to do
      return;
    }

    const uvEnabled = yield* config.uv.enabled;
    if (!uvEnabled) {
      // User has uv disabled
      yield* Effect.logDebug("uv integration disabled. Skipping install.").pipe(
        Effect.annotateLogs({
          packages: operation.packages,
        }),
      );

      return;
    }

    let options: { script: MarimoNotebookDocument } | { venvPath: string };

    if (typeof controller.executable === "string") {
      // Only venv environments (with pyvenv.cfg) support uv package installation
      // Non-venv environments (pixi, conda, bazel, global) are skipped
      const venvPath = findVenvPath(controller.executable);

      if (Option.isNone(venvPath)) {
        yield* Effect.logDebug(
          "No venv found for environment. Skipping uv install.",
        ).pipe(
          Effect.annotateLogs({
            packages: operation.packages,
            executable: controller.executable,
          }),
        );
        return;
      }

      options = { venvPath: venvPath.value };
    } else {
      options = { script: notebook };
    }

    const choice = yield* code.window.showInformationMessage(
      operation.packages.length === 1
        ? `Missing package: ${operation.packages[0]}. Install with uv?`
        : `Missing packages: ${operation.packages.join(", ")}. Install with uv?`,
      {
        items: ["Install All", "Customize..."],
      },
    );

    if (Option.isNone(choice)) {
      // dismissed
      return;
    }

    if (choice.value === "Install All") {
      yield* Effect.logInfo("Install packages").pipe(
        Effect.annotateLogs("packages", operation.packages),
      );

      if ("venvPath" in options) {
        yield* installPackages(operation.packages, options);
      } else {
        yield* installPackages(operation.packages, options);
      }

      yield* envInvalidation.invalidate("package-install");
      return;
    }

    if (choice.value === "Customize...") {
      const response = yield* code.window.showInputBox({
        prompt: "Add packages",
        value: operation.packages.join(" "),
        placeHolder: "package1 package2 package3",
      });

      if (Option.isNone(response)) {
        return;
      }

      const newPackages = response.value.split(" ");
      yield* Effect.logInfo("Install packages").pipe(
        Effect.annotateLogs("packages", newPackages),
      );

      if ("venvPath" in options) {
        yield* installPackages(newPackages, options);
      } else {
        yield* installPackages(newPackages, options);
      }

      yield* envInvalidation.invalidate("package-install");
    }
  },
);
