/// <reference no-default-lib="true" />
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />
/// <reference lib="dom.asynciterable" />
/// <reference lib="deno.ns" />

import { Manifest, ServerContext, StartOptions } from "$fresh/server.ts";
import manifest from "./fresh.gen.ts";

import twindPlugin from "$fresh/plugins/twind.ts";
import twindConfig from "./twind.config.ts";
import "https://deno.land/std@0.170.0/dotenv/load.ts";

import { Command } from "https://deno.land/x/cliffy@v0.25.6/mod.ts";
import { prepareSecretKey } from "./util/secret.ts";
import { serve } from "http/server.ts";

import { user_command } from "./user.ts";
import { key_out_cmd } from "./keyout.ts";
import { prepareDocs } from "./src/store/doc.ts";
import { connectDB } from "./src/user/db.ts";
import * as users from "./src/user/user.ts";

async function startServer(manifest: Manifest, options: StartOptions = {}) {
  const ctx = await ServerContext.fromManifest(manifest, options);

  options.port ??= 8000;
  if (options.experimentalDenoServe === true) {
    // @ts-ignore as `Deno.serve` is still unstable.
    await Deno.serve(ctx.handler() as Deno.ServeHandler, options);
  } else {
    await serve(ctx.handler(), options);
  }
}

async function start(
  { port = 8000, hostname = "localhost" }: {
    port?: number;
    hostname?: string;
  } = {},
) {
  await startServer(manifest, {
    plugins: [twindPlugin(twindConfig)],
    port: port,
    hostname: hostname,
  });
}

if (import.meta.main) {
  const cmd = new Command();
  cmd.name("fs-server")
    .description(
      "A simple file server that supports search, upload, and download.",
    )
    .version("0.0.1")
    .globalOption("-d, --debug", "Enable debug mode.")
    .command("start", "Start the server.")
    .option("-p, --port <port:number>", "The port to listen on.", {
      default: 8000,
    })
    .option("--auth", "Enable authentication.")
    .option("--db-path <path:string>", "The path to the database file.", {
      default: ":memory:",
    })
    .option(
      "--id-password <idpassword:string>",
      "The password to use. (Not recommended). id:password format.",
    )
    .arguments("[hostname:string]")
    .action(async ({ debug, port, auth, dbPath, idPassword }, hostname) => {
      hostname ??= "localhost";
      if (auth) {
        Deno.env.set("AUTH_REQUIRED", "true");
      }
      if (dbPath) {
        Deno.env.set("DB_PATH", dbPath);
      }
      if (idPassword) {
        Deno.env.set("AUTH_REQUIRED", "true");
        const db = await connectDB();
        const [username, password] = idPassword.split(":");
        const new_user = await users.createUser(username, password);
        await users.addUser(db, new_user);
      }
      if (debug) {
        console.log("Debug mode enabled.");
      }
      await prepareSecretKey();
      await prepareDocs();
      await start({
        port: port,
        hostname: hostname,
      });
    })
    .command("user", user_command)
    .command("keyout", key_out_cmd);

  await cmd.parse(Deno.args);
} else {
  await prepareDocs();
  await start();
}
