/**
 * <folk-newsletter-manager> — Newsletter management UI.
 *
 * Attributes: space, role
 * Uses EncryptID access token for auth headers.
 *
 * Tabs: Drafts (always), Lists / Subscribers / Campaigns (when Listmonk configured).
 * Drafts are Automerge-backed via server CRUD API.
 * Listmonk tabs proxy to the external Listmonk instance.
 */

import { getAccessToken } from '../../../shared/components/rstack-identity';
import { getModuleApiBase } from "../../../shared/url-helpers";

interface DraftData {
	id: string;
	title: string;
	subject: string;
	body: string;
	status: 'draft' | 'ready' | 'sent';
	subscribers: { email: string; name?: string; addedAt: number }[];
	createdAt: number;
	updatedAt: number;
	createdBy: string;
}

type Tab = 'drafts' | 'lists' | 'subscribers' | 'campaigns';

export class FolkNewsletterManager extends HTMLElement {
	private _space = 'demo';
	private _role = 'viewer';
	private _tab: Tab = 'drafts';
	private _configured = false;
	private _loading = true;
	private _error = '';

	// Drafts data
	private _drafts: DraftData[] = [];
	private _editingDraft: DraftData | null = null;

	// Listmonk data
	private _lists: any[] = [];
	private _subscribers: any[] = [];
	private _subscriberTotal = 0;
	private _subscriberPage = 1;
	private _campaigns: any[] = [];
	private _showCreateForm = false;

	// Listmonk editor state
	private _editingCampaign: any | null = null;
	private _selectedListIds: number[] = [];
	private _previewHtml = '';
	private _previewTimer: any = null;

	static get observedAttributes() { return ['space', 'role']; }

	connectedCallback() {
		if (!this.shadowRoot) this.attachShadow({ mode: 'open' });
		this._space = this.getAttribute('space') || 'demo';
		this._role = this.getAttribute('role') || 'viewer';
		this.render();
		this.checkStatus();

		this.addEventListener('module-configured', () => {
			this._configured = false;
			this._loading = true;
			this.render();
			this.checkStatus();
		});
	}

	attributeChangedCallback(name: string, _old: string, val: string) {
		if (name === 'space') this._space = val;
		if (name === 'role') this._role = val;
	}

	private apiBase(): string {
		return `${getModuleApiBase("rsocials")}/api/newsletter`;
	}

	private async apiFetch(path: string, opts: RequestInit = {}): Promise<Response> {
		const headers = new Headers(opts.headers);
		const token = getAccessToken();
		if (token) headers.set('Authorization', `Bearer ${token}`);
		if (opts.body && !headers.has('Content-Type')) headers.set('Content-Type', 'application/json');
		return fetch(`${this.apiBase()}${path}`, { ...opts, headers });
	}

	private async checkStatus() {
		this._loading = true;
		this.render();
		try {
			// Always load drafts first
			await this.loadDrafts();
			// Then check Listmonk status
			const res = await fetch(`${this.apiBase()}/status`);
			const data = await res.json();
			this._configured = data.configured;
		} catch (e: any) {
			this._error = e.message || 'Failed to check status';
		}
		this._loading = false;
		this.render();
	}

	private async loadTab() {
		this._error = '';
		this._loading = true;
		this.render();
		try {
			if (this._tab === 'drafts') await this.loadDrafts();
			else if (this._tab === 'lists') await this.loadLists();
			else if (this._tab === 'subscribers') await this.loadSubscribers();
			else if (this._tab === 'campaigns') await this.loadCampaigns();
		} catch (e: any) {
			this._error = e.message || 'Request failed';
		}
		this._loading = false;
		this.render();
	}

	// ── Draft CRUD ──

	private async loadDrafts() {
		const res = await this.apiFetch('/drafts');
		if (!res.ok) throw new Error(`Failed to load drafts (${res.status})`);
		const data = await res.json();
		this._drafts = data.drafts || [];
	}

	private async saveDraftToServer() {
		const root = this.shadowRoot!;
		const title = (root.querySelector('[data-field="draft-title"]') as HTMLInputElement)?.value?.trim();
		const subject = (root.querySelector('[data-field="draft-subject"]') as HTMLInputElement)?.value?.trim();
		const body = (root.querySelector('[data-field="draft-body"]') as HTMLTextAreaElement)?.value || '';
		const statusSelect = root.querySelector('[data-field="draft-status"]') as HTMLSelectElement | null;
		const status = (statusSelect?.value || 'draft') as DraftData['status'];

		if (!title || !subject) {
			this._error = 'Title and subject are required';
			this.render();
			return;
		}

		// Collect subscribers from current editing state
		const subscribers = this._editingDraft?.subscribers || [];

		try {
			let res: Response;
			const payload = { title, subject, body, status, subscribers };
			if (this._editingDraft?.id) {
				res = await this.apiFetch(`/drafts/${this._editingDraft.id}`, {
					method: 'PUT',
					body: JSON.stringify(payload),
				});
			} else {
				res = await this.apiFetch('/drafts', {
					method: 'POST',
					body: JSON.stringify(payload),
				});
			}
			if (!res.ok) {
				const err = await res.json().catch(() => ({}));
				throw new Error(err.message || err.error || 'Failed to save draft');
			}
			const data = await res.json();
			this._editingDraft = data.draft;
			this._error = '';
			this.render();
		} catch (e: any) {
			this._error = e.message;
			this.render();
		}
	}

	private async deleteDraft(id: string) {
		if (!confirm('Delete this draft? This cannot be undone.')) return;
		try {
			const res = await this.apiFetch(`/drafts/${id}`, { method: 'DELETE' });
			if (!res.ok) {
				const err = await res.json().catch(() => ({}));
				throw new Error(err.message || err.error || 'Failed to delete');
			}
			if (this._editingDraft?.id === id) this._editingDraft = null;
			await this.loadDrafts();
			this.render();
		} catch (e: any) {
			this._error = e.message;
			this.render();
		}
	}

	private openDraftEditor(draft?: DraftData) {
		if (draft) {
			this._editingDraft = { ...draft, subscribers: [...(draft.subscribers || [])] };
		} else {
			this._editingDraft = {
				id: '',
				title: '',
				subject: '',
				body: '',
				status: 'draft',
				subscribers: [],
				createdAt: 0,
				updatedAt: 0,
				createdBy: '',
			};
		}
		this._previewHtml = this._editingDraft.body;
		this.render();
	}

	private closeDraftEditor() {
		this._editingDraft = null;
		this._previewHtml = '';
		this.loadDrafts();
	}

	private addSubscriberToDraft() {
		if (!this._editingDraft) return;
		const root = this.shadowRoot!;
		const emailInput = root.querySelector('[data-field="sub-email"]') as HTMLInputElement;
		const nameInput = root.querySelector('[data-field="sub-name"]') as HTMLInputElement;
		const email = emailInput?.value?.trim();
		const name = nameInput?.value?.trim();
		if (!email || !email.includes('@')) {
			this._error = 'Enter a valid email address';
			this.render();
			return;
		}
		if (this._editingDraft.subscribers.some(s => s.email === email)) {
			this._error = 'This email is already in the list';
			this.render();
			return;
		}
		this._editingDraft.subscribers.push({ email, name, addedAt: Date.now() });
		this._error = '';
		this.render();
	}

	private removeSubscriberFromDraft(email: string) {
		if (!this._editingDraft) return;
		this._editingDraft.subscribers = this._editingDraft.subscribers.filter(s => s.email !== email);
		this.render();
	}

	// ── Listmonk data loading ──

	private async loadLists() {
		const res = await this.apiFetch('/lists');
		if (!res.ok) throw new Error(`Failed to load lists (${res.status})`);
		const data = await res.json();
		this._lists = data.data?.results || data.results || [];
	}

	private async loadSubscribers() {
		const res = await this.apiFetch(`/subscribers?page=${this._subscriberPage}&per_page=50`);
		if (!res.ok) throw new Error(`Failed to load subscribers (${res.status})`);
		const data = await res.json();
		this._subscribers = data.data?.results || data.results || [];
		this._subscriberTotal = data.data?.total || data.total || 0;
	}

	private async loadCampaigns() {
		const res = await this.apiFetch('/campaigns');
		if (!res.ok) throw new Error(`Failed to load campaigns (${res.status})`);
		const data = await res.json();
		this._campaigns = data.data?.results || data.results || [];
	}

	// ── Listmonk Campaign CRUD ──

	private async createCampaign(form: HTMLFormElement) {
		const fd = new FormData(form);
		const body = JSON.stringify({
			name: fd.get('name'),
			subject: fd.get('subject'),
			body: fd.get('body') || '<p>Newsletter content</p>',
			content_type: 'richtext',
			type: 'regular',
			lists: this._lists.length > 0 ? [this._lists[0].id] : [],
		});

		const res = await this.apiFetch('/campaigns', { method: 'POST', body });
		if (!res.ok) {
			const err = await res.json().catch(() => ({}));
			throw new Error(err.message || err.error || 'Failed to create campaign');
		}
		this._showCreateForm = false;
		await this.loadCampaigns();
	}

	private async setCampaignStatus(id: number, status: string, sendAt?: string) {
		const payload: any = { status };
		if (sendAt) payload.send_at = sendAt;
		const res = await this.apiFetch(`/campaigns/${id}/status`, {
			method: 'PUT',
			body: JSON.stringify(payload),
		});
		if (!res.ok) {
			const err = await res.json().catch(() => ({}));
			this._error = err.message || err.error || 'Failed to update status';
			this.render();
			return;
		}
		await this.loadCampaigns();
		this.render();
	}

	private async openEditor(campaignId?: number) {
		if (campaignId) {
			this._loading = true;
			this.render();
			try {
				const res = await this.apiFetch(`/campaigns/${campaignId}`);
				if (!res.ok) throw new Error('Failed to load campaign');
				const data = await res.json();
				this._editingCampaign = data.data || data;
				this._selectedListIds = (this._editingCampaign.lists || []).map((l: any) => l.id);
				this._previewHtml = this._editingCampaign.body || '';
			} catch (e: any) {
				this._error = e.message;
			}
			this._loading = false;
		} else {
			this._editingCampaign = { name: '', subject: '', body: '', lists: [] };
			this._selectedListIds = [];
			this._previewHtml = '';
		}
		if (this._lists.length === 0) {
			try { await this.loadLists(); } catch {}
		}
		this.render();
	}

	private closeEditor() {
		this._editingCampaign = null;
		this._selectedListIds = [];
		this._previewHtml = '';
		this.loadCampaigns();
	}

	private async saveListmonkDraft() {
		const root = this.shadowRoot!;
		const name = (root.querySelector('[data-field="name"]') as HTMLInputElement)?.value?.trim();
		const subject = (root.querySelector('[data-field="subject"]') as HTMLInputElement)?.value?.trim();
		const body = (root.querySelector('[data-field="body"]') as HTMLTextAreaElement)?.value || '';

		if (!name || !subject) {
			this._error = 'Name and subject are required';
			this.render();
			return;
		}

		const payload: any = {
			name, subject, body,
			content_type: 'richtext',
			type: 'regular',
			lists: this._selectedListIds,
		};

		try {
			let res: Response;
			if (this._editingCampaign?.id) {
				res = await this.apiFetch(`/campaigns/${this._editingCampaign.id}`, {
					method: 'PUT',
					body: JSON.stringify(payload),
				});
			} else {
				res = await this.apiFetch('/campaigns', {
					method: 'POST',
					body: JSON.stringify(payload),
				});
			}
			if (!res.ok) {
				const err = await res.json().catch(() => ({}));
				throw new Error(err.message || err.error || 'Failed to save');
			}
			const data = await res.json();
			this._editingCampaign = data.data || data;
			this._error = '';
			this.render();
		} catch (e: any) {
			this._error = e.message;
			this.render();
		}
	}

	private async deleteCampaign(id: number) {
		if (!confirm('Delete this campaign? This cannot be undone.')) return;
		try {
			const res = await this.apiFetch(`/campaigns/${id}`, { method: 'DELETE' });
			if (!res.ok) {
				const err = await res.json().catch(() => ({}));
				throw new Error(err.message || err.error || 'Failed to delete');
			}
			if (this._editingCampaign?.id === id) this._editingCampaign = null;
			await this.loadCampaigns();
			this.render();
		} catch (e: any) {
			this._error = e.message;
			this.render();
		}
	}

	private async sendNow() {
		if (!this._editingCampaign?.id) {
			this._error = 'Save the campaign first';
			this.render();
			return;
		}
		if (!confirm('Send this campaign now to the selected lists? This action cannot be undone.')) return;
		await this.saveListmonkDraft();
		await this.setCampaignStatus(this._editingCampaign.id, 'running');
		this.closeEditor();
	}

	private async scheduleSend() {
		if (!this._editingCampaign?.id) {
			this._error = 'Save the campaign first';
			this.render();
			return;
		}
		const input = this.shadowRoot!.querySelector('[data-field="schedule"]') as HTMLInputElement;
		const sendAt = input?.value;
		if (!sendAt) {
			this._error = 'Pick a date and time to schedule';
			this.render();
			return;
		}
		await this.saveListmonkDraft();
		await this.setCampaignStatus(this._editingCampaign.id, 'scheduled', new Date(sendAt).toISOString());
		this.closeEditor();
	}

	private get isAdmin(): boolean {
		return this._role === 'admin';
	}

	// ── Rendering ──

	private render() {
		const root = this.shadowRoot!;
		root.innerHTML = `<link rel="stylesheet" href="/modules/rsocials/newsletter.css">${this.renderBody()}`;
		this.attachListeners();
	}

	private renderBody(): string {
		if (this._loading && this._drafts.length === 0 && !this._configured) {
			return `<div class="nl-loading">Loading...</div>`;
		}

		// Draft editor mode
		if (this._editingDraft) {
			return this.renderDraftEditor();
		}

		// Listmonk campaign editor mode
		if (this._editingCampaign) {
			return this.renderListmonkEditor();
		}

		// Build tabs: Drafts always, Listmonk tabs conditionally
		const listmonkTabs = this._configured ? `
			<button class="nl-tab ${this._tab === 'lists' ? 'active' : ''}" data-tab="lists">Lists</button>
			<button class="nl-tab ${this._tab === 'subscribers' ? 'active' : ''}" data-tab="subscribers">Subscribers</button>
			<button class="nl-tab ${this._tab === 'campaigns' ? 'active' : ''}" data-tab="campaigns">Campaigns</button>
		` : '';

		const infoBanner = !this._configured ? `
			<div class="nl-info-banner">
				Listmonk is not configured for this space. You can still create and manage newsletter drafts with built-in subscriber lists.
				Configure Listmonk in space settings to enable sending.
			</div>
		` : '';

		return `
			<div class="nl-header">
				<h2>Newsletter</h2>
				<p>Draft newsletters, manage subscribers, and send campaigns</p>
			</div>
			${this._error ? `<div class="nl-error">${this.esc(this._error)}</div>` : ''}
			${infoBanner}
			<div class="nl-tabs">
				<button class="nl-tab ${this._tab === 'drafts' ? 'active' : ''}" data-tab="drafts">Drafts</button>
				${listmonkTabs}
			</div>
			${this._loading ? '<div class="nl-loading">Loading...</div>' : this.renderTabContent()}
		`;
	}

	private renderTabContent(): string {
		if (this._tab === 'drafts') return this.renderDrafts();
		if (this._tab === 'lists') return this.renderLists();
		if (this._tab === 'subscribers') return this.renderSubscribers();
		return this.renderCampaigns();
	}

	// ── Drafts tab ──

	private renderDrafts(): string {
		const createBtn = this.isAdmin ? `
			<div class="nl-toolbar">
				<span></span>
				<button class="nl-btn nl-btn--primary" data-action="new-draft">+ New Draft</button>
			</div>
		` : '';

		if (this._drafts.length === 0) {
			return `${createBtn}<div class="nl-empty">No newsletter drafts yet. Create your first draft to get started.</div>`;
		}

		const statusClass = (s: string) => {
			if (s === 'ready') return 'active';
			if (s === 'sent') return 'finished';
			return 'draft';
		};

		const rows = this._drafts.map(d => {
			const actions: string[] = [];
			if (this.isAdmin) {
				actions.push(`<button class="nl-btn nl-btn--sm" data-action="edit-draft" data-id="${d.id}">Edit</button>`);
				actions.push(`<button class="nl-btn nl-btn--sm nl-btn--danger" data-action="delete-draft" data-id="${d.id}">Delete</button>`);
			}
			return `
				<tr>
					<td>${this.esc(d.title)}</td>
					<td>${this.esc(d.subject)}</td>
					<td><span class="nl-badge nl-badge--${statusClass(d.status)}">${this.esc(d.status)}</span></td>
					<td>${d.subscribers?.length || 0}</td>
					<td>${d.updatedAt ? new Date(d.updatedAt).toLocaleDateString() : '—'}</td>
					<td class="nl-actions-cell">${actions.join(' ')}</td>
				</tr>
			`;
		}).join('');

		return `
			${createBtn}
			<table class="nl-table">
				<thead><tr><th>Title</th><th>Subject</th><th>Status</th><th>Subscribers</th><th>Updated</th><th>Actions</th></tr></thead>
				<tbody>${rows}</tbody>
			</table>
		`;
	}

	private renderDraftEditor(): string {
		const d = this._editingDraft!;
		const isNew = !d.id;
		const title = isNew ? 'New Newsletter Draft' : `Edit: ${this.esc(d.title)}`;

		const subRows = (d.subscribers || []).map(s => `
			<tr>
				<td>${this.esc(s.email)}</td>
				<td>${this.esc(s.name || '—')}</td>
				<td>${this.isAdmin ? `<button class="nl-btn nl-btn--sm nl-btn--danger" data-action="remove-sub" data-email="${this.escAttr(s.email)}">Remove</button>` : ''}</td>
			</tr>
		`).join('');

		const subscriberSection = `
			<div class="nl-draft-subscribers">
				<h3>Subscribers (${d.subscribers?.length || 0})</h3>
				${this.isAdmin ? `
					<div class="nl-form-row nl-sub-add-row">
						<div><input data-field="sub-email" placeholder="email@example.com" type="email" /></div>
						<div><input data-field="sub-name" placeholder="Name (optional)" /></div>
						<div><button class="nl-btn nl-btn--sm nl-btn--primary" data-action="add-subscriber">Add</button></div>
					</div>
				` : ''}
				${d.subscribers?.length ? `
					<table class="nl-table nl-table--compact">
						<thead><tr><th>Email</th><th>Name</th><th></th></tr></thead>
						<tbody>${subRows}</tbody>
					</table>
				` : '<div class="nl-empty">No subscribers added yet</div>'}
			</div>
		`;

		return `
			<div class="nl-editor-header">
				<button class="nl-btn" data-action="close-draft-editor">&larr; Back</button>
				<h2>${title}</h2>
			</div>
			${this._error ? `<div class="nl-error">${this.esc(this._error)}</div>` : ''}
			<div class="nl-editor-fields">
				<div class="nl-form-row">
					<div>
						<label>Title</label>
						<input data-field="draft-title" value="${this.escAttr(d.title || '')}" placeholder="Newsletter title" />
					</div>
					<div>
						<label>Subject Line</label>
						<input data-field="draft-subject" value="${this.escAttr(d.subject || '')}" placeholder="Email subject" />
					</div>
				</div>
				<div class="nl-form-row">
					<div>
						<label>Status</label>
						<select data-field="draft-status">
							<option value="draft" ${d.status === 'draft' ? 'selected' : ''}>Draft</option>
							<option value="ready" ${d.status === 'ready' ? 'selected' : ''}>Ready</option>
							<option value="sent" ${d.status === 'sent' ? 'selected' : ''}>Sent</option>
						</select>
					</div>
				</div>
			</div>
			<div class="nl-editor">
				<div class="nl-editor__body">
					<label>Body (HTML)</label>
					<textarea data-field="draft-body" placeholder="<p>Newsletter content goes here...</p>">${this.esc(d.body || '')}</textarea>
				</div>
				<div class="nl-editor__preview">
					<label>Preview</label>
					<iframe class="nl-preview-frame" sandbox="allow-same-origin" srcdoc="${this.escAttr(this._previewHtml || d.body || '')}"></iframe>
				</div>
			</div>
			${subscriberSection}
			<div class="nl-editor-actions">
				<div class="nl-editor-actions__left">
					<button class="nl-btn" data-action="close-draft-editor">Cancel</button>
					${d.id ? `<button class="nl-btn nl-btn--danger" data-action="delete-draft" data-id="${d.id}">Delete</button>` : ''}
				</div>
				<div class="nl-editor-actions__right">
					<button class="nl-btn nl-btn--primary" data-action="save-newsletter-draft">Save</button>
				</div>
			</div>
		`;
	}

	// ── Listmonk tabs ──

	private renderLists(): string {
		if (this._lists.length === 0) return `<div class="nl-empty">No mailing lists found</div>`;

		const rows = this._lists.map(l => {
			const optinLabel = l.optin === 'double' ? 'double' : 'single';
			return `
				<tr>
					<td>${this.esc(l.name)}</td>
					<td><span class="nl-badge nl-badge--${l.type === 'public' ? 'active' : 'draft'}">${this.esc(l.type)}</span></td>
					<td><span class="nl-badge nl-badge--enabled">${l.subscriber_count ?? 0}</span></td>
					<td><span class="nl-badge nl-badge--${optinLabel === 'double' ? 'active' : 'draft'}">${optinLabel} opt-in</span></td>
					<td>${l.created_at ? new Date(l.created_at).toLocaleDateString() : '—'}</td>
				</tr>
			`;
		}).join('');

		return `
			<table class="nl-table">
				<thead><tr><th>Name</th><th>Type</th><th>Subscribers</th><th>Opt-in</th><th>Created</th></tr></thead>
				<tbody>${rows}</tbody>
			</table>
		`;
	}

	private renderSubscribers(): string {
		if (this._subscribers.length === 0) return `<div class="nl-empty">No subscribers found</div>`;

		const rows = this._subscribers.map(s => `
			<tr>
				<td>${this.esc(s.email)}</td>
				<td>${this.esc(s.name || '—')}</td>
				<td><span class="nl-badge nl-badge--${s.status === 'enabled' ? 'enabled' : 'blocklisted'}">${this.esc(s.status)}</span></td>
				<td>${(s.lists || []).map((l: any) => this.esc(l.name)).join(', ') || '—'}</td>
				<td>${s.created_at ? new Date(s.created_at).toLocaleDateString() : '—'}</td>
			</tr>
		`).join('');

		const totalPages = Math.ceil(this._subscriberTotal / 50);
		const pagination = totalPages > 1 ? `
			<div class="nl-pagination">
				<button class="nl-btn nl-btn--sm" data-action="prev-page" ${this._subscriberPage <= 1 ? 'disabled' : ''}>Prev</button>
				<span>Page ${this._subscriberPage} of ${totalPages}</span>
				<button class="nl-btn nl-btn--sm" data-action="next-page" ${this._subscriberPage >= totalPages ? 'disabled' : ''}>Next</button>
			</div>
		` : '';

		return `
			<table class="nl-table">
				<thead><tr><th>Email</th><th>Name</th><th>Status</th><th>Lists</th><th>Created</th></tr></thead>
				<tbody>${rows}</tbody>
			</table>
			${pagination}
		`;
	}

	private renderCampaigns(): string {
		const createBtn = this.isAdmin ? `
			<div class="nl-toolbar">
				<span></span>
				<button class="nl-btn nl-btn--primary" data-action="new-campaign">+ New Campaign</button>
			</div>
		` : '';

		if (this._campaigns.length === 0) {
			return `${createBtn}<div class="nl-empty">No campaigns found</div>`;
		}

		const statusLabel = (s: string) => {
			const map: Record<string, string> = { draft: 'draft', running: 'running', paused: 'paused', finished: 'finished', scheduled: 'active' };
			return map[s] || s;
		};

		const rows = this._campaigns.map(c => {
			const listNames = (c.lists || []).map((l: any) => this.esc(l.name)).join(', ') || '—';
			const actions: string[] = [];
			if (c.status === 'draft' && this.isAdmin) {
				actions.push(`<button class="nl-btn nl-btn--sm" data-action="edit-campaign" data-id="${c.id}">Edit</button>`);
				actions.push(`<button class="nl-btn nl-btn--sm" data-action="start-campaign" data-id="${c.id}">Start</button>`);
				actions.push(`<button class="nl-btn nl-btn--sm nl-btn--danger" data-action="delete-campaign" data-id="${c.id}">Delete</button>`);
			} else if (c.status === 'running') {
				actions.push(`<button class="nl-btn nl-btn--sm" data-action="pause-campaign" data-id="${c.id}">Pause</button>`);
			} else if (c.status === 'paused') {
				actions.push(`<button class="nl-btn nl-btn--sm" data-action="resume-campaign" data-id="${c.id}">Resume</button>`);
			} else if (c.status === 'scheduled' && this.isAdmin) {
				actions.push(`<button class="nl-btn nl-btn--sm" data-action="edit-campaign" data-id="${c.id}">Edit</button>`);
			}
			return `
				<tr>
					<td>${this.esc(c.name)}</td>
					<td>${this.esc(c.subject || '—')}</td>
					<td><span class="nl-badge nl-badge--${statusLabel(c.status)}">${this.esc(c.status)}</span></td>
					<td>${listNames}</td>
					<td>${c.sent ?? '—'}</td>
					<td>${c.created_at ? new Date(c.created_at).toLocaleDateString() : '—'}</td>
					<td class="nl-actions-cell">${actions.join(' ')}</td>
				</tr>
			`;
		}).join('');

		return `
			${createBtn}
			<table class="nl-table">
				<thead><tr><th>Name</th><th>Subject</th><th>Status</th><th>Lists</th><th>Sent</th><th>Created</th><th>Actions</th></tr></thead>
				<tbody>${rows}</tbody>
			</table>
		`;
	}

	private renderListmonkEditor(): string {
		const c = this._editingCampaign;
		const isNew = !c.id;
		const title = isNew ? 'New Campaign' : `Edit: ${this.esc(c.name)}`;

		const listCheckboxes = this._lists.map(l => {
			const checked = this._selectedListIds.includes(l.id) ? 'checked' : '';
			return `
				<label class="nl-list-option">
					<input type="checkbox" data-list-id="${l.id}" ${checked} />
					<span>${this.esc(l.name)}</span>
					<span class="nl-list-count">${l.subscriber_count ?? 0}</span>
				</label>
			`;
		}).join('');

		return `
			<div class="nl-editor-header">
				<button class="nl-btn" data-action="close-editor">&larr; Back</button>
				<h2>${title}</h2>
			</div>
			${this._error ? `<div class="nl-error">${this.esc(this._error)}</div>` : ''}
			<div class="nl-editor-fields">
				<div class="nl-form-row">
					<div>
						<label>Campaign Name</label>
						<input data-field="name" value="${this.escAttr(c.name || '')}" placeholder="My Newsletter #1" />
					</div>
					<div>
						<label>Subject Line</label>
						<input data-field="subject" value="${this.escAttr(c.subject || '')}" placeholder="This week's update" />
					</div>
				</div>
				<label>Target Lists</label>
				<div class="nl-list-selector">
					${listCheckboxes || '<span class="nl-empty">No lists available</span>'}
				</div>
			</div>
			<div class="nl-editor">
				<div class="nl-editor__body">
					<label>Body (HTML)</label>
					<textarea data-field="body" placeholder="<p>Newsletter content goes here...</p>">${this.esc(c.body || '')}</textarea>
				</div>
				<div class="nl-editor__preview">
					<label>Preview</label>
					<iframe class="nl-preview-frame" sandbox="allow-same-origin" srcdoc="${this.escAttr(this._previewHtml || c.body || '')}"></iframe>
				</div>
			</div>
			<div class="nl-editor-actions">
				<div class="nl-editor-actions__left">
					<button class="nl-btn" data-action="close-editor">Cancel</button>
					${c.id ? `<button class="nl-btn nl-btn--danger" data-action="delete-campaign" data-id="${c.id}">Delete</button>` : ''}
				</div>
				<div class="nl-editor-actions__right">
					<button class="nl-btn nl-btn--primary" data-action="save-draft">Save Draft</button>
					<div class="nl-schedule-group">
						<input type="datetime-local" data-field="schedule" />
						<button class="nl-btn" data-action="schedule-send">Schedule</button>
					</div>
					<button class="nl-btn nl-btn--send" data-action="send-now">Send Now</button>
				</div>
			</div>
		`;
	}

	// ── Event listeners ──

	private attachListeners() {
		const root = this.shadowRoot!;

		// Tab switching
		root.querySelectorAll('.nl-tab').forEach(btn => {
			btn.addEventListener('click', () => {
				this._tab = (btn as HTMLElement).dataset.tab as Tab;
				this.loadTab();
			});
		});

		// Pagination
		root.querySelector('[data-action="prev-page"]')?.addEventListener('click', () => {
			if (this._subscriberPage > 1) { this._subscriberPage--; this.loadTab(); }
		});
		root.querySelector('[data-action="next-page"]')?.addEventListener('click', () => {
			this._subscriberPage++;
			this.loadTab();
		});

		// ── Draft actions ──
		root.querySelector('[data-action="new-draft"]')?.addEventListener('click', () => {
			this.openDraftEditor();
		});
		root.querySelectorAll('[data-action="edit-draft"]').forEach(btn => {
			btn.addEventListener('click', () => {
				const id = (btn as HTMLElement).dataset.id!;
				const draft = this._drafts.find(d => d.id === id);
				if (draft) this.openDraftEditor(draft);
			});
		});
		root.querySelectorAll('[data-action="delete-draft"]').forEach(btn => {
			btn.addEventListener('click', () => {
				this.deleteDraft((btn as HTMLElement).dataset.id!);
			});
		});
		root.querySelectorAll('[data-action="close-draft-editor"]').forEach(btn => {
			btn.addEventListener('click', () => this.closeDraftEditor());
		});
		root.querySelector('[data-action="save-newsletter-draft"]')?.addEventListener('click', () => {
			this.saveDraftToServer();
		});
		root.querySelector('[data-action="add-subscriber"]')?.addEventListener('click', () => {
			this.addSubscriberToDraft();
		});
		root.querySelectorAll('[data-action="remove-sub"]').forEach(btn => {
			btn.addEventListener('click', () => {
				this.removeSubscriberFromDraft((btn as HTMLElement).dataset.email!);
			});
		});

		// ── Listmonk campaign actions ──
		root.querySelector('[data-action="new-campaign"]')?.addEventListener('click', () => {
			this.openEditor();
		});
		root.querySelectorAll('[data-action="toggle-create"]').forEach(btn => {
			btn.addEventListener('click', () => {
				this._showCreateForm = !this._showCreateForm;
				this.render();
			});
		});
		const form = root.querySelector('[data-form="create-campaign"]') as HTMLFormElement | null;
		form?.addEventListener('submit', async (e) => {
			e.preventDefault();
			try {
				await this.createCampaign(form);
				this.render();
			} catch (err: any) {
				this._error = err.message;
				this.render();
			}
		});
		root.querySelectorAll('[data-action="start-campaign"]').forEach(btn => {
			btn.addEventListener('click', () => this.setCampaignStatus(Number((btn as HTMLElement).dataset.id), 'running'));
		});
		root.querySelectorAll('[data-action="pause-campaign"]').forEach(btn => {
			btn.addEventListener('click', () => this.setCampaignStatus(Number((btn as HTMLElement).dataset.id), 'paused'));
		});
		root.querySelectorAll('[data-action="resume-campaign"]').forEach(btn => {
			btn.addEventListener('click', () => this.setCampaignStatus(Number((btn as HTMLElement).dataset.id), 'running'));
		});
		root.querySelectorAll('[data-action="edit-campaign"]').forEach(btn => {
			btn.addEventListener('click', () => this.openEditor(Number((btn as HTMLElement).dataset.id)));
		});
		root.querySelectorAll('[data-action="delete-campaign"]').forEach(btn => {
			btn.addEventListener('click', () => this.deleteCampaign(Number((btn as HTMLElement).dataset.id)));
		});
		root.querySelectorAll('[data-action="close-editor"]').forEach(btn => {
			btn.addEventListener('click', () => this.closeEditor());
		});
		root.querySelector('[data-action="save-draft"]')?.addEventListener('click', () => this.saveListmonkDraft());
		root.querySelector('[data-action="send-now"]')?.addEventListener('click', () => this.sendNow());
		root.querySelector('[data-action="schedule-send"]')?.addEventListener('click', () => this.scheduleSend());

		// List selector checkboxes
		root.querySelectorAll('[data-list-id]').forEach(cb => {
			cb.addEventListener('change', () => {
				const id = Number((cb as HTMLElement).dataset.listId);
				if ((cb as HTMLInputElement).checked) {
					if (!this._selectedListIds.includes(id)) this._selectedListIds.push(id);
				} else {
					this._selectedListIds = this._selectedListIds.filter(x => x !== id);
				}
			});
		});

		// Live preview update on body textarea input (both draft and listmonk editors)
		const bodyTextarea = (root.querySelector('[data-field="draft-body"]') || root.querySelector('[data-field="body"]')) as HTMLTextAreaElement | null;
		bodyTextarea?.addEventListener('input', () => {
			clearTimeout(this._previewTimer);
			this._previewTimer = setTimeout(() => {
				this._previewHtml = bodyTextarea.value;
				const iframe = root.querySelector('.nl-preview-frame') as HTMLIFrameElement | null;
				if (iframe) iframe.srcdoc = this._previewHtml;
			}, 300);
		});

		// Allow Enter in subscriber email field to trigger add
		const subEmailInput = root.querySelector('[data-field="sub-email"]') as HTMLInputElement | null;
		subEmailInput?.addEventListener('keydown', (e) => {
			if (e.key === 'Enter') {
				e.preventDefault();
				this.addSubscriberToDraft();
			}
		});
	}

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

	private escAttr(s: string): string {
		return s.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
	}
}

customElements.define('folk-newsletter-manager', FolkNewsletterManager);
