import { SvelteKitAuth } from '@auth/sveltekit';
import type { OIDCConfig } from '@auth/core/providers';
import { env } from '$env/dynamic/private';

interface AuthentikProfile {
	sub: string;
	email: string;
	name: string;
	preferred_username: string;
	groups: string[];
}

/**
 * Fetch the user's node_id from maskinrommet using the Authentik access token.
 * Called once at sign-in so the node_id is cached in the JWT for future requests.
 */
async function fetchNodeId(accessToken: string): Promise<string | null> {
	const url = env.MASKINROMMET_URL ?? 'http://127.0.0.1:3100';
	try {
		const res = await fetch(`${url}/me`, {
			headers: { Authorization: `Bearer ${accessToken}` }
		});
		if (res.ok) {
			const data = await res.json();
			return data.node_id ?? null;
		}
		console.error(`[auth] /me returned ${res.status}: ${await res.text()}`);
	} catch (e) {
		console.error('[auth] Failed to fetch node_id from maskinrommet:', e);
	}
	return null;
}

/**
 * Sync user profile (username) to maskinrommet on sign-in.
 * Stores Authentik preferred_username in auth_identities.username.
 */
async function syncUsername(accessToken: string, username: string): Promise<void> {
	const url = env.MASKINROMMET_URL ?? 'http://127.0.0.1:3100';
	try {
		const res = await fetch(`${url}/auth/sync`, {
			method: 'POST',
			headers: {
				Authorization: `Bearer ${accessToken}`,
				'Content-Type': 'application/json'
			},
			body: JSON.stringify({ username })
		});
		if (!res.ok) {
			console.error(`[auth] /auth/sync returned ${res.status}: ${await res.text()}`);
		}
	} catch (e) {
		console.error('[auth] Failed to sync username to maskinrommet:', e);
	}
}

export const { handle, signIn, signOut } = SvelteKitAuth({
	trustHost: true,
	cookies: {
		sessionToken: {
			name: '__Secure-authjs.session-token',
			options: {
				httpOnly: true,
				sameSite: 'lax',
				path: '/',
				secure: true,
				domain: '.synops.no',
			},
		},
		callbackUrl: {
			name: '__Secure-authjs.callback-url',
			options: {
				httpOnly: true,
				sameSite: 'lax',
				path: '/',
				secure: true,
				domain: '.synops.no',
			},
		},
		csrfToken: {
			name: '__Host-authjs.csrf-token',
			options: {
				httpOnly: true,
				sameSite: 'lax',
				path: '/',
				secure: true,
			},
		},
	},
	providers: [
		{
			id: 'authentik',
			name: 'Authentik',
			type: 'oidc',
			issuer: env.AUTHENTIK_ISSUER,
			clientId: env.AUTHENTIK_CLIENT_ID,
			clientSecret: env.AUTHENTIK_CLIENT_SECRET,
			authorization: {
				params: {
					scope: 'openid email profile offline_access'
				}
			},
			checks: ['pkce', 'state'],
			profile(profile: AuthentikProfile) {
				return {
					id: profile.sub,
					name: profile.name ?? profile.preferred_username,
					email: profile.email
				};
			}
		} satisfies OIDCConfig<AuthentikProfile>
	],
	callbacks: {
		async jwt({ token, account, profile }) {
			// profile is only available on initial sign-in, not on refresh.
			// Store authentik_sub in the token so it persists across refreshes.
			if (profile?.sub) {
				token.authentik_sub = profile.sub;
			}
			// On initial sign-in, fetch node_id and store access_token
			if (account?.access_token) {
				token.access_token = account.access_token;
				if (!token.node_id) {
					token.node_id = await fetchNodeId(account.access_token);
				}
				// Sync username from Authentik preferred_username on each sign-in
				const p = profile as AuthentikProfile | undefined;
				if (p?.preferred_username) {
					await syncUsername(account.access_token, p.preferred_username);
				}
			}
			return token;
		},
		session({ session, token }) {
			if (session.user) {
				// Use Authentik sub as user ID (not @auth/sveltekit's internal ID)
				session.user.id = (token.authentik_sub ?? token.sub) as string;
			}
			// Expose node_id and access_token so frontend can call maskinrommet
			// eslint-disable-next-line @typescript-eslint/no-explicit-any
			const s = session as any;
			s.nodeId = token.node_id as string | undefined;
			s.accessToken = token.access_token as string | undefined;
			return session;
		}
	}
});
