/**
 * ClickUp OAuth2 flow.
 *
 * GET  /authorize?space=X  → redirect to ClickUp
 * 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 { clickupConnectionDocId } from '../../modules/rtasks/schemas';
import type { ClickUpConnectionDoc } from '../../modules/rtasks/schemas';
import type { SyncServer } from '../local-first/sync-server';

const clickupOAuthRoutes = new Hono();

const CLICKUP_CLIENT_ID = process.env.CLICKUP_CLIENT_ID || '';
const CLICKUP_CLIENT_SECRET = process.env.CLICKUP_CLIENT_SECRET || '';
const CLICKUP_REDIRECT_URI = process.env.CLICKUP_REDIRECT_URI || '';

let _syncServer: SyncServer | null = null;

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

function ensureConnectionDoc(space: string): ClickUpConnectionDoc {
	if (!_syncServer) throw new Error('SyncServer not initialized');
	const docId = clickupConnectionDocId(space);
	let doc = _syncServer.getDoc<ClickUpConnectionDoc>(docId);
	if (!doc) {
		doc = Automerge.change(Automerge.init<ClickUpConnectionDoc>(), 'init clickup connection', (d) => {
			d.meta = {
				module: 'tasks',
				collection: 'clickup-connection',
				version: 1,
				spaceSlug: space,
				createdAt: Date.now(),
			};
		});
		_syncServer.setDoc(docId, doc);
	}
	return doc;
}

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

	const state = Buffer.from(JSON.stringify({ space })).toString('base64url');
	const url = `https://app.clickup.com/api?client_id=${CLICKUP_CLIENT_ID}&redirect_uri=${encodeURIComponent(CLICKUP_REDIRECT_URI)}&state=${state}`;

	return c.redirect(url);
});

// GET /callback — exchange code for token
clickupOAuthRoutes.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.clickup.com/api/v2/oauth/token', {
		method: 'POST',
		headers: { 'Content-Type': 'application/json' },
		body: JSON.stringify({
			client_id: CLICKUP_CLIENT_ID,
			client_secret: CLICKUP_CLIENT_SECRET,
			code,
		}),
	});

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

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

	// Fetch workspace info to store team ID
	const teamsRes = await fetch('https://api.clickup.com/api/v2/team', {
		headers: { 'Authorization': accessToken },
	});

	let teamId = '';
	let teamName = 'ClickUp Workspace';
	if (teamsRes.ok) {
		const teamsData = await teamsRes.json() as any;
		if (teamsData.teams?.length > 0) {
			teamId = teamsData.teams[0].id;
			teamName = teamsData.teams[0].name;
		}
	}

	// Generate webhook secret
	const secretBuf = new Uint8Array(32);
	crypto.getRandomValues(secretBuf);
	const webhookSecret = Array.from(secretBuf).map(b => b.toString(16).padStart(2, '0')).join('');

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

	_syncServer!.changeDoc<ClickUpConnectionDoc>(docId, 'Connect ClickUp', (d) => {
		d.clickup = {
			accessToken,
			teamId,
			teamName,
			connectedAt: Date.now(),
			webhookSecret,
		};
	});

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

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

	const docId = clickupConnectionDocId(space);
	const doc = _syncServer?.getDoc<ClickUpConnectionDoc>(docId);

	if (doc?.clickup) {
		_syncServer!.changeDoc<ClickUpConnectionDoc>(docId, 'Disconnect ClickUp', (d) => {
			delete d.clickup;
		});
	}

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

export { clickupOAuthRoutes };
