// @ts-types="@types/react-dom/server";
import * as ReactDOM from "react-dom/server";
import * as anywidget from "@anywidget/deno";
import * as base64 from "@std/encoding/base64";
import * as linkedom from "linkedom";

import type * as pl from "nodejs-polars";
import type * as React from "react";

/**
 * A parsed HTML document for rendering Observable Plot's with Deno.
 */
export const document: globalThis.Document =
  linkedom.parseHTML("<html></html>").document;

/**
 * Renders a React node as an HTML string for display in Jupyter.
 *
 * Note: This only supports server-side rendering (SSR). Hooks and stateful
 * components cannot be used.
 *
 * @example Usage
 * ```tsx
 * import * as React from "npm:react";
 * import { render, document } from "jsr:@manzt/jupyter";
 *
 * render(<h1>Hello, Jupyter!</h1>);
 * ```
 *
 * @param reactNode - The React node to render.
 * @returns A Jupyter-compatible display object.
 */
export function render(reactNode: React.ReactNode): Deno.jupyter.Displayable {
  return {
    [Deno.jupyter.$display]() {
      return {
        "text/html": ReactDOM.renderToString(reactNode),
      };
    },
  };
}

// Widgets

// Types for frontend libs included below
declare const $base64: typeof import("@std/encoding/base64");
declare const $agGrid: typeof import("ag-grid-community");
declare const $flech: typeof import("@uwdata/flechette");
declare const $mosaic: typeof import("@uwdata/mosaic-core");
declare const $quak: typeof import("@manzt/quak");

/**
 * Display a Polars DataFrame as an interactive agGrid.
 *
 * @example Usage
 * ```ts
 * import * as pl from "npm:nodejs-polars";
 * import { agGrid } from "jsr:@manzt/jupyter-helper";
 *
 * let response = await fetch("https://raw.githubusercontent.com/uwdata/mosaic/refs/heads/main/data/penguins.csv");
 * let df = pl.readCSV(await response.text());
 * agGrid(df);
 * ```
 *
 * @param df The DataFrame to display
 * @returns A "live" anywidget instance
 */
export function agGrid(
  df: pl.DataFrame,
): anywidget.Model<unknown> {
  return anywidget.widget({
    state: {
      ipc: base64.encodeBase64(df.writeIPC()),
      _css: "https://esm.sh/ag-grid-community@33.0.4/styles/ag-grid.css",
    },
    imports: `
import * as $agGrid from "https://esm.sh/ag-grid-community@33.0.4";
import * as $flech from "https://esm.sh/@uwdata/flechette@1.1.2";
import * as $base64 from "https://esm.sh/jsr/@std/encoding@1.0.7/base64";
    `,
    render: ({ model, el }) => {
      $agGrid.ModuleRegistry.registerModules([$agGrid.AllCommunityModule]);
      el.style.height = "400px";
      let bytes = $base64.decodeBase64(model.get("ipc"));
      let table = $flech.tableFromIPC(bytes);
      $agGrid.createGrid(el, {
        columnDefs: table.names.map((field) => ({ field })),
        rowData: table.toArray(),
        pagination: true,
      });
    },
  });
}

/**
 * Display a Polars DataFrame as an interactive quak data table.
 *
 * @example Usage
 * ```ts
 * import * as pl from "npm:nodejs-polars";
 * import { quak } from "jsr:@manzt/jupyter-helper";
 *
 * let response = await fetch("https://raw.githubusercontent.com/uwdata/mosaic/refs/heads/main/data/penguins.csv");
 * let df = pl.readCSV(await response.text());
 * quak(df)
 * ```
 *
 * @param df The DataFrame to display
 * @returns A "live" anywidget instance
 */
export function quak(
  df: pl.DataFrame,
): anywidget.Model<unknown> {
  return anywidget.widget({
    state: { parquet: base64.encodeBase64(df.writeParquet()) },
    imports: `
import * as $mosaic from "https://esm.sh/@uwdata/mosaic-core@~0.11?bundle";
import * as $quak from "https://esm.sh/jsr/@manzt/quak@0.0.2";
import * as $base64 from "https://esm.sh/jsr/@std/encoding@1.0.7/base64";
    `,
    render: async ({ model, el }) => {
      let connector = $mosaic.wasmConnector();
      let db = await connector.getDuckDB();
      let coordinator = new $mosaic.Coordinator();
      coordinator.databaseConnector(connector);

      let bytes = $base64.decodeBase64(model.get("parquet"));
      await db.registerFileBuffer("df.parquet", bytes);
      await coordinator.exec([
        `CREATE OR REPLACE TABLE "df" AS SELECT * FROM "df.parquet"`,
      ]);

      let dt = await $quak.datatable("df", { coordinator, height: 400 });
      el.appendChild(dt.node());

      let div = document.createElement("div");
      div.style.height = "435px";
      div.appendChild(dt.node());

      el.appendChild(div);
    },
  });
}
