/**
 * rSheets module — collaborative spreadsheets powered by dSheet.
 *
 * Embeds @fileverse-dev/dsheet as an external app within the rSpace shell.
 */

import { Hono } from "hono";
import * as Automerge from "@automerge/automerge";
import { renderExternalAppShell } from "../../server/shell";
import { getModuleInfoList } from "../../shared/module";
import type { RSpaceModule } from "../../shared/module";
import { verifyToken, extractToken } from "../../server/auth";
import type { SyncServer } from '../../server/local-first/sync-server';
import { sheetSchema, sheetDocId } from './schemas';
import type { SheetDoc } from './schemas';

let _syncServer: SyncServer | null = null;

const routes = new Hono();

// ── Local-first helpers ──

function ensureSheetDoc(space: string, sheetId: string): SheetDoc {
	const docId = sheetDocId(space, sheetId);
	let doc = _syncServer!.getDoc<SheetDoc>(docId);
	if (!doc) {
		doc = Automerge.change(Automerge.init<SheetDoc>(), 'init sheet', (d) => {
			const init = sheetSchema.init();
			Object.assign(d, init);
			d.meta.spaceSlug = space;
			d.sheet.id = sheetId;
		});
		_syncServer!.setDoc(docId, doc);
	}
	return doc;
}

// ── CRUD: Sheets ──

routes.get("/api/sheets", (c) => {
	if (!_syncServer) return c.json({ sheets: [] });
	const space = c.req.param("space") || "demo";
	const prefix = `${space}:sheet:sheets:`;
	const sheets: any[] = [];
	for (const docId of _syncServer.listDocs()) {
		if (!docId.startsWith(prefix)) continue;
		const doc = _syncServer.getDoc<SheetDoc>(docId);
		if (!doc?.sheet) continue;
		sheets.push({ id: doc.sheet.id, name: doc.sheet.name, description: doc.sheet.description, cellCount: Object.keys(doc.cells || {}).length, createdAt: doc.sheet.createdAt, updatedAt: doc.sheet.updatedAt });
	}
	return c.json({ sheets });
});

routes.post("/api/sheets", async (c) => {
	const token = extractToken(c.req.raw.headers);
	if (!token) return c.json({ error: "Authentication required" }, 401);
	try { await verifyToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
	if (!_syncServer) return c.json({ error: "Not initialized" }, 503);
	const space = c.req.param("space") || "demo";
	const { name = "Untitled Sheet", description = "" } = await c.req.json();
	const id = crypto.randomUUID();
	const docId = sheetDocId(space, id);
	const doc = Automerge.change(Automerge.init<SheetDoc>(), 'create sheet', (d) => {
		const init = sheetSchema.init();
		Object.assign(d, init);
		d.meta.spaceSlug = space;
		d.sheet.id = id;
		d.sheet.name = name;
		d.sheet.description = description;
	});
	_syncServer.setDoc(docId, doc);
	const created = _syncServer.getDoc<SheetDoc>(docId)!;
	return c.json({ id: created.sheet.id, name: created.sheet.name }, 201);
});

routes.get("/api/sheets/:id", (c) => {
	if (!_syncServer) return c.json({ error: "Not initialized" }, 503);
	const space = c.req.param("space") || "demo";
	const id = c.req.param("id");
	const doc = _syncServer.getDoc<SheetDoc>(sheetDocId(space, id));
	if (!doc) return c.json({ error: "Not found" }, 404);
	return c.json({ sheet: doc.sheet, cells: doc.cells, columns: doc.columns, rows: doc.rows });
});

routes.put("/api/sheets/:id/cells", async (c) => {
	const token = extractToken(c.req.raw.headers);
	if (!token) return c.json({ error: "Authentication required" }, 401);
	try { await verifyToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
	if (!_syncServer) return c.json({ error: "Not initialized" }, 503);
	const space = c.req.param("space") || "demo";
	const id = c.req.param("id");
	const docId = sheetDocId(space, id);
	const doc = _syncServer.getDoc<SheetDoc>(docId);
	if (!doc) return c.json({ error: "Not found" }, 404);
	const { cells } = await c.req.json();
	if (!cells || typeof cells !== 'object') return c.json({ error: "cells object required" }, 400);
	_syncServer.changeDoc<SheetDoc>(docId, 'update cells', (d) => {
		for (const [key, val] of Object.entries(cells as Record<string, any>)) {
			d.cells[key] = { value: val.value || '', formula: val.formula || null, format: val.format || null, updatedAt: Date.now() };
		}
	});
	return c.json({ ok: true });
});

// ── Routes ──

routes.get("/", (c) => {
	const space = c.req.param("space") || "demo";
	return c.html(
		renderExternalAppShell({
			title: `Spreadsheets — rSheets | rSpace`,
			moduleId: "rsheets",
			spaceSlug: space,
			modules: getModuleInfoList(),
			appUrl: `/rsheets/app`,
			appName: "dSheet",
			theme: "dark",
		})
	);
});

routes.get("/app", (c) => {
	const space = c.req.param("space") || "demo";
	const collabWs = process.env.COLLAB_WS_URL || "wss://collab-ws.rnotes.online";

	return c.html(`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>rSheets — ${space}</title>
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  html, body, #sheet { width: 100%; height: 100%; background: #0f172a; color: #e2e8f0; }
  #loading { display: flex; align-items: center; justify-content: center; height: 100%; font-family: system-ui; color: #94a3b8; }
  #loading.hidden { display: none; }
</style>
</head>
<body>
<div id="loading">Loading spreadsheet...</div>
<div id="sheet"></div>
<script type="module">
  // dSheet from CDN — renders a collaborative spreadsheet
  // Uses Y.js for real-time sync via the shared WebSocket server
  import * as Y from 'https://cdn.jsdelivr.net/npm/yjs@13/+esm';
  import { WebsocketProvider } from 'https://cdn.jsdelivr.net/npm/y-websocket@2/+esm';

  const space = ${JSON.stringify(space)};
  const wsUrl = ${JSON.stringify(collabWs)};

  const ydoc = new Y.Doc();
  const provider = new WebsocketProvider(wsUrl, 'sheet-' + space, ydoc);
  const ydata = ydoc.getArray('sheet-data');

  // Minimal spreadsheet grid (dSheet CDN integration point)
  // When @fileverse-dev/dsheet publishes an ESM CDN build, replace this
  // with: import { DSheet } from 'https://cdn.jsdelivr.net/npm/@fileverse-dev/dsheet/+esm'
  const container = document.getElementById('sheet');
  const loading = document.getElementById('loading');

  provider.on('sync', (synced) => {
    if (!synced) return;
    loading.classList.add('hidden');
    renderGrid();
  });

  const ROWS = 50;
  const COLS = 26;

  function renderGrid() {
    const table = document.createElement('table');
    table.style.cssText = 'width:100%;border-collapse:collapse;font-family:monospace;font-size:13px;';

    // Header row
    const thead = table.createTHead();
    const hr = thead.insertRow();
    hr.insertCell().textContent = '';
    for (let c = 0; c < COLS; c++) {
      const th = document.createElement('th');
      th.textContent = String.fromCharCode(65 + c);
      th.style.cssText = 'padding:4px 8px;background:#1e293b;color:#94a3b8;border:1px solid #334155;min-width:80px;';
      hr.appendChild(th);
    }

    const tbody = table.createTBody();
    for (let r = 0; r < ROWS; r++) {
      const tr = tbody.insertRow();
      const rowLabel = tr.insertCell();
      rowLabel.textContent = String(r + 1);
      rowLabel.style.cssText = 'padding:4px 8px;background:#1e293b;color:#94a3b8;border:1px solid #334155;text-align:center;';

      for (let c = 0; c < COLS; c++) {
        const td = tr.insertCell();
        td.contentEditable = 'true';
        td.style.cssText = 'padding:4px 8px;border:1px solid #334155;background:#0f172a;color:#e2e8f0;min-width:80px;outline:none;';
        td.dataset.row = String(r);
        td.dataset.col = String(c);

        // Load existing data from Y.js
        const key = r + ':' + c;
        const existing = getCellValue(key);
        if (existing) td.textContent = existing;

        td.addEventListener('blur', () => {
          setCellValue(key, td.textContent || '');
        });

        td.addEventListener('focus', () => {
          td.style.outline = '2px solid #3b82f6';
        });
        td.addEventListener('blur', () => {
          td.style.outline = 'none';
        });
      }
    }

    container.innerHTML = '';
    container.style.overflow = 'auto';
    container.appendChild(table);

    // Observe Y.js changes for live sync
    ydata.observe(() => {
      for (const cell of container.querySelectorAll('td[data-row]')) {
        const key = cell.dataset.row + ':' + cell.dataset.col;
        const val = getCellValue(key);
        if (cell.textContent !== val && document.activeElement !== cell) {
          cell.textContent = val;
        }
      }
    });
  }

  function getCellValue(key) {
    const map = ydoc.getMap('sheet-cells');
    return map.get(key) || '';
  }

  function setCellValue(key, value) {
    const map = ydoc.getMap('sheet-cells');
    if (value) {
      map.set(key, value);
    } else {
      map.delete(key);
    }
  }
</script>
</body>
</html>`);
});

// ── MI Integration ──

export function getRecentSheetsForMI(space: string, limit = 5): { id: string; name: string; cellCount: number; updatedAt: number }[] {
	if (!_syncServer) return [];
	const sheets: { id: string; name: string; cellCount: number; updatedAt: number }[] = [];
	const prefix = `${space}:sheet:sheets:`;
	for (const docId of _syncServer.listDocs()) {
		if (!docId.startsWith(prefix)) continue;
		const doc = _syncServer.getDoc<SheetDoc>(docId);
		if (!doc?.sheet) continue;
		sheets.push({ id: doc.sheet.id, name: doc.sheet.name, cellCount: Object.keys(doc.cells || {}).length, updatedAt: doc.sheet.updatedAt });
	}
	return sheets.sort((a, b) => b.updatedAt - a.updatedAt).slice(0, limit);
}

// ── Module definition ──

export const sheetsModule: RSpaceModule = {
	id: "rsheets",
	name: "rSheets",
	icon: "\u{1F4CA}",
	description: "Collaborative spreadsheets",
	scoping: { defaultScope: "space", userConfigurable: false },
	docSchemas: [{ pattern: '{space}:sheet:sheets:{sheetId}', description: 'One doc per spreadsheet', init: sheetSchema.init }],
	routes,
	externalApp: {
		url: "/rsheets/app",
		name: "dSheet",
	},
	async onInit(ctx) { _syncServer = ctx.syncServer; },
	outputPaths: [
		{
			path: "",
			name: "Spreadsheets",
			icon: "\u{1F4CA}",
			description: "Collaborative spreadsheet workspace",
		},
	],
	onboardingActions: [
		{
			label: "Open Spreadsheet",
			icon: "\u{1F4CA}",
			description: "Create or edit a collaborative spreadsheet",
			type: "navigate" as any,
			href: "/rsheets",
		},
	],
};
