/**
 * <import-export-dialog> — Modal dialog for importing, exporting, and syncing notes.
 *
 * Import sources: Files (direct), Obsidian, Logseq, Notion, Google Docs, Evernote, Roam Research.
 * Export targets: Obsidian, Logseq, Notion, Google Docs.
 * Sync: Re-fetch from API sources (Notion/Google) or re-upload vault ZIPs for file-based.
 *
 * File-based sources (Obsidian/Logseq/Evernote/Roam) use file upload.
 * API-based sources (Notion/Google) use OAuth connections.
 */

import { getModuleApiBase } from "../../../shared/url-helpers";

interface NotebookOption {
	id: string;
	title: string;
}

interface ConnectionStatus {
	notion: { connected: boolean; workspaceName?: string };
	google: { connected: boolean; email?: string };
	logseq: { connected: boolean };
	obsidian: { connected: boolean };
}

interface RemotePage {
	id: string;
	title: string;
	lastEdited?: string;
	lastModified?: string;
	icon?: string;
}

class ImportExportDialog extends HTMLElement {
	private shadow!: ShadowRoot;
	private space = '';
	private activeTab: 'import' | 'export' | 'sync' = 'import';
	private activeSource: 'files' | 'obsidian' | 'logseq' | 'notion' | 'google-docs' | 'evernote' | 'roam' = 'files';
	private notebooks: NotebookOption[] = [];
	private connections: ConnectionStatus & { evernote?: { connected: boolean }; roam?: { connected: boolean } } = {
		notion: { connected: false },
		google: { connected: false },
		logseq: { connected: true },
		obsidian: { connected: true },
	};
	private selectedFiles: File[] = [];
	private remotePages: RemotePage[] = [];
	private selectedPages = new Set<string>();
	private importing = false;
	private exporting = false;
	private syncing = false;
	private syncStatuses: Record<string, { source: string; syncStatus?: string; lastSyncedAt?: number; hasConflict: boolean }> = {};
	private statusMessage = '';
	private statusType: 'info' | 'success' | 'error' = 'info';
	private selectedFile: File | null = null;
	private targetNotebookId = '';

	constructor() {
		super();
		this.shadow = this.attachShadow({ mode: 'open' });
	}

	connectedCallback() {
		this.space = this.getAttribute('space') || 'demo';
		this.render();
		this.loadConnections();
	}

	/** Open the dialog. */
	open(notebooks: NotebookOption[], tab: 'import' | 'export' | 'sync' = 'import') {
		this.notebooks = notebooks;
		this.activeTab = tab;
		this.statusMessage = '';
		this.selectedPages.clear();
		this.selectedFile = null;
		this.selectedFiles = [];
		this.syncStatuses = {};
		this.render();
		(this.shadow.querySelector('.dialog-overlay') as HTMLElement)?.classList.add('open');
	}

	/** Close the dialog. */
	close() {
		(this.shadow.querySelector('.dialog-overlay') as HTMLElement)?.classList.remove('open');
	}

	private async loadConnections() {
		try {
			const res = await fetch(`${getModuleApiBase("rdocs")}/api/connections`);
			if (res.ok) {
				this.connections = await res.json();
			}
		} catch { /* ignore */ }
	}

	private async loadRemotePages() {
		this.remotePages = [];
		this.selectedPages.clear();

		if (this.activeSource === 'notion') {
			try {
				const res = await fetch(`${getModuleApiBase("rdocs")}/api/import/notion/pages`);
				if (res.ok) {
					const data = await res.json();
					this.remotePages = data.pages || [];
				}
			} catch { /* ignore */ }
		} else if (this.activeSource === 'google-docs') {
			try {
				const res = await fetch(`${getModuleApiBase("rdocs")}/api/import/google-docs/list`);
				if (res.ok) {
					const data = await res.json();
					this.remotePages = data.docs || [];
				}
			} catch { /* ignore */ }
		}

		this.render();
		(this.shadow.querySelector('.dialog-overlay') as HTMLElement)?.classList.add('open');
	}

	private setStatus(msg: string, type: 'info' | 'success' | 'error' = 'info') {
		this.statusMessage = msg;
		this.statusType = type;
		const statusEl = this.shadow.querySelector('.status-message') as HTMLElement;
		if (statusEl) {
			statusEl.textContent = msg;
			statusEl.className = `status-message status-${type}`;
			statusEl.style.display = msg ? 'block' : 'none';
		}
	}

	private async handleImport() {
		this.importing = true;
		this.setStatus('Importing...', 'info');

		try {
			if (this.activeSource === 'files') {
				if (this.selectedFiles.length === 0) {
					this.setStatus('Please select at least one file', 'error');
					this.importing = false;
					return;
				}

				const formData = new FormData();
				for (const f of this.selectedFiles) formData.append('files', f);
				if (this.targetNotebookId) formData.append('notebookId', this.targetNotebookId);

				const token = localStorage.getItem('encryptid_token') || '';
				const res = await fetch(`${getModuleApiBase("rdocs")}/api/import/files`, {
					method: 'POST',
					headers: { 'Authorization': `Bearer ${token}` },
					body: formData,
				});

				const data = await res.json();
				if (data.ok) {
					this.setStatus(`Imported ${data.imported} note${data.imported !== 1 ? 's' : ''}${data.warnings?.length ? ` (${data.warnings.length} warnings)` : ''}`, 'success');
					this.dispatchEvent(new CustomEvent('import-complete', { detail: data }));
				} else {
					this.setStatus(data.error || 'Import failed', 'error');
				}
			} else if (this.activeSource === 'obsidian' || this.activeSource === 'logseq' || this.activeSource === 'evernote' || this.activeSource === 'roam') {
				if (!this.selectedFile) {
					const fileTypeHint: Record<string, string> = {
						obsidian: 'ZIP file', logseq: 'ZIP file',
						evernote: '.enex file', roam: 'JSON file',
					};
					this.setStatus(`Please select a ${fileTypeHint[this.activeSource] || 'file'}`, 'error');
					this.importing = false;
					return;
				}

				const formData = new FormData();
				formData.append('file', this.selectedFile);
				formData.append('source', this.activeSource);
				if (this.targetNotebookId) {
					formData.append('notebookId', this.targetNotebookId);
				}

				const token = localStorage.getItem('encryptid_token') || '';
				const res = await fetch(`${getModuleApiBase("rdocs")}/api/import/upload`, {
					method: 'POST',
					headers: { 'Authorization': `Bearer ${token}` },
					body: formData,
				});

				const data = await res.json();
				if (data.ok) {
					this.setStatus(
						`Imported ${data.imported} notes${data.updated ? `, updated ${data.updated}` : ''}${data.warnings?.length ? ` (${data.warnings.length} warnings)` : ''}`,
						'success'
					);
					this.dispatchEvent(new CustomEvent('import-complete', { detail: data }));
				} else {
					this.setStatus(data.error || 'Import failed', 'error');
				}
			} else if (this.activeSource === 'notion') {
				if (this.selectedPages.size === 0) {
					this.setStatus('Please select at least one page', 'error');
					this.importing = false;
					return;
				}

				const token = localStorage.getItem('encryptid_token') || '';
				const res = await fetch(`${getModuleApiBase("rdocs")}/api/import/notion`, {
					method: 'POST',
					headers: {
						'Authorization': `Bearer ${token}`,
						'Content-Type': 'application/json',
					},
					body: JSON.stringify({
						pageIds: Array.from(this.selectedPages),
						notebookId: this.targetNotebookId || undefined,
					}),
				});

				const data = await res.json();
				if (data.ok) {
					this.setStatus(`Imported ${data.imported} notes from Notion`, 'success');
					this.dispatchEvent(new CustomEvent('import-complete', { detail: data }));
				} else {
					this.setStatus(data.error || 'Notion import failed', 'error');
				}
			} else if (this.activeSource === 'google-docs') {
				if (this.selectedPages.size === 0) {
					this.setStatus('Please select at least one document', 'error');
					this.importing = false;
					return;
				}

				const token = localStorage.getItem('encryptid_token') || '';
				const res = await fetch(`${getModuleApiBase("rdocs")}/api/import/google-docs`, {
					method: 'POST',
					headers: {
						'Authorization': `Bearer ${token}`,
						'Content-Type': 'application/json',
					},
					body: JSON.stringify({
						docIds: Array.from(this.selectedPages),
						notebookId: this.targetNotebookId || undefined,
					}),
				});

				const data = await res.json();
				if (data.ok) {
					this.setStatus(`Imported ${data.imported} notes from Google Docs`, 'success');
					this.dispatchEvent(new CustomEvent('import-complete', { detail: data }));
				} else {
					this.setStatus(data.error || 'Google Docs import failed', 'error');
				}
			}
		} catch (err) {
			this.setStatus(`Import error: ${(err as Error).message}`, 'error');
		}

		this.importing = false;
	}

	private async handleExport() {
		if (!this.targetNotebookId) {
			this.setStatus('Please select a notebook to export', 'error');
			return;
		}

		this.exporting = true;
		this.setStatus('Exporting...', 'info');

		try {
			if (this.activeSource === 'obsidian' || this.activeSource === 'logseq' || this.activeSource === 'markdown' as any) {
				const format = this.activeSource === 'markdown' as any ? 'markdown' : this.activeSource;
				const url = `${getModuleApiBase("rdocs")}/api/export/${format}?notebookId=${encodeURIComponent(this.targetNotebookId)}`;
				const res = await fetch(url);

				if (res.ok) {
					const blob = await res.blob();
					const disposition = res.headers.get('Content-Disposition') || '';
					const filename = disposition.match(/filename="(.+)"/)?.[1] || `export-${format}.zip`;

					const a = document.createElement('a');
					a.href = URL.createObjectURL(blob);
					a.download = filename;
					a.click();
					URL.revokeObjectURL(a.href);

					this.setStatus('Download started!', 'success');
				} else {
					const data = await res.json();
					this.setStatus(data.error || 'Export failed', 'error');
				}
			} else if (this.activeSource === 'notion') {
				const token = localStorage.getItem('encryptid_token') || '';
				const res = await fetch(`${getModuleApiBase("rdocs")}/api/export/notion`, {
					method: 'POST',
					headers: {
						'Authorization': `Bearer ${token}`,
						'Content-Type': 'application/json',
					},
					body: JSON.stringify({ notebookId: this.targetNotebookId }),
				});

				const data = await res.json();
				if (data.exported) {
					this.setStatus(`Exported ${data.exported.length} notes to Notion`, 'success');
				} else {
					this.setStatus(data.error || 'Notion export failed', 'error');
				}
			} else if (this.activeSource === 'google-docs') {
				const token = localStorage.getItem('encryptid_token') || '';
				const res = await fetch(`${getModuleApiBase("rdocs")}/api/export/google-docs`, {
					method: 'POST',
					headers: {
						'Authorization': `Bearer ${token}`,
						'Content-Type': 'application/json',
					},
					body: JSON.stringify({ notebookId: this.targetNotebookId }),
				});

				const data = await res.json();
				if (data.exported) {
					this.setStatus(`Exported ${data.exported.length} notes to Google Docs`, 'success');
				} else {
					this.setStatus(data.error || 'Google Docs export failed', 'error');
				}
			}
		} catch (err) {
			this.setStatus(`Export error: ${(err as Error).message}`, 'error');
		}

		this.exporting = false;
	}

	private esc(s: string): string {
		const d = document.createElement('div');
		d.textContent = s || '';
		return d.innerHTML;
	}

	private render() {
		const isApiSource = this.activeSource === 'notion' || this.activeSource === 'google-docs';
		const isFileSource = this.activeSource === 'files';
		// File-based sources (files, obsidian, logseq, evernote, roam) are always "connected" — no auth needed
		const fileBased = ['files', 'obsidian', 'logseq', 'evernote', 'roam'];
		const sourceConnKey = this.activeSource === 'google-docs' ? 'google' : this.activeSource;
		const isConnected = fileBased.includes(this.activeSource) || (this.connections as any)[sourceConnKey]?.connected || false;

		this.shadow.innerHTML = `
		<style>${this.getStyles()}</style>
		<div class="dialog-overlay">
			<div class="dialog">
				<div class="dialog-header">
					<h2>${this.activeTab === 'sync' ? 'Sync' : this.activeTab === 'export' ? 'Export' : 'Import'} Notes</h2>
					<button class="dialog-close" id="btn-close">&times;</button>
				</div>

				<div class="tab-bar">
					<button class="tab ${this.activeTab === 'import' ? 'active' : ''}" data-tab="import">Import</button>
					<button class="tab ${this.activeTab === 'export' ? 'active' : ''}" data-tab="export">Export</button>
					<button class="tab ${this.activeTab === 'sync' ? 'active' : ''}" data-tab="sync">Sync</button>
				</div>

				${this.activeTab !== 'sync' ? (() => {
					const sources = this.activeTab === 'export'
						? (['obsidian', 'logseq', 'notion', 'google-docs'] as const)
						: (['files', 'obsidian', 'logseq', 'notion', 'google-docs', 'evernote', 'roam'] as const);
					return `<div class="source-bar">
					${sources.map(s => `
						<button class="source-btn ${this.activeSource === s ? 'active' : ''}" data-source="${s}">
							${this.sourceIcon(s)} ${this.sourceName(s)}
						</button>
					`).join('')}
				</div>`;
				})() : ''}

				<div class="dialog-body">
					${this.activeTab === 'sync' ? this.renderSyncTab() : this.activeTab === 'import' ? this.renderImportTab(isApiSource, isConnected) : this.renderExportTab(isApiSource, isConnected)}

					<div class="status-message ${this.statusMessage ? `status-${this.statusType}` : ''}" style="display:${this.statusMessage ? 'block' : 'none'}">
						${this.esc(this.statusMessage)}
					</div>
				</div>
			</div>
		</div>`;

		this.attachListeners();
	}

	private renderImportTab(isApiSource: boolean, isConnected: boolean): string {
		if (isApiSource && !isConnected) {
			return `
				<div class="connect-prompt">
					<p>Connect your ${this.sourceName(this.activeSource)} account to import notes.</p>
					<button class="btn-primary" id="btn-connect">Connect ${this.sourceName(this.activeSource)}</button>
				</div>`;
		}

		if (isApiSource) {
			// Show page list for selection
			return `
				<div class="page-list-header">
					<span>Select pages to import:</span>
					<button class="btn-secondary btn-sm" id="btn-refresh-pages">Refresh</button>
				</div>
				<div class="page-list">
					${this.remotePages.length === 0
						? '<div class="empty-list">No pages found. Click Refresh to load.</div>'
						: this.remotePages.map(p => `
							<label class="page-item">
								<input type="checkbox" value="${p.id}" ${this.selectedPages.has(p.id) ? 'checked' : ''}>
								<span class="page-icon">${p.icon || (this.activeSource === 'notion' ? 'N' : 'G')}</span>
								<span class="page-title">${this.esc(p.title)}</span>
							</label>
						`).join('')}
				</div>
				<div class="form-row">
					<label>Target notebook:</label>
					<select id="target-notebook">
						<option value="">Create new notebook</option>
						${this.notebooks.map(nb => `<option value="${nb.id}">${this.esc(nb.title)}</option>`).join('')}
					</select>
				</div>
				<button class="btn-primary" id="btn-import" ${this.importing ? 'disabled' : ''}>
					${this.importing ? 'Importing...' : `Import Selected (${this.selectedPages.size})`}
				</button>`;
		}

		// Generic file import
		if (this.activeSource === 'files') {
			return `
				<div class="upload-area" id="upload-area">
					<p>Drop files to import as notes</p>
					<input type="file" id="file-input" accept=".md,.txt,.html,.htm,.jpg,.jpeg,.png,.gif,.webp,.svg" multiple style="display:none">
					<button class="btn-secondary" id="btn-choose-file">
						${this.selectedFiles.length > 0 ? `${this.selectedFiles.length} file${this.selectedFiles.length > 1 ? 's' : ''} selected` : 'Choose Files'}
					</button>
					<p class="upload-hint">.md .txt .html .jpg .png .webp — drag & drop supported</p>
				</div>
				<div class="form-row">
					<label>Target notebook:</label>
					<select id="target-notebook">
						<option value="">Create new notebook</option>
						${this.notebooks.map(nb => `<option value="${nb.id}">${this.esc(nb.title)}</option>`).join('')}
					</select>
				</div>
				<button class="btn-primary" id="btn-import" ${this.importing || this.selectedFiles.length === 0 ? 'disabled' : ''}>
					${this.importing ? 'Importing...' : `Import ${this.selectedFiles.length} File${this.selectedFiles.length !== 1 ? 's' : ''}`}
				</button>`;
		}

		// File-based source import (Obsidian/Logseq/Evernote/Roam)
		const acceptMap: Record<string, string> = {
			obsidian: '.zip', logseq: '.zip', evernote: '.enex,.zip', roam: '.json,.zip',
		};
		const hintMap: Record<string, string> = {
			obsidian: 'Upload a ZIP of your Obsidian vault',
			logseq: 'Upload a ZIP of your Logseq graph',
			evernote: 'Upload an .enex export file',
			roam: 'Upload a Roam Research JSON export',
		};
		return `
			<div class="upload-area" id="upload-area">
				<p>${hintMap[this.activeSource] || 'Upload a file'}</p>
				<input type="file" id="file-input" accept="${acceptMap[this.activeSource] || '.zip'}" style="display:none">
				<button class="btn-secondary" id="btn-choose-file">
					${this.selectedFile ? this.esc(this.selectedFile.name) : 'Choose File'}
				</button>
				<p class="upload-hint">or drag & drop here</p>
			</div>
			<div class="form-row">
				<label>Target notebook:</label>
				<select id="target-notebook">
					<option value="">Create new notebook</option>
					${this.notebooks.map(nb => `<option value="${nb.id}">${this.esc(nb.title)}</option>`).join('')}
				</select>
			</div>
			<button class="btn-primary" id="btn-import" ${this.importing || !this.selectedFile ? 'disabled' : ''}>
				${this.importing ? 'Importing...' : 'Import'}
			</button>`;
	}

	private renderExportTab(isApiSource: boolean, isConnected: boolean): string {
		if (isApiSource && !isConnected) {
			return `
				<div class="connect-prompt">
					<p>Connect your ${this.sourceName(this.activeSource)} account to export notes.</p>
					<button class="btn-primary" id="btn-connect">Connect ${this.sourceName(this.activeSource)}</button>
				</div>`;
		}

		return `
			<div class="form-row">
				<label>Source notebook:</label>
				<select id="target-notebook">
					<option value="">Select a notebook</option>
					${this.notebooks.map(nb => `<option value="${nb.id}">${this.esc(nb.title)}</option>`).join('')}
				</select>
			</div>
			<button class="btn-primary" id="btn-export" ${this.exporting ? 'disabled' : ''}>
				${this.exporting ? 'Exporting...'
					: isApiSource ? `Export to ${this.sourceName(this.activeSource)}`
					: 'Download ZIP'}
			</button>`;
	}

	private renderSyncTab(): string {
		const statusEntries = Object.entries(this.syncStatuses);
		const hasApiNotes = statusEntries.some(([_, s]) => s.source === 'notion' || s.source === 'google-docs');
		const hasFileNotes = statusEntries.some(([_, s]) => s.source === 'obsidian' || s.source === 'logseq');

		return `
			<div class="form-row">
				<label>Notebook:</label>
				<select id="sync-notebook">
					<option value="">Select a notebook</option>
					${this.notebooks.map(nb => `<option value="${nb.id}" ${this.targetNotebookId === nb.id ? 'selected' : ''}>${this.esc(nb.title)}</option>`).join('')}
				</select>
			</div>

			${this.targetNotebookId ? `
				<div class="sync-summary">
					${statusEntries.length === 0
						? '<p class="sync-empty">No imported notes found in this notebook. Import notes first to enable sync.</p>'
						: `<p class="sync-count">${statusEntries.length} synced note${statusEntries.length !== 1 ? 's' : ''} found</p>
							<div class="sync-list">
								${statusEntries.map(([id, s]) => `
									<div class="sync-item">
										<span class="sync-source-badge ${s.source}">${s.source}</span>
										<span class="sync-status ${s.syncStatus || 'synced'}">${s.hasConflict ? 'conflict' : s.syncStatus || 'synced'}</span>
										${s.lastSyncedAt ? `<span class="sync-time">${this.relativeTime(s.lastSyncedAt)}</span>` : ''}
									</div>
								`).join('')}
							</div>`
					}
				</div>

				${hasApiNotes ? `
					<button class="btn-primary" id="btn-sync-api" ${this.syncing ? 'disabled' : ''}>
						${this.syncing ? 'Syncing...' : 'Sync API Notes (Notion / Google Docs)'}
					</button>
				` : ''}

				${hasFileNotes ? `
					<div class="sync-upload" style="margin-top: 12px;">
						<p style="font-size: 12px; color: var(--rs-text-muted, #888); margin: 0 0 8px;">
							File-based sources require re-uploading your vault ZIP:
						</p>
						<div class="upload-area" id="sync-upload-area" style="padding: 16px;">
							<input type="file" id="sync-file-input" accept=".zip" style="display:none">
							<button class="btn-secondary btn-sm" id="btn-sync-choose-file">Choose ZIP</button>
						</div>
					</div>
				` : ''}
			` : ''}
		`;
	}

	private relativeTime(ts: number): string {
		const diff = Date.now() - ts;
		if (diff < 60000) return 'just now';
		if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
		if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;
		return `${Math.floor(diff / 86400000)}d ago`;
	}

	private async loadSyncStatus(notebookId: string) {
		try {
			const res = await fetch(`${getModuleApiBase("rdocs")}/api/sync/status/${notebookId}`);
			if (res.ok) {
				const data = await res.json();
				this.syncStatuses = data.statuses || {};
			}
		} catch { /* ignore */ }
		this.render();
		(this.shadow.querySelector('.dialog-overlay') as HTMLElement)?.classList.add('open');
	}

	private async handleSyncApi() {
		if (!this.targetNotebookId) return;
		this.syncing = true;
		this.setStatus('Syncing...', 'info');

		try {
			const token = localStorage.getItem('encryptid_token') || '';
			const res = await fetch(`${getModuleApiBase("rdocs")}/api/sync/notebook/${this.targetNotebookId}`, {
				method: 'POST',
				headers: { 'Authorization': `Bearer ${token}` },
			});

			const data = await res.json();
			if (data.ok) {
				const parts: string[] = [];
				if (data.synced > 0) parts.push(`${data.synced} updated`);
				if (data.conflicts > 0) parts.push(`${data.conflicts} conflicts`);
				if (data.errors > 0) parts.push(`${data.errors} errors`);
				this.setStatus(parts.length > 0 ? `Sync complete: ${parts.join(', ')}` : 'All notes up to date', 'success');
				this.dispatchEvent(new CustomEvent('sync-complete', { detail: data }));
				this.loadSyncStatus(this.targetNotebookId);
			} else {
				this.setStatus(data.error || 'Sync failed', 'error');
			}
		} catch (err) {
			this.setStatus(`Sync error: ${(err as Error).message}`, 'error');
		}

		this.syncing = false;
	}

	private async handleSyncUpload(file: File) {
		if (!this.targetNotebookId) return;
		this.syncing = true;
		this.setStatus('Syncing from ZIP...', 'info');

		// Detect source from sync statuses
		const sources = new Set(Object.values(this.syncStatuses).map(s => s.source));
		const source = sources.has('obsidian') ? 'obsidian' : sources.has('logseq') ? 'logseq' : '';
		if (!source) {
			this.setStatus('Could not determine source type', 'error');
			this.syncing = false;
			return;
		}

		try {
			const formData = new FormData();
			formData.append('file', file);
			formData.append('notebookId', this.targetNotebookId);
			formData.append('source', source);

			const token = localStorage.getItem('encryptid_token') || '';
			const res = await fetch(`${getModuleApiBase("rdocs")}/api/sync/upload`, {
				method: 'POST',
				headers: { 'Authorization': `Bearer ${token}` },
				body: formData,
			});

			const data = await res.json();
			if (data.ok) {
				this.setStatus(`Sync: ${data.synced} updated, ${data.conflicts} conflicts`, 'success');
				this.dispatchEvent(new CustomEvent('sync-complete', { detail: data }));
				this.loadSyncStatus(this.targetNotebookId);
			} else {
				this.setStatus(data.error || 'Sync failed', 'error');
			}
		} catch (err) {
			this.setStatus(`Sync error: ${(err as Error).message}`, 'error');
		}

		this.syncing = false;
	}

	private sourceName(s: string): string {
		const names: Record<string, string> = {
			files: 'Files',
			obsidian: 'Obsidian',
			logseq: 'Logseq',
			notion: 'Notion',
			'google-docs': 'Google Docs',
			evernote: 'Evernote',
			roam: 'Roam',
		};
		return names[s] || s;
	}

	private sourceIcon(s: string): string {
		const icons: Record<string, string> = {
			files: '<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M2 2h5l2 2h5v10H2V2z"/></svg>',
			obsidian: '<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M8 1l6 3.5v7L8 15l-6-3.5v-7L8 1z"/></svg>',
			logseq: '<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><circle cx="8" cy="8" r="6"/></svg>',
			notion: '<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><rect x="2" y="2" width="12" height="12" rx="2"/></svg>',
			'google-docs': '<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M4 1h6l4 4v10H4V1z"/></svg>',
			evernote: '<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M8 1C4.7 1 2 3.7 2 7c0 2.2 1.2 4.1 3 5.2V15l3-2 3 2v-2.8c1.8-1.1 3-3 3-5.2 0-3.3-2.7-6-6-6z"/></svg>',
			roam: '<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><circle cx="8" cy="8" r="3"/><circle cx="8" cy="2" r="1.5"/><circle cx="13" cy="11" r="1.5"/><circle cx="3" cy="11" r="1.5"/></svg>',
		};
		return icons[s] || '';
	}

	private attachListeners() {
		// Close button
		this.shadow.getElementById('btn-close')?.addEventListener('click', () => this.close());

		// Overlay click to close
		this.shadow.querySelector('.dialog-overlay')?.addEventListener('click', (e) => {
			if ((e.target as HTMLElement).classList.contains('dialog-overlay')) this.close();
		});

		// Tab switching
		this.shadow.querySelectorAll('.tab').forEach(btn => {
			btn.addEventListener('click', () => {
				this.activeTab = (btn as HTMLElement).dataset.tab as any;
				// Auto-select valid source when switching to export (non-exportable: files, evernote, roam)
				if (this.activeTab === 'export' && ['files', 'evernote', 'roam'].includes(this.activeSource)) {
					this.activeSource = 'obsidian';
				}
				this.statusMessage = '';
				this.render();
				(this.shadow.querySelector('.dialog-overlay') as HTMLElement)?.classList.add('open');
			});
		});

		// Source switching
		this.shadow.querySelectorAll('.source-btn').forEach(btn => {
			btn.addEventListener('click', () => {
				this.activeSource = (btn as HTMLElement).dataset.source as any;
				this.remotePages = [];
				this.selectedPages.clear();
				this.selectedFile = null;
				this.selectedFiles = [];
				this.statusMessage = '';
				this.render();
				(this.shadow.querySelector('.dialog-overlay') as HTMLElement)?.classList.add('open');
			});
		});

		// File input
		const fileInput = this.shadow.getElementById('file-input') as HTMLInputElement;
		const chooseBtn = this.shadow.getElementById('btn-choose-file');
		chooseBtn?.addEventListener('click', () => fileInput?.click());
		fileInput?.addEventListener('change', () => {
			if (this.activeSource === 'files') {
				this.selectedFiles = Array.from(fileInput.files || []);
				if (chooseBtn) chooseBtn.textContent = this.selectedFiles.length > 0 ? `${this.selectedFiles.length} file${this.selectedFiles.length > 1 ? 's' : ''} selected` : 'Choose Files';
				const importBtn = this.shadow.getElementById('btn-import') as HTMLButtonElement;
				if (importBtn) importBtn.disabled = this.selectedFiles.length === 0;
			} else {
				this.selectedFile = fileInput.files?.[0] || null;
				if (chooseBtn) chooseBtn.textContent = this.selectedFile?.name || 'Choose File';
				const importBtn = this.shadow.getElementById('btn-import') as HTMLButtonElement;
				if (importBtn) importBtn.disabled = !this.selectedFile;
			}
		});

		// Drag & drop
		const uploadArea = this.shadow.getElementById('upload-area');
		if (uploadArea) {
			uploadArea.addEventListener('dragover', (e) => { e.preventDefault(); uploadArea.classList.add('dragover'); });
			uploadArea.addEventListener('dragleave', () => uploadArea.classList.remove('dragover'));
			uploadArea.addEventListener('drop', (e) => {
				e.preventDefault();
				uploadArea.classList.remove('dragover');
				const files = (e as DragEvent).dataTransfer?.files;
				if (!files || files.length === 0) return;
				if (this.activeSource === 'files') {
					this.selectedFiles = Array.from(files);
					if (chooseBtn) chooseBtn.textContent = `${this.selectedFiles.length} file${this.selectedFiles.length > 1 ? 's' : ''} selected`;
					const importBtn = this.shadow.getElementById('btn-import') as HTMLButtonElement;
					if (importBtn) importBtn.disabled = false;
				} else {
					const file = files[0];
					this.selectedFile = file;
					if (chooseBtn) chooseBtn.textContent = file.name;
					const importBtn = this.shadow.getElementById('btn-import') as HTMLButtonElement;
					if (importBtn) importBtn.disabled = false;
				}
			});
		}

		// Target notebook select
		const notebookSelect = this.shadow.getElementById('target-notebook') as HTMLSelectElement;
		notebookSelect?.addEventListener('change', () => {
			this.targetNotebookId = notebookSelect.value;
		});

		// Page checkboxes
		this.shadow.querySelectorAll('.page-item input[type="checkbox"]').forEach(cb => {
			cb.addEventListener('change', () => {
				const input = cb as HTMLInputElement;
				if (input.checked) {
					this.selectedPages.add(input.value);
				} else {
					this.selectedPages.delete(input.value);
				}
				const importBtn = this.shadow.getElementById('btn-import');
				if (importBtn) importBtn.textContent = `Import Selected (${this.selectedPages.size})`;
			});
		});

		// Refresh pages
		this.shadow.getElementById('btn-refresh-pages')?.addEventListener('click', () => {
			this.loadRemotePages();
		});

		// Connect button
		this.shadow.getElementById('btn-connect')?.addEventListener('click', () => {
			const provider = this.activeSource === 'google-docs' ? 'google' : this.activeSource;
			window.location.href = `/api/oauth/${provider}/authorize?space=${this.space}`;
		});

		// Import button
		this.shadow.getElementById('btn-import')?.addEventListener('click', () => this.handleImport());

		// Export button
		this.shadow.getElementById('btn-export')?.addEventListener('click', () => this.handleExport());

		// Sync tab listeners
		const syncNotebookSelect = this.shadow.getElementById('sync-notebook') as HTMLSelectElement;
		syncNotebookSelect?.addEventListener('change', () => {
			this.targetNotebookId = syncNotebookSelect.value;
			if (this.targetNotebookId) {
				this.loadSyncStatus(this.targetNotebookId);
			} else {
				this.syncStatuses = {};
				this.render();
				(this.shadow.querySelector('.dialog-overlay') as HTMLElement)?.classList.add('open');
			}
		});

		this.shadow.getElementById('btn-sync-api')?.addEventListener('click', () => this.handleSyncApi());

		const syncFileInput = this.shadow.getElementById('sync-file-input') as HTMLInputElement;
		this.shadow.getElementById('btn-sync-choose-file')?.addEventListener('click', () => syncFileInput?.click());
		syncFileInput?.addEventListener('change', () => {
			const file = syncFileInput.files?.[0];
			if (file) this.handleSyncUpload(file);
		});
	}

	private getStyles(): string {
		return `
			:host { display: block; }

			.dialog-overlay {
				display: none;
				position: fixed; top: 0; left: 0; right: 0; bottom: 0;
				background: rgba(0,0,0,0.5); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
				z-index: 10000; justify-content: center; align-items: center;
			}
			.dialog-overlay.open { display: flex; }

			.dialog {
				background: var(--rs-bg-surface, #1a1a2e);
				border: 1px solid var(--rs-border, #2a2a4a);
				border-radius: 16px; width: 560px; max-width: 95vw;
				max-height: 80vh; display: flex; flex-direction: column;
				box-shadow: 0 24px 80px rgba(0,0,0,0.5);
			}

			.dialog-header {
				display: flex; justify-content: space-between; align-items: center;
				padding: 16px 20px; border-bottom: 1px solid var(--rs-border-subtle, #2a2a4a);
			}
			.dialog-header h2 { margin: 0; font-size: 16px; font-weight: 600; color: var(--rs-text-primary, #e0e0e0); }
			.dialog-close {
				background: none; border: none; color: var(--rs-text-muted, #888);
				font-size: 22px; cursor: pointer; padding: 0 4px; line-height: 1;
			}
			.dialog-close:hover { color: var(--rs-text-primary, #e0e0e0); }

			.tab-bar {
				display: flex; gap: 0; border-bottom: 1px solid var(--rs-border-subtle, #2a2a4a);
			}
			.tab {
				flex: 1; padding: 10px; border: none; background: none;
				color: var(--rs-text-secondary, #aaa); font-size: 13px; font-weight: 500;
				cursor: pointer; transition: all 0.15s;
				border-bottom: 2px solid transparent;
			}
			.tab.active {
				color: var(--rs-primary, #6366f1);
				border-bottom-color: var(--rs-primary, #6366f1);
			}
			.tab:hover { color: var(--rs-text-primary, #e0e0e0); }

			.source-bar {
				display: flex; gap: 4px; padding: 12px 16px; flex-wrap: wrap;
				border-bottom: 1px solid var(--rs-border-subtle, #2a2a4a);
			}
			.source-btn {
				padding: 6px 12px; border-radius: 6px; border: 1px solid var(--rs-border, #2a2a4a);
				background: transparent; color: var(--rs-text-secondary, #aaa);
				font-size: 12px; cursor: pointer; display: flex; align-items: center; gap: 4px;
				transition: all 0.15s;
			}
			.source-btn:hover { border-color: var(--rs-border-strong, #444); color: var(--rs-text-primary, #e0e0e0); }
			.source-btn.active {
				background: var(--rs-primary, #6366f1); color: #fff;
				border-color: var(--rs-primary, #6366f1);
			}

			.dialog-body {
				padding: 16px 20px; overflow-y: auto; flex: 1;
			}

			.upload-area {
				border: 2px dashed var(--rs-border, #2a2a4a);
				border-radius: 10px; padding: 24px; text-align: center;
				margin-bottom: 16px; transition: border-color 0.15s, background 0.15s;
			}
			.upload-area.dragover {
				border-color: var(--rs-primary, #6366f1);
				background: rgba(99,102,241,0.05);
			}
			.upload-area p { margin: 0 0 8px; color: var(--rs-text-secondary, #aaa); font-size: 13px; }
			.upload-hint { font-size: 11px !important; color: var(--rs-text-muted, #666) !important; margin-top: 8px !important; }

			.form-row {
				display: flex; align-items: center; gap: 10px; margin-bottom: 14px;
			}
			.form-row label { font-size: 13px; color: var(--rs-text-secondary, #aaa); white-space: nowrap; }
			.form-row select {
				flex: 1; padding: 7px 10px; border-radius: 6px;
				border: 1px solid var(--rs-border, #2a2a4a);
				background: var(--rs-input-bg, #111); color: var(--rs-text-primary, #e0e0e0);
				font-size: 13px;
			}

			.btn-primary {
				width: 100%; padding: 10px; border-radius: 8px; border: none;
				background: var(--rs-primary, #6366f1); color: #fff; font-weight: 600;
				font-size: 13px; cursor: pointer; transition: background 0.15s;
			}
			.btn-primary:hover { background: var(--rs-primary-hover, #5558e6); }
			.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }

			.btn-secondary {
				padding: 8px 16px; border-radius: 6px;
				border: 1px solid var(--rs-border, #2a2a4a);
				background: transparent; color: var(--rs-text-primary, #e0e0e0);
				font-size: 13px; cursor: pointer;
			}
			.btn-secondary:hover { border-color: var(--rs-border-strong, #444); }
			.btn-sm { padding: 4px 10px; font-size: 11px; }

			.connect-prompt {
				text-align: center; padding: 24px;
			}
			.connect-prompt p { color: var(--rs-text-secondary, #aaa); margin-bottom: 16px; font-size: 13px; }

			.page-list-header {
				display: flex; justify-content: space-between; align-items: center;
				margin-bottom: 8px;
			}
			.page-list-header span { font-size: 13px; color: var(--rs-text-secondary, #aaa); }

			.page-list {
				max-height: 200px; overflow-y: auto;
				border: 1px solid var(--rs-border-subtle, #2a2a4a);
				border-radius: 8px; margin-bottom: 14px;
			}
			.page-item {
				display: flex; align-items: center; gap: 8px; padding: 8px 12px;
				cursor: pointer; transition: background 0.1s;
				border-bottom: 1px solid var(--rs-border-subtle, #1a1a2e);
			}
			.page-item:last-child { border-bottom: none; }
			.page-item:hover { background: rgba(255,255,255,0.03); }
			.page-item input[type="checkbox"] { accent-color: var(--rs-primary, #6366f1); }
			.page-icon {
				width: 20px; height: 20px; font-size: 12px;
				display: flex; align-items: center; justify-content: center;
				background: var(--rs-bg-surface-raised, #222); border-radius: 4px;
				color: var(--rs-text-muted, #888);
			}
			.page-title { font-size: 13px; color: var(--rs-text-primary, #e0e0e0); flex: 1; }

			.empty-list {
				padding: 20px; text-align: center; color: var(--rs-text-muted, #666); font-size: 12px;
			}

			.status-message {
				margin-top: 12px; padding: 8px 12px; border-radius: 6px;
				font-size: 12px; text-align: center;
			}
			.status-info { background: rgba(99,102,241,0.1); color: var(--rs-primary, #6366f1); }
			.status-success { background: rgba(34,197,94,0.1); color: var(--rs-success, #22c55e); }
			.status-error { background: rgba(239,68,68,0.1); color: var(--rs-error, #ef4444); }

			.sync-summary { margin: 12px 0; }
			.sync-empty { font-size: 12px; color: var(--rs-text-muted, #666); text-align: center; padding: 20px; }
			.sync-count { font-size: 12px; color: var(--rs-text-secondary, #aaa); margin: 0 0 8px; }
			.sync-list {
				max-height: 200px; overflow-y: auto;
				border: 1px solid var(--rs-border-subtle, #2a2a4a); border-radius: 8px;
				margin-bottom: 14px;
			}
			.sync-item {
				display: flex; align-items: center; gap: 8px; padding: 6px 12px;
				border-bottom: 1px solid var(--rs-border-subtle, #1a1a2e); font-size: 12px;
			}
			.sync-item:last-child { border-bottom: none; }
			.sync-source-badge {
				padding: 2px 6px; border-radius: 4px; font-size: 10px; font-weight: 600;
				text-transform: uppercase; letter-spacing: 0.5px;
				background: rgba(99,102,241,0.15); color: var(--rs-primary, #6366f1);
			}
			.sync-source-badge.notion { background: rgba(255,255,255,0.08); }
			.sync-source-badge.google-docs { background: rgba(66,133,244,0.15); color: #4285f4; }
			.sync-source-badge.obsidian { background: rgba(126,100,255,0.15); color: #7e64ff; }
			.sync-source-badge.logseq { background: rgba(133,211,127,0.15); color: #85d37f; }
			.sync-status {
				font-size: 10px; padding: 2px 6px; border-radius: 4px;
			}
			.sync-status.synced { background: rgba(34,197,94,0.1); color: var(--rs-success, #22c55e); }
			.sync-status.conflict { background: rgba(239,68,68,0.1); color: var(--rs-error, #ef4444); }
			.sync-status.local-modified { background: rgba(250,204,21,0.1); color: #facc15; }
			.sync-status.remote-modified { background: rgba(99,102,241,0.1); color: var(--rs-primary, #6366f1); }
			.sync-time { color: var(--rs-text-muted, #666); margin-left: auto; font-size: 10px; }
			.sync-upload { margin-top: 12px; }
		`;
	}
}

customElements.define('import-export-dialog', ImportExportDialog);

export { ImportExportDialog };
