import {
  findNodeOfType,
  removeParentPointers,
  renderToText,
} from "$sb/lib/tree.ts";
import { getText } from "$sb/silverbullet-syscall/editor.ts";
import { parseMarkdown } from "$sb/silverbullet-syscall/markdown.ts";
import {
  extractFrontmatter,
  prepareFrontmatterDispatch,
} from "$sb/lib/frontmatter.ts";
import * as YAML from "yaml";
import {
  clientStore,
  collab,
  editor,
  markdown,
} from "$sb/silverbullet-syscall/mod.ts";

import { nanoid } from "https://esm.sh/nanoid@4.0.0";
import {
  FileData,
  FileEncoding,
} from "../../common/spaces/space_primitives.ts";
import { FileMeta } from "../../common/types.ts";
import { base64EncodedDataUrl } from "../../plugos/asset_bundle/base64.ts";

const defaultServer = "wss://collab.silverbullet.md";

async function ensureUsername(): Promise<string> {
  let username = await clientStore.get("collabUsername");
  if (!username) {
    username = await editor.prompt(
      "Please enter a publicly visible user name (or cancel for 'anonymous'):",
    );
    if (!username) {
      return "anonymous";
    } else {
      await clientStore.set("collabUsername", username);
    }
  }
  return username;
}

export async function joinCommand() {
  let collabUri = await editor.prompt(
    "Collab share URI:",
  );
  if (!collabUri) {
    return;
  }
  if (!collabUri.startsWith("collab:")) {
    collabUri = "collab:" + collabUri;
  }
  await editor.navigate(collabUri);
}

export async function shareCommand() {
  const serverUrl = await editor.prompt(
    "Please enter the URL of the collab server to use:",
    defaultServer,
  );
  if (!serverUrl) {
    return;
  }
  const roomId = nanoid().replaceAll("_", "-");
  await editor.save();
  const text = await editor.getText();
  const tree = await markdown.parseMarkdown(text);
  let { $share } = extractFrontmatter(tree);
  if (!$share) {
    $share = [];
  }
  if (!Array.isArray($share)) {
    $share = [$share];
  }

  removeParentPointers(tree);
  const dispatchData = prepareFrontmatterDispatch(tree, {
    $share: [...$share, `collab:${serverUrl}/${roomId}`],
  });

  await editor.dispatch(dispatchData);

  collab.start(
    serverUrl,
    roomId,
    await ensureUsername(),
  );
}

export async function detectPage() {
  const tree = await parseMarkdown(await getText());
  const frontMatter = findNodeOfType(tree, "FrontMatter");
  if (frontMatter) {
    const yamlText = renderToText(frontMatter.children![1].children![0]);
    try {
      let { $share } = YAML.parse(yamlText) as any;
      if (!$share) {
        return;
      }
      if (!Array.isArray($share)) {
        $share = [$share];
      }
      for (const uri of $share) {
        if (uri.startsWith("collab:")) {
          console.log("Going to enable collab");
          const uriPieces = uri.substring("collab:".length).split("/");
          await collab.start(
            // All parts except the last one
            uriPieces.slice(0, uriPieces.length - 1).join("/"),
            // because the last one is the room ID
            uriPieces[uriPieces.length - 1],
            await ensureUsername(),
          );
        }
      }
    } catch (e) {
      console.error("Error parsing YAML", e);
    }
  }
}

export function shareNoop() {
  return true;
}

export async function readFileCollab(
  name: string,
  encoding: FileEncoding,
): Promise<{ data: FileData; meta: FileMeta }> {
  if (!name.endsWith(".md")) {
    throw new Error("File not found");
  }
  const collabUri = name.substring(0, name.length - ".md".length);
  const text = `---\n$share: ${collabUri}\n---\n`;

  return {
    // encoding === "arraybuffer" is not an option, so either it's "string" or "dataurl"
    data: encoding === "string" ? text : base64EncodedDataUrl(
      "text/markdown",
      new TextEncoder().encode(text),
    ),
    meta: {
      name,
      contentType: "text/markdown",
      size: text.length,
      lastModified: 0,
      perm: "rw",
    },
  };
}

export function getFileMetaCollab(name: string): FileMeta {
  return {
    name,
    contentType: "text/markdown",
    size: -1,
    lastModified: 0,
    perm: "rw",
  };
}

export function writeFileCollab(name: string): FileMeta {
  return {
    name,
    contentType: "text/markdown",
    size: -1,
    lastModified: 0,
    perm: "rw",
  };
}