<!--
  WebhookPanel

  Generic panel that displays an entity-scoped webhook URL and exposes a
  "regenerate" action. Used by both project and static-site detail pages.

  Parent supplies a fetch + regenerate pair returning { webhook_url, webhook_secret }.
  Panel absolutises the URL with window.location.origin for easy copy/paste.
-->
<script lang="ts">
	import { onMount } from 'svelte';
	import { toasts } from '$lib/stores/toast';
	import { t } from '$lib/i18n';
	import ToggleSwitch from './ToggleSwitch.svelte';
	import { IconCopy, IconRefresh, IconLoader, IconShield, IconX } from '$lib/components/icons';

	interface WebhookUrlResponse {
		webhook_url: string;
		webhook_secret: string;
		has_signing_secret?: boolean;
		webhook_require_signature?: boolean;
	}

	interface SigningSecretResponse {
		signing_secret: string;
	}

	interface Props {
		title: string;
		description: string;
		fetchWebhook: () => Promise<WebhookUrlResponse>;
		regenerateWebhook: () => Promise<WebhookUrlResponse>;
		// Inbound HMAC signing — optional; if omitted, the signing UI hides.
		regenerateSigningSecret?: () => Promise<SigningSecretResponse>;
		disableSigning?: () => Promise<void>;
		setRequireSignature?: (require: boolean) => Promise<void>;
	}

	let {
		title,
		description,
		fetchWebhook,
		regenerateWebhook,
		regenerateSigningSecret,
		disableSigning,
		setRequireSignature
	}: Props = $props();

	let relativeUrl = $state('');
	let loading = $state(true);
	let regenerating = $state(false);
	let confirmOpen = $state(false);

	// Signing state.
	let hasSigningSecret = $state(false);
	let requireSignature = $state(false);
	// Newly issued signing secret — displayed once after rotate, hidden on next load.
	let issuedSigningSecret = $state('');
	let signingBusy = $state(false);
	let confirmDisableSigning = $state(false);

	const absoluteUrl = $derived(
		relativeUrl && typeof window !== 'undefined' ? window.location.origin + relativeUrl : relativeUrl
	);

	async function load() {
		loading = true;
		try {
			const res = await fetchWebhook();
			relativeUrl = res.webhook_url;
			hasSigningSecret = res.has_signing_secret ?? false;
			requireSignature = res.webhook_require_signature ?? false;
			// Hide any previously-displayed issued secret on reload — it
			// must only ever be shown once at issue time.
			issuedSigningSecret = '';
		} catch (err) {
			toasts.error(err instanceof Error ? err.message : $t('webhookPanel.loadFailed'));
		} finally {
			loading = false;
		}
	}

	async function handleRegenerate() {
		confirmOpen = false;
		regenerating = true;
		try {
			const res = await regenerateWebhook();
			relativeUrl = res.webhook_url;
			hasSigningSecret = res.has_signing_secret ?? hasSigningSecret;
			requireSignature = res.webhook_require_signature ?? requireSignature;
			toasts.success($t('webhookPanel.regenerated'));
		} catch (err) {
			toasts.error(err instanceof Error ? err.message : $t('webhookPanel.regenerateFailed'));
		} finally {
			regenerating = false;
		}
	}

	function handleCopy() {
		if (!absoluteUrl) return;
		navigator.clipboard.writeText(absoluteUrl).then(
			() => toasts.info($t('webhookPanel.copied')),
			() => toasts.error($t('webhookPanel.copyFailed'))
		);
	}

	function copyIssuedSecret() {
		if (!issuedSigningSecret) return;
		navigator.clipboard.writeText(issuedSigningSecret).then(
			() => toasts.info($t('webhookPanel.signingCopied')),
			() => toasts.error($t('webhookPanel.copyFailed'))
		);
	}

	async function handleIssueSigning() {
		if (!regenerateSigningSecret) return;
		signingBusy = true;
		try {
			const res = await regenerateSigningSecret();
			issuedSigningSecret = res.signing_secret;
			hasSigningSecret = true;
			toasts.success($t('webhookPanel.signingIssued'));
		} catch (err) {
			toasts.error(err instanceof Error ? err.message : $t('webhookPanel.signingIssueFailed'));
		} finally {
			signingBusy = false;
		}
	}

	async function handleDisableSigning() {
		if (!disableSigning) return;
		confirmDisableSigning = false;
		signingBusy = true;
		try {
			await disableSigning();
			hasSigningSecret = false;
			requireSignature = false;
			issuedSigningSecret = '';
			toasts.success($t('webhookPanel.signingDisabled'));
		} catch (err) {
			toasts.error(err instanceof Error ? err.message : $t('webhookPanel.signingDisableFailed'));
		} finally {
			signingBusy = false;
		}
	}

	async function handleToggleRequire(next: boolean) {
		if (!setRequireSignature) return;
		// Optimistic UI; revert on error.
		const previous = requireSignature;
		requireSignature = next;
		try {
			await setRequireSignature(next);
		} catch (err) {
			requireSignature = previous;
			toasts.error(err instanceof Error ? err.message : $t('webhookPanel.signingRequireFailed'));
		}
	}

	onMount(() => {
		load();
	});
</script>

<div class="rounded-xl border border-[var(--border-primary)] bg-[var(--surface-card)] p-6 shadow-[var(--shadow-sm)]">
	<h2 class="mb-1 text-lg font-semibold text-[var(--text-primary)]">{title}</h2>
	<p class="mb-4 text-sm text-[var(--text-secondary)]">{description}</p>

	{#if loading}
		<div class="h-11 rounded-lg bg-[var(--surface-card-hover)]"></div>
	{:else if relativeUrl}
		<div class="flex items-center gap-3">
			<code class="flex-1 rounded-lg border border-[var(--border-primary)] bg-[var(--surface-card-hover)] px-3 py-2.5 font-mono text-sm text-[var(--text-secondary)] break-all">
				{absoluteUrl}
			</code>
			<button
				type="button"
				onclick={handleCopy}
				class="inline-flex items-center gap-1.5 rounded-lg border border-[var(--border-primary)] px-3 py-2.5 text-sm font-medium text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)] transition-colors"
			>
				<IconCopy size={16} />
				{$t('webhookPanel.copy')}
			</button>
		</div>
	{:else}
		<p class="text-sm text-[var(--text-tertiary)] italic">{$t('webhookPanel.noUrl')}</p>
	{/if}

	<div class="mt-4">
		{#if confirmOpen}
			<div class="flex items-center gap-2">
				<span class="text-sm text-[var(--text-secondary)]">{$t('webhookPanel.confirmRegenerate')}</span>
				<button
					type="button"
					onclick={handleRegenerate}
					disabled={regenerating}
					class="inline-flex items-center gap-2 rounded-lg bg-[var(--color-danger)] px-3 py-1.5 text-sm font-medium text-white hover:opacity-90 disabled:opacity-50 active:animate-press"
				>
					{#if regenerating}<IconLoader size={14} />{/if}
					{$t('webhookPanel.confirmYes')}
				</button>
				<button
					type="button"
					onclick={() => (confirmOpen = false)}
					class="inline-flex items-center rounded-lg border border-[var(--border-primary)] px-3 py-1.5 text-sm font-medium text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)]"
				>
					{$t('webhookPanel.confirmNo')}
				</button>
			</div>
		{:else}
			<button
				type="button"
				onclick={() => (confirmOpen = true)}
				disabled={regenerating || loading}
				class="inline-flex items-center gap-2 rounded-lg border border-[var(--color-danger)] px-4 py-2 text-sm font-medium text-[var(--color-danger)] hover:bg-[var(--color-danger-light)] transition-colors disabled:opacity-50 active:animate-press"
			>
				<IconRefresh size={16} />
				{$t('webhookPanel.regenerate')}
			</button>
			<p class="mt-1 text-xs text-[var(--text-tertiary)]">{$t('webhookPanel.regenerateWarning')}</p>
		{/if}
	</div>

	<!-- HMAC signing section (rendered only if the parent wired the handlers). -->
	{#if regenerateSigningSecret && setRequireSignature && disableSigning}
		<div class="mt-6 border-t border-[var(--border-primary)] pt-4">
			<div class="flex items-start gap-2">
				<IconShield size={18} />
				<div class="flex-1">
					<h3 class="text-sm font-semibold text-[var(--text-primary)]">{$t('webhookPanel.signingTitle')}</h3>
					<p class="mt-1 text-xs text-[var(--text-secondary)]">{$t('webhookPanel.signingDesc')}</p>
				</div>
			</div>

			{#if issuedSigningSecret}
				<!-- One-shot display of the freshly issued secret. -->
				<div class="mt-3 rounded-lg border border-amber-300 bg-amber-50 p-3 dark:border-amber-700 dark:bg-amber-950/30">
					<p class="text-xs font-medium text-amber-900 dark:text-amber-200">{$t('webhookPanel.signingShownOnce')}</p>
					<div class="mt-2 flex items-center gap-2">
						<code class="flex-1 break-all rounded bg-[var(--surface-card)] px-2 py-1.5 font-mono text-xs text-[var(--text-primary)]">
							{issuedSigningSecret}
						</code>
						<button
							type="button"
							onclick={copyIssuedSecret}
							class="inline-flex items-center gap-1 rounded-lg border border-[var(--border-primary)] px-2.5 py-1.5 text-xs font-medium text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)] transition-colors"
						>
							<IconCopy size={12} />
							{$t('webhookPanel.copy')}
						</button>
						<button
							type="button"
							onclick={() => (issuedSigningSecret = '')}
							title={$t('webhookPanel.signingDismiss')}
							aria-label={$t('webhookPanel.signingDismiss')}
							class="rounded-lg p-1.5 text-[var(--text-tertiary)] hover:bg-[var(--surface-card-hover)]"
						>
							<IconX size={14} />
						</button>
					</div>
					<p class="mt-2 text-xs text-amber-900 dark:text-amber-200">
						{$t('webhookPanel.signingHint', { header: 'X-Hub-Signature-256' })}
					</p>
				</div>
			{/if}

			<div class="mt-3 flex items-center justify-between gap-3">
				<div class="text-sm text-[var(--text-secondary)]">
					{#if hasSigningSecret}
						{$t('webhookPanel.signingActive')}
					{:else}
						{$t('webhookPanel.signingInactive')}
					{/if}
				</div>
				<div class="flex items-center gap-2">
					<button
						type="button"
						onclick={handleIssueSigning}
						disabled={signingBusy || loading}
						class="inline-flex items-center gap-1.5 rounded-lg border border-[var(--border-primary)] px-3 py-1.5 text-sm font-medium text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)] disabled:opacity-50 transition-colors"
					>
						{#if signingBusy}<IconLoader size={14} />{/if}
						{hasSigningSecret ? $t('webhookPanel.signingRotate') : $t('webhookPanel.signingIssue')}
					</button>
					{#if hasSigningSecret}
						{#if confirmDisableSigning}
							<button
								type="button"
								onclick={handleDisableSigning}
								disabled={signingBusy}
								class="inline-flex items-center gap-1.5 rounded-lg bg-[var(--color-danger)] px-3 py-1.5 text-sm font-medium text-white hover:opacity-90 disabled:opacity-50"
							>
								{$t('webhookPanel.signingDisableConfirm')}
							</button>
							<button
								type="button"
								onclick={() => (confirmDisableSigning = false)}
								class="rounded-lg border border-[var(--border-primary)] px-3 py-1.5 text-sm text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)]"
							>
								{$t('webhookPanel.confirmNo')}
							</button>
						{:else}
							<button
								type="button"
								onclick={() => (confirmDisableSigning = true)}
								disabled={signingBusy}
								class="inline-flex items-center gap-1 rounded-lg border border-[var(--color-danger)] px-3 py-1.5 text-sm font-medium text-[var(--color-danger)] hover:bg-[var(--color-danger-light)] disabled:opacity-50 transition-colors"
							>
								{$t('webhookPanel.signingDisable')}
							</button>
						{/if}
					{/if}
				</div>
			</div>

			<div class="mt-3 flex items-center gap-3">
				<ToggleSwitch
					checked={requireSignature}
					disabled={!hasSigningSecret || signingBusy}
					onchange={handleToggleRequire}
					label={$t('webhookPanel.requireSignature')}
				/>
				<div class="flex-1">
					<span class="text-sm font-medium text-[var(--text-primary)]">{$t('webhookPanel.requireSignature')}</span>
					<p class="text-xs text-[var(--text-tertiary)]">{$t('webhookPanel.requireSignatureHelp')}</p>
				</div>
			</div>
		</div>
	{/if}
</div>
