import pkg from '@supabase/ssr';
const { createServerClient } = pkg;
import { type Handle, redirect } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks';
import { env as publicEnv } from '$env/dynamic/public';
import { env } from '$env/dynamic/private';
import type { Database } from '$lib/types/database';
import { supabaseAdmin } from '$lib/server/supabase';

// Cache for setup check to avoid hitting DB on every request
let setupCheckCache: { needsSetup: boolean; checkedAt: number } | null = null;
const SETUP_CACHE_TTL = 60000; // 1 minute cache

/**
 * Invalidate the setup check cache
 * Call this after creating the first admin user
 */
export function invalidateSetupCache() {
	setupCheckCache = null;
}

/**
 * Supabase authentication hook
 * Sets up the Supabase client with cookie handling for SSR
 */
const supabaseHandle: Handle = async ({ event, resolve }) => {
	// Use internal URL for server-side operations (Docker network), fallback to public URL
	const supabaseUrl = env.SUPABASE_INTERNAL_URL || publicEnv.PUBLIC_SUPABASE_URL;
	const supabaseAnonKey = publicEnv.PUBLIC_SUPABASE_ANON_KEY;

	event.locals.supabase = createServerClient<Database>(
		supabaseUrl,
		supabaseAnonKey,
		{
			cookies: {
				getAll: () => event.cookies.getAll(),
				setAll: (cookiesToSet) => {
					cookiesToSet.forEach(({ name, value, options }) => {
						event.cookies.set(name, value, { ...options, path: '/' });
					});
				}
			}
		}
	);

	/**
	 * Safe session getter that validates the JWT
	 * Returns session, user, and member data
	 */
	event.locals.safeGetSession = async () => {
		const {
			data: { session }
		} = await event.locals.supabase.auth.getSession();

		if (!session) {
			return { session: null, user: null, member: null };
		}

		// Validate the session by getting the user
		const {
			data: { user },
			error: userError
		} = await event.locals.supabase.auth.getUser();

		if (userError || !user) {
			return { session: null, user: null, member: null };
		}

		// Fetch member profile with dues status
		const { data: member } = await event.locals.supabase
			.from('members_with_dues')
			.select('*')
			.eq('id', user.id)
			.single();

		return { session, user, member };
	};

	return resolve(event, {
		filterSerializedResponseHeaders(name) {
			return name === 'content-range' || name === 'x-supabase-api-version';
		}
	});
};

/**
 * Setup check hook
 * Redirects to /setup if no users exist in the system
 */
const setupCheckHandle: Handle = async ({ event, resolve }) => {
	const path = event.url.pathname;

	// Always allow access to setup page and static assets
	if (path === '/setup' || path.startsWith('/api/') || path.startsWith('/auth/')) {
		return resolve(event);
	}

	// Check cache first
	const now = Date.now();
	if (setupCheckCache && (now - setupCheckCache.checkedAt) < SETUP_CACHE_TTL) {
		if (setupCheckCache.needsSetup) {
			throw redirect(303, '/setup');
		}
		return resolve(event);
	}

	// Check if any users exist
	try {
		const { count, error } = await supabaseAdmin
			.from('members')
			.select('*', { count: 'exact', head: true });

		if (error) {
			console.error('Error checking for existing users:', error);
			// If table doesn't exist or other DB error, assume setup is needed
			// Common errors: relation "members" does not exist, permission denied
			if (error.message?.includes('does not exist') ||
			    error.message?.includes('relation') ||
			    error.code === '42P01' || // undefined_table
			    error.code === 'PGRST204') { // no rows (table might not exist)
				setupCheckCache = { needsSetup: true, checkedAt: now };
				throw redirect(303, '/setup');
			}
			// For other errors, fail open to avoid blocking the app
			return resolve(event);
		}

		const needsSetup = !count || count === 0;
		setupCheckCache = { needsSetup, checkedAt: now };

		if (needsSetup) {
			throw redirect(303, '/setup');
		}
	} catch (err) {
		// If it's a redirect, rethrow it
		if (err && typeof err === 'object' && 'status' in err) {
			throw err;
		}
		console.error('Error in setup check:', err);
		// On unexpected errors, redirect to setup as a safe default
		throw redirect(303, '/setup');
	}

	return resolve(event);
};

/**
 * Authorization hook
 * Protects routes based on authentication and role requirements
 */
const authorizationHandle: Handle = async ({ event, resolve }) => {
	const { session, member } = await event.locals.safeGetSession();
	const path = event.url.pathname;

	// Setup page is handled by setupCheckHandle
	if (path === '/setup') {
		return resolve(event);
	}

	// API routes handle their own authentication
	if (path.startsWith('/api/')) {
		return resolve(event);
	}

	// Auth callback routes should always be accessible
	if (path.startsWith('/auth/')) {
		return resolve(event);
	}

	// Logout route should always be accessible
	if (path === '/logout') {
		return resolve(event);
	}

	// Protected routes - require authentication
	const protectedPrefixes = ['/dashboard', '/profile', '/payments', '/documents', '/board', '/admin'];
	const isProtectedRoute = protectedPrefixes.some((prefix) => path.startsWith(prefix));

	if (isProtectedRoute && !session) {
		throw redirect(303, `/login?redirectTo=${encodeURIComponent(path)}`);
	}

	// Handle authenticated users without a member profile
	// This can happen if member record creation failed or was deleted
	if (isProtectedRoute && session && !member) {
		console.error('Authenticated user has no member profile:', session.user?.id);
		// Sign them out and redirect to login with an error
		await event.locals.supabase.auth.signOut();
		throw redirect(303, '/login?error=no_profile');
	}

	// Board routes - require board or admin role
	if (path.startsWith('/board') && member) {
		if (member.role !== 'board' && member.role !== 'admin') {
			throw redirect(303, '/dashboard');
		}
	}

	// Admin routes - require admin role
	if (path.startsWith('/admin') && member) {
		if (member.role !== 'admin') {
			throw redirect(303, '/dashboard');
		}
	}

	// Redirect authenticated users away from auth pages
	if (session && (path === '/login' || path === '/signup')) {
		throw redirect(303, '/dashboard');
	}

	return resolve(event);
};

export const handle: Handle = sequence(supabaseHandle, setupCheckHandle, authorizationHandle);
