import { JWTUtils } from './jwtUtils';

const PREVIEW_PURPOSE = 'space_preview';
export const SPACE_PREVIEW_TOKEN_TTL_SECONDS = 30 * 60;
export const SPACE_PREVIEW_COOKIE_NAME = '__space_preview';

export interface SpacePreviewClaims {
	spaceName: string;
	branch: string;
	userId: string;
	/**
	 * The app's `previewVersion` at mint time. Compared against the app's
	 * current value at preview time so a visibility toggle (which bumps the
	 * version) revokes this token.
	 */
	previewVersion: number;
}

export async function signSpacePreviewToken(
	env: { JWT_SECRET: string },
	claims: SpacePreviewClaims,
): Promise<string> {
	const jwt = JWTUtils.getInstance(env);
	return jwt.signPayload({ ...claims, purpose: PREVIEW_PURPOSE }, SPACE_PREVIEW_TOKEN_TTL_SECONDS);
}

export async function verifySpacePreviewToken(
	env: { JWT_SECRET: string },
	token: string,
	expectedSpaceName: string,
	expectedBranch: string,
): Promise<SpacePreviewClaims | null> {
	const jwt = JWTUtils.getInstance(env);
	const payload = await jwt.verifyPayload(token);
	if (!payload) return null;
	if (payload.purpose !== PREVIEW_PURPOSE) return null;
	if (typeof payload.spaceName !== 'string' || payload.spaceName !== expectedSpaceName) return null;
	if (typeof payload.branch !== 'string' || payload.branch !== expectedBranch) return null;
	if (typeof payload.userId !== 'string' || payload.userId.trim() === '') return null;
	if (typeof payload.previewVersion !== 'number' || !Number.isFinite(payload.previewVersion)) return null;

	return {
		spaceName: payload.spaceName,
		branch: payload.branch,
		userId: payload.userId,
		previewVersion: payload.previewVersion,
	};
}

/**
 * Path the preview cookie is scoped to. No trailing slash so it prefix-matches
 * `/space/<name>/preview/<branch>/...` but not a sibling like `<branch>2`.
 */
export function buildSpacePreviewCookiePath(spaceName: string, branch: string): string {
	return `/space/${encodeURIComponent(spaceName)}/preview/${encodeURIComponent(branch)}`;
}

/**
 * Build the `Set-Cookie` value for the path-scoped HttpOnly preview cookie.
 * - Separate preview domain (cross-site iframe): `SameSite=None; Secure; Partitioned`.
 * - Same-origin (dev/main domain): `SameSite=Lax` (+ `Secure` when served over https).
 */
export function buildPreviewCookie(opts: {
	token: string;
	spaceName: string;
	branch: string;
	crossSite: boolean;
	secure: boolean;
	ttlSeconds?: number;
}): string {
	const { token, spaceName, branch, crossSite, secure } = opts;
	const maxAge = opts.ttlSeconds ?? SPACE_PREVIEW_TOKEN_TTL_SECONDS;
	const path = buildSpacePreviewCookiePath(spaceName, branch);
	const parts = [
		`${SPACE_PREVIEW_COOKIE_NAME}=${token}`,
		`Path=${path}`,
		`Max-Age=${maxAge}`,
		'HttpOnly',
	];
	if (crossSite) {
		parts.push('SameSite=None', 'Secure', 'Partitioned');
	} else {
		parts.push('SameSite=Lax');
		if (secure) parts.push('Secure');
	}
	return parts.join('; ');
}

/** Read the raw preview-cookie value from a request's `Cookie` header. */
export function readPreviewCookie(request: Request): string | null {
	const header = request.headers.get('Cookie');
	if (!header) return null;
	for (const pair of header.split(';')) {
		const idx = pair.indexOf('=');
		if (idx === -1) continue;
		const name = pair.slice(0, idx).trim();
		if (name === SPACE_PREVIEW_COOKIE_NAME) {
			return pair.slice(idx + 1).trim();
		}
	}
	return null;
}
