const APPLE_AUTH_URL = 'https://appleid.apple.com/auth/authorize';
const APPLE_TOKEN_URL = 'https://appleid.apple.com/auth/token';

export function getAppleAuthUrl(state: string): string {
	const params = new URLSearchParams({
		client_id: Bun.env.APPLE_ID!,
		redirect_uri: `${Bun.env.PUBLIC_SITE_URL}/auth/apple/callback`,
		response_type: 'code',
		response_mode: 'form_post',
		scope: 'name email',
		state
	});
	return `${APPLE_AUTH_URL}?${params.toString()}`;
}

export async function generateAppleClientSecret(): Promise<string> {
	const header = { alg: 'ES256', kid: Bun.env.APPLE_KEY_ID! };
	const now = Math.floor(Date.now() / 1000);
	const payload = {
		iss: Bun.env.APPLE_TEAM_ID!,
		iat: now,
		exp: now + 86400 * 180,
		aud: 'https://appleid.apple.com',
		sub: Bun.env.APPLE_ID!
	};

	const encodedHeader = Buffer.from(JSON.stringify(header)).toString('base64url');
	const encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64url');
	const signingInput = `${encodedHeader}.${encodedPayload}`;

	// Import PEM private key
	const pemBody = Bun.env.APPLE_PRIVATE_KEY!.replace(/-----BEGIN PRIVATE KEY-----/, '')
		.replace(/-----END PRIVATE KEY-----/, '')
		.replace(/\s/g, '');
	const keyBuffer = Uint8Array.from(atob(pemBody), (c) => c.charCodeAt(0));

	const key = await crypto.subtle.importKey(
		'pkcs8',
		keyBuffer,
		{ name: 'ECDSA', namedCurve: 'P-256' },
		false,
		['sign']
	);

	const signatureBuffer = await crypto.subtle.sign(
		{ name: 'ECDSA', hash: 'SHA-256' },
		key,
		new TextEncoder().encode(signingInput)
	);

	const signature = new Uint8Array(signatureBuffer);

	// crypto.subtle may return DER or raw (IEEE P1363) format depending on runtime
	// Raw format is exactly 64 bytes (32-byte r + 32-byte s)
	const rawSignature = signature.length === 64 ? signature : derToRaw(signature);
	const encodedSignature = Buffer.from(rawSignature).toString('base64url');

	return `${signingInput}.${encodedSignature}`;
}

/**
 * Convert a DER-encoded ECDSA signature to raw r||s format (64 bytes for P-256)
 */
function derToRaw(der: Uint8Array): Uint8Array {
	// DER structure: 0x30 [total-len] 0x02 [r-len] [r] 0x02 [s-len] [s]
	let offset = 2; // skip 0x30 and total length

	// Read r
	if (der[offset] !== 0x02) throw new Error('Invalid DER signature');
	offset++;
	const rLen = der[offset];
	offset++;
	let r = der.slice(offset, offset + rLen);
	offset += rLen;

	// Read s
	if (der[offset] !== 0x02) throw new Error('Invalid DER signature');
	offset++;
	const sLen = der[offset];
	offset++;
	let s = der.slice(offset, offset + sLen);

	// Remove leading zero padding (DER uses it for positive sign)
	if (r.length === 33 && r[0] === 0) r = r.slice(1);
	if (s.length === 33 && s[0] === 0) s = s.slice(1);

	// Pad to 32 bytes each
	const raw = new Uint8Array(64);
	raw.set(r, 32 - r.length);
	raw.set(s, 64 - s.length);
	return raw;
}

export async function exchangeAppleCode(
	code: string,
	redirectUri: string
): Promise<{
	access_token: string;
	token_type: string;
	expires_in: number;
	refresh_token: string;
	id_token: string;
}> {
	const clientSecret = await generateAppleClientSecret();

	const params = new URLSearchParams({
		client_id: Bun.env.APPLE_ID!,
		client_secret: clientSecret,
		code,
		grant_type: 'authorization_code',
		redirect_uri: redirectUri
	});

	const response = await fetch(APPLE_TOKEN_URL, {
		method: 'POST',
		headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
		body: params.toString()
	});

	if (!response.ok) {
		const error = await response.text();
		throw new Error(`Apple token exchange failed: ${error}`);
	}

	return await response.json();
}

/**
 * Decode Apple's id_token JWT payload without signature verification.
 * Safe because the token is received directly from Apple's token endpoint over TLS.
 */
export function decodeAppleIdToken(idToken: string): {
	sub: string;
	email?: string;
	email_verified?: string;
	is_private_email?: string;
} {
	const [, payloadB64] = idToken.split('.');
	const padded = payloadB64 + '='.repeat((4 - (payloadB64.length % 4)) % 4);
	const payload = JSON.parse(atob(padded.replace(/-/g, '+').replace(/_/g, '/')));
	return payload;
}
