<script lang="ts">
	import { beforeNavigate } from '$app/navigation';
	import { page } from '$app/state';
	import { openConfirmDialog } from '$lib/components/confirm-dialog';
	import CopyToClipboard from '$lib/components/copy-to-clipboard.svelte';
	import FormattedMessage from '$lib/components/formatted-message.svelte';
	import * as Alert from '$lib/components/ui/alert';
	import { Button } from '$lib/components/ui/button';
	import * as Card from '$lib/components/ui/card';
	import * as Field from '$lib/components/ui/field';
	import * as Tabs from '$lib/components/ui/tabs';
	import UserGroupSelection from '$lib/components/user-group-selection.svelte';
	import { m } from '$lib/paraglide/messages';
	import OidcService from '$lib/services/oidc-service';
	import ScimService from '$lib/services/scim-service';
	import clientSecretStore from '$lib/stores/client-secret-store';
	import type { OidcClientCreateWithLogo } from '$lib/types/oidc.type';
	import type { ScimServiceProviderCreate } from '$lib/types/scim.type';
	import { cachedOidcClientLogo } from '$lib/utils/cached-image-util';
	import { axiosErrorToast } from '$lib/utils/error-util';
	import {
		LucideChevronLeft,
		LucideInfo,
		LucideRefreshCcw,
		LucideTriangleAlert
	} from '@lucide/svelte';
	import { toast } from 'svelte-sonner';
	import { slide } from 'svelte/transition';
	import { backNavigate } from '../../users/navigate-back-util';
	import OidcForm from '../oidc-client-form.svelte';
	import OidcClientPreviewModal from '../oidc-client-preview-modal.svelte';
	import ApiAccessCard from './api-access-card.svelte';
	import ScimResourceProviderForm from './scim-resource-provider-form.svelte';

	let { data } = $props();
	let client = $state({
		...data.client,
		allowedUserGroupIds: data.client.allowedUserGroups.map((g) => g.id)
	});

	let scimServiceProvider = $state(data.scimServiceProvider);
	let showAllDetails = $state(false);
	let showPreview = $state(false);

	const oidcService = new OidcService();
	const scimService = new ScimService();
	const backNavigation = backNavigate('/settings/admin/oidc-clients');

	const setupDetails = $state({
		[m.issuer_url()]: `https://${page.url.host}`,
		[m.authorization_url()]: `https://${page.url.host}/authorize`,
		[m.oidc_discovery_url()]: `https://${page.url.host}/.well-known/openid-configuration`,
		[m.token_url()]: `https://${page.url.host}/api/oidc/token`,
		[m.userinfo_url()]: `https://${page.url.host}/api/oidc/userinfo`,
		[m.logout_url()]: `https://${page.url.host}/api/oidc/end-session`,
		[m.certificate_url()]: `https://${page.url.host}/.well-known/jwks.json`,
		[m.pkce()]: client.pkceEnabled ? m.enabled() : m.disabled(),
		[m.requires_reauthentication()]: client.requiresReauthentication ? m.enabled() : m.disabled(),
		[m.requires_pushed_authorization_requests()]: client.requiresPushedAuthorizationRequests
			? m.enabled()
			: m.disabled()
	});

	async function updateClient(updatedClient: OidcClientCreateWithLogo) {
		let success = true;
		const dataPromise = oidcService.updateClient(client.id, updatedClient);
		const imagePromise =
			updatedClient.logo !== undefined
				? oidcService.updateClientLogo(client, updatedClient.logo, true)
				: Promise.resolve();

		const darkImagePromise =
			updatedClient.darkLogo !== undefined
				? oidcService.updateClientLogo(client, updatedClient.darkLogo, false)
				: Promise.resolve();

		client.isPublic = updatedClient.isPublic;
		setupDetails[m.pkce()] = updatedClient.pkceEnabled ? m.enabled() : m.disabled();
		setupDetails[m.requires_reauthentication()] = updatedClient.requiresReauthentication
			? m.enabled()
			: m.disabled();

		await Promise.all([dataPromise, imagePromise, darkImagePromise])
			.then(() => {
				setupDetails[m.requires_pushed_authorization_requests()] =
					updatedClient.requiresPushedAuthorizationRequests ? m.enabled() : m.disabled();
				if (updatedClient.logoUrl) {
					cachedOidcClientLogo.bustCache(client.id, true);
				}
				if (updatedClient.darkLogoUrl) {
					cachedOidcClientLogo.bustCache(client.id, false);
				}

				// Update the hasLogo and hasDarkLogo flags after successful upload
				if (updatedClient.logo !== undefined || updatedClient.logoUrl !== undefined) {
					client.hasLogo = updatedClient.logo !== null || !!updatedClient.logoUrl;
				}
				if (updatedClient.darkLogo !== undefined || updatedClient.darkLogoUrl !== undefined) {
					client.hasDarkLogo = updatedClient.darkLogo !== null || !!updatedClient.darkLogoUrl;
				}
				if (updatedClient.pkceEnabled) {
					client.pkceEnabled = updatedClient.pkceEnabled;
				}
				toast.success(m.oidc_client_updated_successfully());
			})
			.catch((e) => {
				axiosErrorToast(e);
				success = false;
			});

		return success;
	}

	async function enableGroupRestriction() {
		client.isGroupRestricted = true;
		await oidcService
			.updateClient(client.id, {
				...client,
				isGroupRestricted: true
			})
			.then(() => {
				toast.success(m.user_groups_restriction_updated_successfully());
				client.isGroupRestricted = true;
			})
			.catch(axiosErrorToast);
	}

	function disableGroupRestriction() {
		openConfirmDialog({
			title: m.unrestrict_oidc_client({ clientName: client.name }),
			message: m.confirm_unrestrict_oidc_client_description({ clientName: client.name }),
			confirm: {
				label: m.unrestrict(),
				destructive: true,
				action: async () => {
					await oidcService
						.updateClient(client.id, {
							...client,
							isGroupRestricted: false
						})
						.then(() => {
							toast.success(m.user_groups_restriction_updated_successfully());
							client.allowedUserGroupIds = [];
							client.isGroupRestricted = false;
						})
						.catch(axiosErrorToast);
				}
			}
		});
	}

	async function createClientSecret() {
		openConfirmDialog({
			title: m.create_new_client_secret(),
			message: m.are_you_sure_you_want_to_create_a_new_client_secret(),
			confirm: {
				label: m.generate(),
				destructive: true,
				action: async () => {
					try {
						const clientSecret = await oidcService.createClientSecret(client.id);
						clientSecretStore.set(clientSecret);
						toast.success(m.new_client_secret_created_successfully());
					} catch (e) {
						axiosErrorToast(e);
					}
				}
			}
		});
	}

	async function updateUserGroupClients(allowedGroups: string[]) {
		await oidcService
			.updateAllowedUserGroups(client.id, allowedGroups)
			.then(() => {
				toast.success(m.allowed_user_groups_updated_successfully());
			})
			.catch((e) => {
				axiosErrorToast(e);
			});
	}

	async function saveScimServiceProvider(provider: ScimServiceProviderCreate | null) {
		try {
			if (!provider) {
				await scimService.deleteServiceProvider(scimServiceProvider!.id);
				scimServiceProvider = undefined;
				toast.success(m.scim_disabled_successfully());
				return true;
			}
			let createdProvider;
			if (scimServiceProvider) {
				createdProvider = await scimService.updateServiceProvider(scimServiceProvider.id, provider);
				toast.success(m.scim_configuration_updated_successfully());
			} else {
				createdProvider = await scimService.createServiceProvider(provider);
				toast.success(m.scim_enabled_successfully());
			}
			scimServiceProvider = createdProvider;
			return true;
		} catch (e) {
			axiosErrorToast(e);
			return false;
		}
	}

	beforeNavigate(() => {
		clientSecretStore.clear();
	});
</script>

<svelte:head>
	<title>{m.oidc_client_name({ name: client.name })}</title>
</svelte:head>

{#snippet UnrestrictButton()}
	<Button
		onclick={enableGroupRestriction}
		variant={client.isGroupRestricted ? 'secondary' : 'default'}>{m.restrict()}</Button
	>
{/snippet}

{#if client.pkceSupported && !client.pkceEnabled}
	<Alert.Root variant="info">
		<LucideInfo class="size-4" />
		<Alert.Title>{m.pkce_supported_client_title()}</Alert.Title>
		<Alert.Description>
			{m.pkce_supported_client_description()}
		</Alert.Description>
	</Alert.Root>
{/if}

<div>
	<button type="button" class="text-muted-foreground flex text-sm" onclick={backNavigation.go}
		><LucideChevronLeft class="size-5" /> {m.back()}</button
	>
</div>

<Tabs.Root value="general" useHash class="gap-4">
	<div class="overflow-x-auto pb-1">
		<Tabs.List variant="line" class="min-w-max">
			<Tabs.Trigger value="general">{m.general()}</Tabs.Trigger>
			<Tabs.Trigger value="user-groups">
				{m.allowed_user_groups()}
				{#if client.isGroupRestricted && client.allowedUserGroupIds.length === 0}
					<LucideTriangleAlert class="ml-0.5 size-4 text-yellow-600 dark:text-yellow-400" />
				{/if}</Tabs.Trigger
			>
			<Tabs.Trigger value="api-access">{m.api_access()}</Tabs.Trigger>
			<Tabs.Trigger value="scim">{m.scim_provisioning()}</Tabs.Trigger>
			<Tabs.Trigger value="preview">{m.oidc_data_preview()}</Tabs.Trigger>
		</Tabs.List>
	</div>

	<Tabs.Content value="general" class="flex flex-col gap-4">
		<Card.Root>
			<Card.Header>
				<Card.Title>{client.name}</Card.Title>
			</Card.Header>
			<Card.Content>
				<div class="flex flex-col">
					<div class="mb-2 flex flex-col sm:flex-row sm:items-center">
						<Field.Label class="w-52">{m.client_id()}</Field.Label>
						<CopyToClipboard value={client.id}>
							<span class="text-muted-foreground text-sm" data-testid="client-id">
								{client.id}
							</span>
						</CopyToClipboard>
					</div>
					{#if !client.isPublic}
						<div class="mt-1 mb-2 flex flex-col sm:flex-row sm:items-center">
							<Field.Label class="w-52">{m.client_secret()}</Field.Label>
							{#if $clientSecretStore}
								<CopyToClipboard value={$clientSecretStore}>
									<span class="text-muted-foreground text-sm" data-testid="client-secret">
										{$clientSecretStore}
									</span>
								</CopyToClipboard>
							{:else}
								<div>
									<span class="text-muted-foreground text-sm" data-testid="client-secret"
										>••••••••••••••••••••••••••••••••</span
									>
									<Button
										class="ml-2"
										onclick={createClientSecret}
										size="sm"
										variant="ghost"
										aria-label="Create new client secret"
										><LucideRefreshCcw class="size-3" /></Button
									>
								</div>
							{/if}
						</div>
					{/if}
					{#if showAllDetails}
						<div transition:slide>
							{#each Object.entries(setupDetails) as [key, value]}
								<div class="mb-2 flex flex-col sm:flex-row sm:items-center">
									<Field.Label class="w-52">{key}</Field.Label>
									<CopyToClipboard {value}>
										<span class="text-muted-foreground text-sm">{value}</span>
									</CopyToClipboard>
								</div>
							{/each}
						</div>
					{/if}

					{#if !showAllDetails}
						<div class="mt-4 flex justify-center">
							<Button onclick={() => (showAllDetails = true)} size="sm" variant="ghost"
								>{m.show_more_details()}</Button
							>
						</div>
					{/if}
				</div>
			</Card.Content>
		</Card.Root>

		<Card.Root>
			<Card.Content>
				<OidcForm mode="update" existingClient={client} callback={updateClient} />
			</Card.Content>
		</Card.Root>
	</Tabs.Content>

	<Tabs.Content value="user-groups" id="allowed-user-groups">
		<Card.Root>
			<Card.Header>
				<div class="flex items-center justify-between gap-4">
					<div>
						<Card.Title>{m.allowed_user_groups()}</Card.Title>
						<Card.Description>
							{client.isGroupRestricted
								? m.allowed_user_groups_description()
								: m.allowed_user_groups_status_unrestricted_description()}
						</Card.Description>
					</div>
					{#if !client.isGroupRestricted}
						{@render UnrestrictButton()}
					{/if}
				</div>
			</Card.Header>
			{#if client.isGroupRestricted}
				<Card.Content>
					<UserGroupSelection bind:selectedGroupIds={client.allowedUserGroupIds} />
					<div class="mt-5 flex justify-end gap-3">
						<Button onclick={disableGroupRestriction} variant="secondary">{m.unrestrict()}</Button>

						<Button
							usePromiseLoading
							onclick={() => updateUserGroupClients(client.allowedUserGroupIds)}>{m.save()}</Button
						>
					</div>
				</Card.Content>
			{/if}
		</Card.Root>
	</Tabs.Content>

	<Tabs.Content value="api-access" id="api-access">
		<Card.Root>
			<Card.Header>
				<Card.Title>{m.api_access()}</Card.Title>
				<Card.Description>{m.api_access_description()}</Card.Description>
			</Card.Header>
			<Card.Content>
				<ApiAccessCard clientId={client.id} isPublicClient={client.isPublic} />
			</Card.Content>
		</Card.Root>
	</Tabs.Content>

	<Tabs.Content value="scim" id="scim-provisioning">
		<Card.Root>
			<Card.Header>
				<Card.Title>{m.scim_provisioning()}</Card.Title>
				<Card.Description>
					<FormattedMessage m={m.scim_provisioning_description()} />
				</Card.Description>
			</Card.Header>
			<Card.Content>
				<ScimResourceProviderForm
					oidcClientId={client.id}
					existingProvider={scimServiceProvider}
					onSave={saveScimServiceProvider}
				/>
			</Card.Content>
		</Card.Root>
	</Tabs.Content>

	<Tabs.Content value="preview">
		<Card.Root>
			<Card.Header>
				<div class="flex flex-col items-start justify-between gap-3 sm:flex-row sm:items-center">
					<div>
						<Card.Title>
							{m.oidc_data_preview()}
						</Card.Title>
						<Card.Description>
							{m.preview_the_oidc_data_that_would_be_sent_for_different_users()}
						</Card.Description>
					</div>

					<Button variant="outline" onclick={() => (showPreview = true)}>
						{m.show()}
					</Button>
				</div>
			</Card.Header>
		</Card.Root>
	</Tabs.Content>
</Tabs.Root>
<OidcClientPreviewModal bind:open={showPreview} clientId={client.id} />
