<script lang="ts">
	import type {
		LoginFlow,
		RegistrationFlow,
		RecoveryFlow,
		VerificationFlow,
		SettingsFlow
	} from '@ory/client';
	import FormField from './FormField.svelte';
	import { filterNodesByGroups, formDataToJson } from '$lib/utils';
	import { kratosClient } from '$lib/kratos';
	import { PUBLIC_KRATOS_URL } from '$env/static/public';

	type Flow = LoginFlow | RegistrationFlow | RecoveryFlow | VerificationFlow | SettingsFlow;

	let {
		flow,
		groups = ['default', 'password', 'profile', 'totp', 'webauthn', 'code', 'link']
	}: { flow: Flow; groups?: string[] } = $props();

	const nodes = $derived(filterNodesByGroups(flow.ui.nodes, ...groups));
	const messages = $derived(flow.ui.messages || []);
	const formMethod = $derived((flow.ui.method?.toLowerCase() || 'post') as 'get' | 'post');

	// Check if form has password fields for accessibility
	const hasPasswordField = $derived(
		nodes.some((node) => {
			const attrs = node.attributes as any;
			return attrs.type === 'password';
		})
	);

	// Get username/email for accessibility (for password managers)
	const usernameValue = $derived(() => {
		// For settings flow, get email from identity
		if ('identity' in flow && flow.identity?.traits) {
			return (flow.identity.traits as any).email || '';
		}
		// For other flows, try to get from nodes
		const emailNode = nodes.find((node) => {
			const attrs = node.attributes as any;
			return attrs.name === 'traits.email' || attrs.name === 'identifier';
		});
		if (emailNode) {
			const attrs = emailNode.attributes as any;
			return attrs.value || '';
		}
		return '';
	});

	// Helper to determine actual flow type since flow.type might be 'browser'
	function getFlowType(flow: Flow): string {
		// If flow.type is specific, use it
		if (flow.type && flow.type !== 'browser') {
			return flow.type;
		}

		// Try to determine from the form action URL
		const actionUrl = flow.ui?.action || '';
		if (actionUrl.includes('/self-service/login')) return 'login';
		if (actionUrl.includes('/self-service/registration')) return 'registration';
		if (actionUrl.includes('/self-service/settings')) return 'settings';
		if (actionUrl.includes('/self-service/recovery')) return 'recovery';
		if (actionUrl.includes('/self-service/verification')) return 'verification';

		// Try to determine from current URL
		if (typeof window !== 'undefined') {
			const currentPath = window.location.pathname;
			if (currentPath.includes('/login')) return 'login';
			if (currentPath.includes('/registration')) return 'registration';
			if (currentPath.includes('/settings')) return 'settings';
			if (currentPath.includes('/recovery')) return 'recovery';
			if (currentPath.includes('/verification')) return 'verification';
		}

		// Fallback to the original type
		return flow.type || 'unknown';
	}

	// SDK-based form submission - handles CSRF automatically via JSON + cookies
	async function handleSubmit(event: SubmitEvent) {
		event.preventDefault();
		const form = event.target as HTMLFormElement;
		const formData = new FormData(form);

		// When using preventDefault, submit button values are not included in FormData
		// We need to manually add the method from the clicked submit button
		const submitter = event.submitter as HTMLButtonElement;
		if (submitter && submitter.name && submitter.value) {
			formData.set(submitter.name, submitter.value);
		}
		const updateBody = formDataToJson(formData);

		// Determine flow type from URL or flow properties since flow.type might be 'browser'
		const flowType = getFlowType(flow);

		// For settings flows, manually add CSRF token if not present in form data
		if (flowType === 'settings' && !updateBody.csrf_token) {
			// Try to get CSRF token from flow nodes (hidden fields)
			const csrfNode = flow.ui.nodes.find((node) => {
				const attrs = node.attributes as any;
				return attrs?.name === 'csrf_token' && attrs?.type === 'hidden';
			});
			if (csrfNode) {
				const attrs = csrfNode.attributes as any;
				if (attrs?.value) {
					updateBody.csrf_token = attrs.value;
				}
			}
		}

		// Validate flow type before attempting submission
		if (!['login', 'registration', 'settings', 'recovery', 'verification'].includes(flowType)) {
			console.error(`Unsupported flow type: ${flowType}`);
			alert(`Error: Unsupported flow type "${flowType}". Please refresh the page and try again.`);
			return;
		}

		try {
			let response;

			// Use appropriate SDK method based on flow type
			switch (flowType) {
				case 'login':
					response = await kratosClient.updateLoginFlow({
						flow: flow.id,
						updateLoginFlowBody: updateBody as any
					});
					break;

				case 'registration':
					response = await kratosClient.updateRegistrationFlow({
						flow: flow.id,
						updateRegistrationFlowBody: updateBody as any
					});
					break;

				case 'settings':
					response = await kratosClient.updateSettingsFlow({
						flow: flow.id,
						updateSettingsFlowBody: updateBody as any
					});
					break;

				case 'recovery':
					response = await kratosClient.updateRecoveryFlow({
						flow: flow.id,
						updateRecoveryFlowBody: updateBody as any
					});
					break;

				case 'verification':
					response = await kratosClient.updateVerificationFlow({
						flow: flow.id,
						updateVerificationFlowBody: updateBody as any
					});
					break;

				default:
					// TypeScript exhaustiveness check - should never reach here
					console.error(`Unsupported flow type: ${flowType}`);
					return;
			}

			// Handle success response
			const responseData = response.data as any;

			// Special handling for recovery and verification flows
			// These flows need to stay on the same page to preserve the flow ID
			if (flowType === 'recovery' || flowType === 'verification') {
				const state = (responseData as any).state;

				// If email was sent or we're in a code-entry state, reload with same flow to update UI
				if (state === 'sent_email' || state === 'choose_method') {
					window.location.href = window.location.pathname + '?flow=' + flow.id;
					return;
				}

				// If we passed the challenge (code verified), follow redirect or go to settings
				if (responseData.redirect_browser_to) {
					window.location.href = responseData.redirect_browser_to;
					return;
				}

				// Otherwise reload with same flow to show updated state
				window.location.href = window.location.pathname + '?flow=' + flow.id;
				return;
			}

			// For other flows, follow redirects or default behavior
			if (responseData.redirect_browser_to) {
				window.location.href = responseData.redirect_browser_to;
			} else {
				// For settings, show success message
				if (flowType === 'settings') {
					window.location.href = window.location.pathname + '?updated=true';
				} else {
					// For login/registration, check for return_to in flow data first
					const returnTo = (flow as any).return_to;
					if (returnTo && (flowType === 'login' || flowType === 'registration')) {
						// Redirect to the original requested URL
						window.location.href = returnTo;
					} else {
						// Default to home page
						window.location.href = '/';
					}
				}
			}
		} catch (error: any) {
			console.error('Flow submission failed:', error);

			// Log detailed error information
			if (error.response) {
				console.error('Error status:', error.response.status);
				console.error('Error data:', JSON.stringify(error.response.data, null, 2));
				console.error('Error headers:', error.response.headers);
			}

			// Handle 422 with redirect_browser_to - this is actually a success case
			// Kratos uses 422 to signal that a redirect is needed to complete the flow
			if (error.response?.status === 422 && error.response?.data?.redirect_browser_to) {
				window.location.href = error.response.data.redirect_browser_to;
				return;
			}

			// Handle flow expired error
			if (error.response?.status === 410) {
				window.location.href = `${PUBLIC_KRATOS_URL}/self-service/${flowType}/browser`;
			} else if (error.response?.data?.ui) {
				// Update flow with validation errors from server
				flow = error.response.data;
			} else if (error.response?.data) {
				console.error('Server error details:', error.response.data);
				// Show error message to user if available
				if (error.response.data.error) {
					alert(
						`Error: ${error.response.data.error.message || JSON.stringify(error.response.data.error)}`
					);
				}
			}
		}
	}
</script>

<div class="flow-form">
	{#if messages.length > 0}
		<div class="mb-4 space-y-2">
			{#each messages as message}
				{#if message.type === 'error'}
					<div class="alert-error">
						{message.text}
					</div>
				{:else if message.type === 'success'}
					<div class="alert-success">
						{message.text}
					</div>
				{:else}
					<div class="rounded-lg border border-primary-400 bg-primary-50 p-4 text-sm text-primary-800 dark:border-primary-600 dark:bg-primary-900/20 dark:text-primary-400">
						{message.text}
					</div>
				{/if}
			{/each}
		</div>
	{/if}

	<form action={flow.ui.action} method={formMethod} onsubmit={handleSubmit}>
		<!-- Hidden username field for accessibility when form has password fields -->
		{#if hasPasswordField && usernameValue()}
			<input
				type="text"
				name="username"
				value={usernameValue()}
				autocomplete="username"
				readonly
				style="display: none;"
				aria-hidden="true"
				tabindex="-1"
			/>
		{/if}
		{#each nodes as node (node.attributes)}
			<FormField {node} />
		{/each}
	</form>
</div>
