import { resolve } from "@std/path";
import assets from "../dist/plug_asset_bundle.json" with { type: "json" };
import { determineDatabaseBackend } from "../server/db_backend.ts";
import type { KvPrimitives } from "$lib/data/kv_primitives.ts";
import { DiskSpacePrimitives } from "$common/spaces/disk_space_primitives.ts";

import { ServerSystem } from "../server/server_system.ts";
import { AssetBundlePlugSpacePrimitives } from "$common/spaces/asset_bundle_space_primitives.ts";
import { LocalShell } from "../server/shell_backend.ts";
import { Hono } from "hono/mod.ts";
import { DataStore } from "$lib/data/datastore.ts";
import { DataStoreMQ } from "$lib/data/mq.datastore.ts";
import { EventHook } from "../common/hooks/event.ts";
import { sleep } from "$lib/async.ts";
import { AssetBundle } from "$lib/asset_bundle/bundle.ts";
import {
  type ConfigContainer,
  defaultConfig,
} from "@silverbulletmd/silverbullet/type/config";

export async function plugRunCommand(
  {
    hostname,
    port,
  }: {
    hostname?: string;
    port?: number;
  },
  spacePath: string,
  functionName: string | undefined,
  ...args: string[]
) {
  spacePath = resolve(spacePath);
  console.log("Space path", spacePath);
  console.log("Function to run:", functionName, "with arguments", args);

  const kvPrimitives = await determineDatabaseBackend(spacePath);

  if (!kvPrimitives) {
    console.error("Cannot run plugs in databaseless mode.");
    return;
  }

  try {
    const result = await runPlug(
      spacePath,
      functionName,
      args,
      new AssetBundle(assets),
      kvPrimitives,
      port,
      hostname,
    );
    if (result) {
      console.log("Output", result);
    }
    kvPrimitives.close();
    Deno.exit(0);
  } catch (e: any) {
    console.error(e.message);
    Deno.exit(1);
  }
}

export async function runPlug(
  spacePath: string,
  functionName: string | undefined,
  args: any[] = [],
  builtinAssetBundle: AssetBundle,
  kvPrimitives: KvPrimitives,
  httpServerPort?: number,
  httpHostname?: string,
) {
  const serverController = new AbortController();
  const app = new Hono();

  const ds = new DataStore(kvPrimitives);
  const mq = new DataStoreMQ(ds);
  const eventHook = new EventHook();
  // TODO: Actually load config from the space
  const configContainer: ConfigContainer = {
    config: defaultConfig,
    loadConfig: () => Promise.resolve(),
  };

  const serverSystem = new ServerSystem(
    new AssetBundlePlugSpacePrimitives(
      new DiskSpacePrimitives(spacePath),
      builtinAssetBundle,
    ),
    kvPrimitives,
    new LocalShell(spacePath),
    mq,
    ds,
    eventHook,
    false,
    true,
    configContainer,
  );
  await serverSystem.init(true);
  if (httpHostname && httpServerPort) {
    Deno.serve({
      hostname: httpHostname,
      port: httpServerPort,
      signal: serverController.signal,
    }, app.fetch);
  }

  if (functionName) {
    const result = await serverSystem.system.invokeFunction(functionName, args);
    await serverSystem.close();
    serverController.abort();
    return result;
  } else {
    console.log("Running in server mode, use Ctrl-c to stop");
    while (true) {
      await sleep(1000);
    }
  }
}
