/**
 * Notion OAuth2 flow.
 *
 * GET  /authorize?space=X  → redirect to Notion
 * GET  /callback           → exchange code, store token, redirect back
 * POST /disconnect?space=X → revoke token
 */

import { Hono } from 'hono';
import * as Automerge from '@automerge/automerge';
import { connectionsDocId } from '../../modules/rdocs/schemas';
import type { ConnectionsDoc } from '../../modules/rdocs/schemas';
import type { SyncServer } from '../local-first/sync-server';

const notionOAuthRoutes = new Hono();

const NOTION_CLIENT_ID = process.env.NOTION_CLIENT_ID || '';
const NOTION_CLIENT_SECRET = process.env.NOTION_CLIENT_SECRET || '';
const NOTION_REDIRECT_URI = process.env.NOTION_REDIRECT_URI || '';

// We'll need a reference to the sync server — set externally
let _syncServer: SyncServer | null = null;

export function setNotionOAuthSyncServer(ss: SyncServer) {
	_syncServer = ss;
}

function ensureConnectionsDoc(space: string): ConnectionsDoc {
	if (!_syncServer) throw new Error('SyncServer not initialized');
	const docId = connectionsDocId(space);
	let doc = _syncServer.getDoc<ConnectionsDoc>(docId);
	if (!doc) {
		doc = Automerge.change(Automerge.init<ConnectionsDoc>(), 'init connections', (d) => {
			d.meta = {
				module: 'notes',
				collection: 'connections',
				version: 1,
				spaceSlug: space,
				createdAt: Date.now(),
			};
		});
		_syncServer.setDoc(docId, doc);
	}
	return doc;
}

// GET /authorize — redirect to Notion OAuth
notionOAuthRoutes.get('/authorize', (c) => {
	const space = c.req.query('space');
	if (!space) return c.json({ error: 'space query param required' }, 400);
	if (!NOTION_CLIENT_ID) return c.json({ error: 'Notion OAuth not configured' }, 500);

	const state = Buffer.from(JSON.stringify({ space })).toString('base64url');
	const url = `https://api.notion.com/v1/oauth/authorize?client_id=${NOTION_CLIENT_ID}&response_type=code&owner=user&redirect_uri=${encodeURIComponent(NOTION_REDIRECT_URI)}&state=${state}`;

	return c.redirect(url);
});

// GET /callback — exchange code for token
notionOAuthRoutes.get('/callback', async (c) => {
	const code = c.req.query('code');
	const stateParam = c.req.query('state');

	if (!code || !stateParam) return c.json({ error: 'Missing code or state' }, 400);

	let state: { space: string };
	try {
		state = JSON.parse(Buffer.from(stateParam, 'base64url').toString());
	} catch {
		return c.json({ error: 'Invalid state parameter' }, 400);
	}

	// Exchange code for access token
	const tokenRes = await fetch('https://api.notion.com/v1/oauth/token', {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json',
			'Authorization': `Basic ${Buffer.from(`${NOTION_CLIENT_ID}:${NOTION_CLIENT_SECRET}`).toString('base64')}`,
		},
		body: JSON.stringify({
			grant_type: 'authorization_code',
			code,
			redirect_uri: NOTION_REDIRECT_URI,
		}),
	});

	if (!tokenRes.ok) {
		const err = await tokenRes.text();
		return c.json({ error: `Token exchange failed: ${err}` }, 502);
	}

	const tokenData = await tokenRes.json() as any;

	// Store token in Automerge connections doc
	ensureConnectionsDoc(state.space);
	const docId = connectionsDocId(state.space);

	_syncServer!.changeDoc<ConnectionsDoc>(docId, 'Connect Notion', (d) => {
		d.notion = {
			accessToken: tokenData.access_token,
			workspaceId: tokenData.workspace_id || '',
			workspaceName: tokenData.workspace_name || 'Notion Workspace',
			connectedAt: Date.now(),
		};
	});

	// Redirect back to rDocs
	const redirectUrl = c.get("isSubdomain") ? `/rdocs?connected=notion` : `/${state.space}/rdocs?connected=notion`;
	return c.redirect(redirectUrl);
});

// POST /disconnect — revoke and remove token
notionOAuthRoutes.post('/disconnect', async (c) => {
	const space = c.req.query('space');
	if (!space) return c.json({ error: 'space query param required' }, 400);

	const docId = connectionsDocId(space);
	const doc = _syncServer?.getDoc<ConnectionsDoc>(docId);

	if (doc?.notion) {
		_syncServer!.changeDoc<ConnectionsDoc>(docId, 'Disconnect Notion', (d) => {
			delete d.notion;
		});
	}

	return c.json({ ok: true });
});

export { notionOAuthRoutes };
