<script lang="ts">
	import { onMount } from 'svelte';
	import { api, fetchAuth } from '$lib/api';
	import { t } from '$lib/i18n';
	import Loading from '$lib/components/Loading.svelte';
	import MdiIcon from '$lib/components/MdiIcon.svelte';
	import ErrorBanner from '$lib/components/ErrorBanner.svelte';
	import Button from '$lib/components/Button.svelte';
	import ConfirmModal from '$lib/components/ConfirmModal.svelte';
	import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';

	import BackupHero from './BackupHero.svelte';
	import PendingStrip from './PendingStrip.svelte';
	import ExportPanel from './ExportPanel.svelte';
	import ImportPanel from './ImportPanel.svelte';
	import ScheduleCassette from './ScheduleCassette.svelte';
	import BackupLedger from './BackupLedger.svelte';

	type SecretsMode = 'exclude' | 'masked' | 'include';
	type ConflictMode = 'skip' | 'rename' | 'overwrite';

	interface BackupFile {
		filename: string;
		size: number;
		created_at?: string | null;
	}

	interface ScheduledSettings {
		backup_scheduled_enabled: string;
		backup_scheduled_interval_hours: string;
		backup_secrets_mode: string;
		backup_retention_count: string;
	}

	interface PendingState {
		pending: boolean;
		uploaded_at?: string | null;
		uploaded_by?: string | null;
		conflict_mode?: string;
		supervised?: boolean;
	}

	const allCategories = [
		'providers', 'telegram_bots', 'matrix_bots', 'email_bots', 'targets',
		'tracking_configs', 'template_configs',
		'command_configs', 'command_template_configs',
		'notification_trackers', 'command_trackers',
		'actions', 'app_settings',
	];

	// --- Export state ---
	let exportSecrets = $state<SecretsMode>('exclude');
	let exporting = $state(false);
	let selectedCategories = $state<Record<string, boolean>>(
		Object.fromEntries(allCategories.map(k => [k, true]))
	);

	// --- Import state ---
	let importFile: File | null = $state(null);
	let importConflict = $state<ConflictMode>('skip');
	let importing = $state(false);
	let validating = $state(false);
	let validationResult: any = $state(null);
	let importResult: any = $state(null);
	let confirmImportOpen = $state(false);
	let confirmExportOpen = $state(false);

	// --- Scheduled backup state ---
	let loaded = $state(false);
	let error = $state('');
	let scheduledSettings = $state<ScheduledSettings>({
		backup_scheduled_enabled: 'false',
		backup_scheduled_interval_hours: '24',
		backup_secrets_mode: 'exclude',
		backup_retention_count: '5',
	});
	let savingSchedule = $state(false);

	// --- Backup files ---
	let backupFiles = $state<BackupFile[]>([]);
	let loadingFiles = $state(false);
	let confirmDeleteFile = $state('');
	let creatingBackup = $state(false);

	// --- Pending restore state ---
	let pending = $state<PendingState | null>(null);
	let postRestoreModalOpen = $state(false);
	let restartingOverlay = $state(false);

	onMount(async () => {
		try {
			const [settings, files, p] = await Promise.all([
				api<ScheduledSettings>('/backup/scheduled'),
				api<BackupFile[]>('/backup/files'),
				api<PendingState>('/backup/pending-restore'),
			]);
			scheduledSettings = settings;
			backupFiles = files;
			pending = p;
		} catch (err: any) {
			error = err.message;
			snackError(err.message);
		} finally {
			loaded = true;
		}
	});

	async function cancelPending(): Promise<void> {
		try {
			await api('/backup/pending-restore', { method: 'DELETE' });
			snackSuccess(t('backup.pendingCancelled'));
			pending = null;
		} catch (err: any) { snackError(err.message); }
	}

	async function applyAndRestart(): Promise<void> {
		try {
			await api('/backup/apply-restart', { method: 'POST' });
			restartingOverlay = true;
			const startedAt = Date.now();
			let attempts = 0;
			const poll = async (): Promise<void> => {
				attempts += 1;
				try {
					const res = await fetch('/api/health');
					if (res.ok && Date.now() - startedAt > 2000) {
						window.location.reload();
						return;
					}
				} catch { /* still down */ }
				if (attempts < 120) setTimeout(poll, 1000);
			};
			setTimeout(poll, 1500);
		} catch (err: any) {
			restartingOverlay = false;
			snackError(err.message);
		}
	}

	async function createManualBackup(): Promise<void> {
		creatingBackup = true;
		try {
			const mode = scheduledSettings.backup_secrets_mode || 'exclude';
			await api(`/backup/files?secrets_mode=${mode}`, { method: 'POST' });
			snackSuccess(t('backup.manualCreated'));
			await refreshFiles();
		} catch (err: any) {
			snackError(err.message);
		} finally {
			creatingBackup = false;
		}
	}

	// --- Export ---
	async function doExport(): Promise<void> {
		if (exportSecrets === 'include') {
			confirmExportOpen = true;
			return;
		}
		await performExport();
	}

	async function performExport(): Promise<void> {
		confirmExportOpen = false;
		exporting = true;
		try {
			const cats = Object.entries(selectedCategories)
				.filter(([_, v]) => v)
				.map(([k]) => k)
				.join(',');
			const data = await api(`/backup/export?secrets_mode=${exportSecrets}&categories=${cats}`);
			const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
			const url = URL.createObjectURL(blob);
			const a = document.createElement('a');
			a.href = url;
			const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
			a.download = `notify-bridge-backup-${ts}.json`;
			a.click();
			URL.revokeObjectURL(url);
			snackSuccess(t('backup.exportSuccess'));
		} catch (err: any) {
			snackError(err.message);
		} finally {
			exporting = false;
		}
	}

	// --- Validate / Import ---
	function handleFileSelect(file: File | null): void {
		importFile = file;
		validationResult = null;
		importResult = null;
	}

	async function validateFile(): Promise<void> {
		if (!importFile) return;
		validating = true;
		validationResult = null;
		importResult = null;
		try {
			const formData = new FormData();
			formData.append('file', importFile);
			const res = await fetchAuth('/backup/validate', { method: 'POST', body: formData });
			validationResult = await res.json();
		} catch (err: any) {
			snackError(err.message);
		} finally {
			validating = false;
		}
	}

	function doImport(): void {
		confirmImportOpen = true;
	}

	async function performImport(): Promise<void> {
		confirmImportOpen = false;
		if (!importFile) return;
		importing = true;
		importResult = null;
		try {
			const formData = new FormData();
			formData.append('file', importFile);
			const res = await fetchAuth(`/backup/prepare-restore?conflict_mode=${importConflict}`, {
				method: 'POST',
				body: formData,
			});
			importResult = await res.json();
			pending = importResult;
			snackSuccess(t('backup.restorePrepared'));
			postRestoreModalOpen = true;
			importFile = null;
		} catch (err: any) {
			snackError(err.message);
		} finally {
			importing = false;
		}
	}

	// --- Scheduled settings ---
	async function saveSchedule(): Promise<void> {
		savingSchedule = true;
		try {
			scheduledSettings = await api<ScheduledSettings>('/backup/scheduled', {
				method: 'PUT',
				body: JSON.stringify(scheduledSettings),
			});
			snackSuccess(t('backup.scheduleSaved'));
		} catch (err: any) {
			snackError(err.message);
		} finally {
			savingSchedule = false;
		}
	}

	// --- File management ---
	async function refreshFiles(): Promise<void> {
		loadingFiles = true;
		try {
			backupFiles = await api<BackupFile[]>('/backup/files');
		} catch (err: any) {
			snackError(err.message);
		} finally {
			loadingFiles = false;
		}
	}

	async function downloadFile(filename: string): Promise<void> {
		try {
			const data = await api(`/backup/files/${filename}`);
			const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
			const url = URL.createObjectURL(blob);
			const a = document.createElement('a');
			a.href = url;
			a.download = filename;
			a.click();
			URL.revokeObjectURL(url);
		} catch (err: any) {
			snackError(err.message);
		}
	}

	async function deleteFile(filename: string): Promise<void> {
		try {
			await api(`/backup/files/${filename}`, { method: 'DELETE' });
			snackSuccess(t('backup.fileDeleted'));
			confirmDeleteFile = '';
			await refreshFiles();
		} catch (err: any) {
			snackError(err.message);
		}
	}
</script>

<BackupHero files={backupFiles} scheduled={scheduledSettings} {pending} />

{#if !loaded}
	<Loading />
{:else}
	<ErrorBanner message={error} />

	<PendingStrip {pending} onApply={applyAndRestart} onCancel={cancelPending} />

	<div class="backup-page stagger-children">
		<div class="action-deck">
			<ExportPanel
				{selectedCategories}
				{exportSecrets}
				{exporting}
				onCategoriesChange={(next) => selectedCategories = next}
				onSecretsChange={(next) => exportSecrets = next}
				onExport={doExport}
			/>
			<ImportPanel
				{importFile}
				{importConflict}
				{validating}
				{validationResult}
				{importing}
				{importResult}
				onFileSelect={handleFileSelect}
				onConflictChange={(mode) => importConflict = mode}
				onValidate={validateFile}
				onImport={doImport}
			/>
		</div>

		<ScheduleCassette
			enabled={scheduledSettings.backup_scheduled_enabled === 'true'}
			bind:intervalHours={scheduledSettings.backup_scheduled_interval_hours}
			bind:secretsMode={scheduledSettings.backup_secrets_mode}
			bind:retentionCount={scheduledSettings.backup_retention_count}
			saving={savingSchedule}
			onToggle={() => scheduledSettings.backup_scheduled_enabled =
				scheduledSettings.backup_scheduled_enabled === 'true' ? 'false' : 'true'}
			onSave={saveSchedule}
		/>

		<BackupLedger
			files={backupFiles}
			loading={loadingFiles}
			creating={creatingBackup}
			onCreate={createManualBackup}
			onRefresh={refreshFiles}
			onDownload={downloadFile}
			onDelete={(filename) => confirmDeleteFile = filename}
		/>
	</div>
{/if}

<!-- Confirm plaintext export -->
<ConfirmModal
	open={confirmExportOpen}
	title={t('backup.confirmExportTitle')}
	message={t('backup.confirmExportMessage')}
	confirmLabel={t('backup.exportBtn')}
	confirmIcon="mdiDownload"
	onconfirm={performExport}
	oncancel={() => confirmExportOpen = false}
/>

<!-- Confirm import -->
<ConfirmModal
	open={confirmImportOpen}
	title={t('backup.confirmImportTitle')}
	message={t('backup.confirmImportMessage')}
	confirmLabel={t('backup.importBtn')}
	confirmIcon="mdiUpload"
	onconfirm={performImport}
	oncancel={() => confirmImportOpen = false}
/>

<!-- Confirm delete file -->
<ConfirmModal
	open={!!confirmDeleteFile}
	title={t('common.delete')}
	message={confirmDeleteFile}
	onconfirm={() => deleteFile(confirmDeleteFile)}
	oncancel={() => confirmDeleteFile = ''}
/>

<!-- Post-restore modal: Apply now or later -->
<svelte:window onkeydown={postRestoreModalOpen ? (e) => { if (e.key === 'Escape') postRestoreModalOpen = false; } : undefined} />
{#if postRestoreModalOpen && pending?.pending}
	<div class="post-restore-backdrop"
		onclick={() => postRestoreModalOpen = false}
		onkeydown={(e) => { if (e.key === 'Escape') postRestoreModalOpen = false; }}
		role="presentation">
		<!-- svelte-ignore a11y_click_events_have_key_events -->
		<div role="dialog" aria-modal="true" aria-labelledby="post-restore-title" tabindex="-1"
			class="post-restore-card"
			onclick={(e) => e.stopPropagation()}>
			<div class="post-restore-head">
				<div class="post-restore-icon">
					<MdiIcon name="mdiClockAlert" size={22} />
				</div>
				<div class="post-restore-text">
					<h3 id="post-restore-title">{t('backup.restorePrepared')}</h3>
					<p>{t('backup.restoreApplyPrompt')}</p>
				</div>
			</div>
			<div class="post-restore-actions">
				<button class="post-restore-later" type="button"
					onclick={() => postRestoreModalOpen = false}>
					{t('backup.applyLater')}
				</button>
				{#if pending.supervised}
					<Button size="sm" onclick={() => { postRestoreModalOpen = false; applyAndRestart(); }}>
						<MdiIcon name="mdiRestart" size={14} /> {t('backup.restartNow')}
					</Button>
				{/if}
			</div>
		</div>
	</div>
{/if}

<!-- Restarting overlay -->
{#if restartingOverlay}
	<div class="restart-overlay" role="alert" aria-live="assertive">
		<div class="restart-card">
			<div class="restart-spinner">
				<MdiIcon name="mdiRestart" size={40} />
			</div>
			<p class="restart-title">{t('backup.restartingTitle')}</p>
			<p class="restart-sub">{t('backup.restartingDescription')}</p>
		</div>
	</div>
{/if}

<style>
	.backup-page {
		display: flex;
		flex-direction: column;
		gap: 1.5rem;
	}

	.action-deck {
		display: grid;
		grid-template-columns: 1fr;
		gap: 1.25rem;
		align-items: stretch;
	}
	@media (min-width: 960px) {
		.action-deck { grid-template-columns: 1fr 1fr; }
	}

	/* Post-restore modal */
	.post-restore-backdrop {
		position: fixed;
		inset: 0;
		z-index: 9999;
		background: rgba(0, 0, 0, 0.55);
		backdrop-filter: blur(4px);
		-webkit-backdrop-filter: blur(4px);
		display: flex;
		align-items: center;
		justify-content: center;
		padding: 1rem;
	}
	.post-restore-card {
		background: var(--color-glass-elev);
		backdrop-filter: blur(28px) saturate(160%);
		-webkit-backdrop-filter: blur(28px) saturate(160%);
		border: 1px solid var(--color-rule-strong);
		border-radius: 22px;
		padding: 1.5rem;
		max-width: 440px;
		width: 100%;
		box-shadow: 0 30px 70px -16px rgba(0, 0, 0, 0.6);
	}
	.post-restore-head {
		display: flex;
		align-items: flex-start;
		gap: 0.85rem;
		margin-bottom: 1.1rem;
	}
	.post-restore-icon {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		width: 42px; height: 42px;
		border-radius: 50%;
		background: var(--color-warning-bg);
		color: var(--color-warning-fg);
		flex-shrink: 0;
	}
	.post-restore-text { min-width: 0; }
	.post-restore-text h3 {
		font-family: var(--font-display);
		font-style: italic;
		font-weight: 500;
		font-size: 1.15rem;
		margin: 0 0 0.25rem;
		letter-spacing: -0.015em;
	}
	.post-restore-text p {
		font-size: 0.82rem;
		color: var(--color-muted-foreground);
		margin: 0;
		line-height: 1.45;
		word-wrap: break-word;
	}
	.post-restore-actions {
		display: flex;
		gap: 0.5rem;
		justify-content: flex-end;
		flex-wrap: wrap;
	}
	.post-restore-later {
		padding: 0 0.95rem;
		height: 34px;
		border-radius: 12px;
		background: transparent;
		border: 1px solid var(--color-border);
		color: var(--color-muted-foreground);
		cursor: pointer;
		font-family: inherit;
		font-size: 0.82rem;
		transition: background 0.15s, color 0.15s, border-color 0.15s;
	}
	.post-restore-later:hover {
		background: var(--color-glass-strong);
		color: var(--color-foreground);
		border-color: var(--color-rule-strong);
	}

	/* Restarting overlay */
	.restart-overlay {
		position: fixed;
		inset: 0;
		z-index: 9999;
		background: rgba(0, 0, 0, 0.72);
		backdrop-filter: blur(6px);
		-webkit-backdrop-filter: blur(6px);
		display: flex;
		align-items: center;
		justify-content: center;
		padding: 1rem;
	}
	.restart-card {
		text-align: center;
		padding: 1.6rem 2rem;
		color: var(--color-foreground);
	}
	.restart-spinner {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		width: 40px;
		height: 40px;
		margin-bottom: 0.85rem;
		color: var(--color-primary);
		animation: restart-spin 1.2s linear infinite;
		transform-origin: center center;
	}
	.restart-title {
		font-family: var(--font-display);
		font-style: italic;
		font-size: 1.2rem;
		font-weight: 500;
		margin: 0;
		letter-spacing: -0.015em;
	}
	.restart-sub {
		font-size: 0.8rem;
		color: var(--color-muted-foreground);
		margin: 0.4rem 0 0;
	}
	@keyframes restart-spin {
		from { transform: rotate(0deg); }
		to { transform: rotate(360deg); }
	}

	@media (prefers-reduced-motion: reduce) {
		.restart-spinner { animation: none !important; }
	}
</style>
