const STORAGE_KEY_SETTINGS = 'epwpScannerSettings';
const STORAGE_KEY_DRAFT = 'epwpScannerDraft';
const STORAGE_KEY_BULK_URLS = 'epwpScannerBulkUrls';
const STORAGE_KEY_DYNAMIC_SCHEMA = 'epwpScannerDynamicSchema';
const STORAGE_KEY_FORM_SCHEMA = 'epwpScannerFormSchema';

const SETTINGS_FIELDS = [
  'pluginBaseUrl',
  'appPass',
  'openaiApiKey',
  'openaiModel',
  'importStatusDefault',
  'importMediaPolicy',
  'importContentMode',
  'requestTimeoutSeconds',
  'retryAttempts'
];

const SETTINGS_CHECKBOXES = [
  'useAiByDefault',
  'autoOpenImportedEvent',
  'rememberDraft',
  'verboseLogs',
  'notifyPotentialOwner',
  'bulkTranslateNonEnglish'
];

const DRAFT_FIELDS = [
  'title',
  'start',
  'end',
  'meta_title',
  'meta_description',
  'timezone',
  'venue',
  'address',
  'city',
  'country',
  'event_type',
  'event_mode',
  'event_price_range',
  'event_tags',
  'capacity',
  'price',
  'ticket_sales_enabled',
  'ticket_price',
  'ticket_button_label',
  'recurrence',
  'recurrence_until',
  'external_url',
  'stream_url',
  'contact_name',
  'contact_email',
  'contact_phone',
  'cover_image_url',
  'official_event',
  'excerpt',
  'description',
  'gallery_urls',
  'video_urls',
  'supporting_docs_urls',
  'sessions',
  'key_speakers'
];

const CORE_MAPPED_META_KEYS = new Set([
  'eep_start',
  'eep_end',
  'eep_timezone',
  'eep_venue',
  'eep_address',
  'eep_city',
  'eep_country',
  'eep_capacity',
  'eep_price',
  'eep_ticket_sales_enabled',
  'eep_ticket_price',
  'eep_ticket_button_label',
  'eep_meta_title',
  'eep_meta_description',
  'eep_external_url',
  'eep_event_stream_url',
  'eep_contact_name',
  'eep_contact_email',
  'eep_contact_phone',
  'eep_cover_image_url',
  'eep_video_urls',
  'eep_gallery_urls',
  'eep_supporting_docs',
  'eep_official_event',
  'eep_recurrence',
  'eep_recurrence_until'
]);

const DEFAULT_SETTINGS = {
  pluginBaseUrl: '',
  appPass: '',
  openaiApiKey: '',
  openaiModel: 'gpt-5.2',
  importStatusDefault: 'draft',
  importMediaPolicy: 'none',
  importContentMode: 'rewrite',
  useAiByDefault: '1',
  autoOpenImportedEvent: '1',
  rememberDraft: '1',
  verboseLogs: '0',
  notifyPotentialOwner: '0',
  bulkTranslateNonEnglish: '0',
  requestTimeoutSeconds: '25',
  retryAttempts: '1'
};

const statusLine = document.getElementById('statusLine');
const logsContainer = document.getElementById('logsContainer');
const logCountBadge = document.getElementById('logCountBadge');
const pluginTestStatus = document.getElementById('pluginTestStatus');
const openaiTestStatus = document.getElementById('openaiTestStatus');
const toastStack = document.getElementById('epwpToastStack');

const saveSettingsBtn = document.getElementById('saveSettingsBtn');
const testConnectionBtn = document.getElementById('testConnectionBtn');
const testOpenAIBtn = document.getElementById('testOpenAIBtn');
const startScanBtn = document.getElementById('startScanBtn');
const scanLoadingOverlay = document.getElementById('scanLoadingOverlay');
const resetDraftBtn = document.getElementById('resetDraftBtn');
const submitEventBtn = document.getElementById('submitEventBtn');
const copyJsonBtn = document.getElementById('copyJsonBtn');
const refreshLogsBtn = document.getElementById('refreshLogsBtn');
const copyLogsBtn = document.getElementById('copyLogsBtn');
const clearLogsBtn = document.getElementById('clearLogsBtn');
const bulkUrlsEl = document.getElementById('bulkUrls');
const bulkStartBtn = document.getElementById('bulkStartBtn');
const bulkStopBtn = document.getElementById('bulkStopBtn');
const bulkClearBtn = document.getElementById('bulkClearBtn');
const bulkStatusSummary = document.getElementById('bulkStatusSummary');
const bulkProgressBar = document.getElementById('bulkProgressBar');
const bulkCurrentUrl = document.getElementById('bulkCurrentUrl');
const bulkResults = document.getElementById('bulkResults');
const bulkTranslateNonEnglishBulk = document.getElementById('bulkTranslateNonEnglishBulk');
const refreshCustomFieldsBtn = document.getElementById('refreshCustomFieldsBtn');
const customFieldsContainer = document.getElementById('customFieldsContainer');
const refreshFormFieldsBtn = document.getElementById('refreshFormFieldsBtn');
const formFieldsContainer = document.getElementById('formFieldsContainer');

let bulkStatusTimer = null;
let dynamicFieldSchema = [];
let dynamicFormFieldSchema = [];

init().catch((error) => {
  setStatus(error?.message || 'Failed to initialize extension.', 'error');
});

async function init() {
  setupTabs();

  const store = await chrome.storage.local.get([STORAGE_KEY_SETTINGS, STORAGE_KEY_DRAFT, STORAGE_KEY_BULK_URLS, STORAGE_KEY_DYNAMIC_SCHEMA, STORAGE_KEY_FORM_SCHEMA]);
  const savedSettings = normalizeSettings(store[STORAGE_KEY_SETTINGS] || {});
  const savedDraft = store[STORAGE_KEY_DRAFT] || {};
  dynamicFieldSchema = normalizeCustomFieldSchema(store[STORAGE_KEY_DYNAMIC_SCHEMA] || []);
  dynamicFormFieldSchema = normalizePluginFormFieldSchema(store[STORAGE_KEY_FORM_SCHEMA] || []);
  applySettings(savedSettings);
  renderDynamicCustomFields(dynamicFieldSchema, savedDraft.custom_fields || {});
  renderDynamicPluginFields(dynamicFormFieldSchema, savedDraft.extra_fields || {});
  applyValues(savedDraft, DRAFT_FIELDS);
  applyDynamicCustomFieldValues(savedDraft.custom_fields || {});
  applyDynamicPluginFieldValues(savedDraft.extra_fields || {});
  if (bulkUrlsEl) {
    bulkUrlsEl.value = String(store[STORAGE_KEY_BULK_URLS] || '');
    bulkUrlsEl.addEventListener('input', debounce(saveBulkUrls, 220));
  }

  SETTINGS_FIELDS.forEach((field) => {
    const el = document.getElementById(field);
    if (!el) {
      return;
    }
    el.addEventListener('change', debounce(saveSettings, 100));
  });

  SETTINGS_CHECKBOXES.forEach((field) => {
    const el = document.getElementById(field);
    if (!el) {
      return;
    }
    el.addEventListener('change', debounce(saveSettings, 100));
  });

  DRAFT_FIELDS.forEach((field) => {
    const el = document.getElementById(field);
    if (!el) {
      return;
    }
    el.addEventListener('input', debounce(saveDraft, 180));
    el.addEventListener('change', debounce(saveDraft, 180));
  });

  refreshCustomFieldsBtn?.addEventListener('click', async () => {
    const settings = collectSettings();
    await syncCustomFieldSchemaFromPlugin(settings, { manual: true });
  });
  refreshFormFieldsBtn?.addEventListener('click', async () => {
    const settings = collectSettings();
    await syncCustomFieldSchemaFromPlugin(settings, { manual: true });
  });

  const bulkTranslateMainToggle = document.getElementById('bulkTranslateNonEnglish');
  if (bulkTranslateMainToggle && bulkTranslateNonEnglishBulk) {
    bulkTranslateNonEnglishBulk.checked = Boolean(bulkTranslateMainToggle.checked);

    bulkTranslateMainToggle.addEventListener('change', () => {
      bulkTranslateNonEnglishBulk.checked = Boolean(bulkTranslateMainToggle.checked);
    });

    bulkTranslateNonEnglishBulk.addEventListener('change', async () => {
      bulkTranslateMainToggle.checked = Boolean(bulkTranslateNonEnglishBulk.checked);
      await saveSettings();
    });
  }

  saveSettingsBtn?.addEventListener('click', async () => {
    const result = await saveSettings({ showStatus: true });
    if (result.ok) {
      const syncResult = await syncPluginPreferencesIfPossible(result.settings || collectSettings());
      if (!syncResult.ok) {
        setStatus(syncResult.error || 'Saved locally, but plugin sync failed.', 'error');
      } else {
        setStatus('Settings saved and synced.', 'success');
      }
      await writeLog('info', 'settings.save', 'Settings saved from side panel.');
    } else {
      setStatus(result.error || 'Could not save settings.', 'error');
    }
  });

  testConnectionBtn?.addEventListener('click', async () => {
    const settings = collectSettings();
    const check = validateSettings(settings, { requireConnection: true });
    if (!check.ok) {
      setStatus(check.error, 'error');
      setTestStatus(pluginTestStatus, false, check.error);
      return;
    }

    setBusy(testConnectionBtn, true);
    const sync = await syncCustomFieldSchemaFromPlugin(settings, { manual: true });
    setBusy(testConnectionBtn, false);
    if (!sync.ok) {
      setTestStatus(pluginTestStatus, false, sync.error || 'Connection failed.');
      return;
    }
    setTestStatus(pluginTestStatus, true, `Connected to ${sync.plugin || 'nexdirectory'}`);
  });

  testOpenAIBtn?.addEventListener('click', async () => {
    const settings = collectSettings();
    const check = validateSettings(settings, { requireOpenAi: true });
    if (!check.ok) {
      setStatus(check.error, 'error');
      setTestStatus(openaiTestStatus, false, check.error);
      return;
    }

    setBusy(testOpenAIBtn, true);
    setStatus('Testing OpenAI...');
    setTestStatus(openaiTestStatus, null, 'Testing OpenAI...');
    const result = await sendRuntimeMessage({ type: 'TEST_OPENAI', settings });
    setBusy(testOpenAIBtn, false);

    if (!result?.ok) {
      setStatus(result?.error || 'OpenAI test failed.', 'error');
      setTestStatus(openaiTestStatus, false, result?.error || 'OpenAI test failed.');
      return;
    }
    setStatus('OpenAI API key and model are working.', 'success');
    setTestStatus(openaiTestStatus, true, 'OpenAI API key and model are working');
  });

  startScanBtn?.addEventListener('click', async () => {
    const settings = collectSettings();
    await syncCustomFieldSchemaFromPlugin(settings, { silent: true });
    await saveSettings();

    setBusy(startScanBtn, true);
    setScanLoadingOverlay(true);
    setStatus('Scanning current page...');
    try {
      const result = await sendRuntimeMessage({
        type: 'SCAN_PAGE',
        settings: {
          ...settings,
          customFieldSchema: dynamicFieldSchema,
          formFieldSchema: dynamicFormFieldSchema
        }
      });

      if (!result?.ok) {
        setStatus(result?.error || 'Scan failed.', 'error');
        return;
      }

      applyEventPayloadToForm(result.extracted || {});
      await saveDraft();
      const title = String(result?.extracted?.title || '').trim();
      setStatus(title ? `Scan complete: ${title}` : 'Scan complete.', 'success');
    } finally {
      setBusy(startScanBtn, false);
      setScanLoadingOverlay(false);
    }
  });

  submitEventBtn?.addEventListener('click', async () => {
    const settings = collectSettings();
    const validation = validateSettings(settings, { requireConnection: true });
    if (!validation.ok) {
      setStatus(validation.error, 'error');
      return;
    }

    const payload = buildPayloadFromForm();
    if (!payload.title) {
      setStatus('Event title is required before import.', 'error');
      return;
    }
    if (!payload.start) {
      setStatus('Event start date/time is required before import.', 'error');
      return;
    }

    await saveSettings();
    await saveDraft();

    let importOptions = {};
    if (toBool(settings.notifyPotentialOwner, false)) {
      const decision = await promptNotifyOwnerConfirmation(payload, settings);
      if (!decision || decision.cancelled) {
        setStatus('Import cancelled.', 'error');
        return;
      }
      importOptions = {
        notifyPotentialOwner: toBool(decision.notify, false),
        notifyEmail: String(decision.email || '').trim(),
        contentMode: String(settings.importContentMode || 'rewrite').trim()
      };
    } else {
      importOptions = {
        notifyPotentialOwner: false,
        notifyEmail: '',
        contentMode: String(settings.importContentMode || 'rewrite').trim()
      };
    }

    setBusy(submitEventBtn, true);
    setStatus('Importing event into WordPress...');
    const result = await sendRuntimeMessage({ type: 'IMPORT_EVENT', settings, payload, importOptions });
    setBusy(submitEventBtn, false);

    if (!result?.ok) {
      const importError = result?.error || 'Import failed.';
      setStatus(importError, 'error');
      pushToast(importError, 'error');
      return;
    }

    const data = result?.data?.data || result?.data || {};
    const postId = data?.post_id || '?';
    const permalink = String(data?.permalink || '').trim();
    setStatus(`Imported successfully (ID ${postId}).`, 'success');
    pushToast(`Imported successfully (ID ${postId}).`, 'success');

    if (toBool(importOptions?.notifyPotentialOwner, false)) {
      const notifySent = toBool(data?.owner_notification_sent, false);
      const recipient = String(
        data?.owner_notification_email || importOptions?.notifyEmail || payload?.contact_email || ''
      ).trim();
      if (notifySent) {
        pushToast(
          recipient
            ? `Owner notification sent to ${recipient}.`
            : 'Owner notification email sent.',
          'success'
        );
      } else {
        pushToast(
          recipient
            ? `Owner notification failed for ${recipient}. Check NexDirectory mail settings/logs.`
            : 'Owner notification failed. Check NexDirectory mail settings/logs.',
          'error',
          7000
        );
      }
    }

    if (permalink && toBool(settings.autoOpenImportedEvent, true)) {
      window.open(permalink, '_blank', 'noopener');
    }
  });

  copyJsonBtn?.addEventListener('click', async () => {
    const payload = buildPayloadFromForm();
    const text = JSON.stringify(payload, null, 2);
    try {
      await navigator.clipboard.writeText(text);
      setStatus('Draft JSON copied to clipboard.', 'success');
    } catch (_error) {
      setStatus('Could not copy JSON.', 'error');
    }
  });

  resetDraftBtn?.addEventListener('click', async () => {
    applyValues({}, DRAFT_FIELDS);
    clearDynamicCustomFieldValues();
    clearDynamicPluginFieldValues();
    await chrome.storage.local.remove(STORAGE_KEY_DRAFT);
    setStatus('Draft reset.', 'success');
    await writeLog('info', 'draft.reset', 'Event draft cleared by user.');
  });

  refreshLogsBtn?.addEventListener('click', refreshLogs);

  copyLogsBtn?.addEventListener('click', async () => {
    const response = await sendRuntimeMessage({ type: 'GET_LOGS' });
    const logs = Array.isArray(response?.logs) ? response.logs : [];
    try {
      await navigator.clipboard.writeText(JSON.stringify(logs, null, 2));
      setStatus('Logs copied to clipboard.', 'success');
    } catch (_error) {
      setStatus('Failed to copy logs.', 'error');
    }
  });

  clearLogsBtn?.addEventListener('click', async () => {
    const ok = window.confirm('Clear all extension logs?');
    if (!ok) {
      return;
    }
    await sendRuntimeMessage({ type: 'CLEAR_LOGS' });
    await refreshLogs();
    setStatus('Logs cleared.', 'success');
  });

  bulkStartBtn?.addEventListener('click', async () => {
    const settings = collectSettings();
    const validation = validateSettings(settings, { requireConnection: true });
    if (!validation.ok) {
      setStatus(validation.error, 'error');
      return;
    }
    if (toBool(settings.bulkTranslateNonEnglish, false)) {
      const aiCheck = validateSettings(settings, { requireOpenAi: true });
      if (!aiCheck.ok) {
        setStatus('Bulk translation requires a valid OpenAI key/model in Settings.', 'error');
        pushToast('Bulk translation requires valid OpenAI settings.', 'error');
        return;
      }
    }

    await syncCustomFieldSchemaFromPlugin(settings, { silent: true });

    const parse = parseBulkUrls(String(bulkUrlsEl?.value || ''));
    if (!parse.urls.length) {
      setStatus('Add at least one valid URL for bulk import.', 'error');
      pushToast('Add at least one valid URL for bulk import.', 'error');
      return;
    }
    if (parse.invalidCount > 0) {
      pushToast(`Skipped ${parse.invalidCount} invalid URL(s).`, 'info');
    }
    if (parse.duplicateCount > 0) {
      pushToast(`Removed ${parse.duplicateCount} duplicate URL(s).`, 'info');
    }

    await saveSettings();
    await saveBulkUrls();
    setBulkButtonsState(true);
    setStatus(`Starting bulk import (${parse.urls.length} URLs)...`);

    const result = await sendRuntimeMessage({
      type: 'START_BULK_IMPORT',
      settings: {
        ...settings,
        customFieldSchema: dynamicFieldSchema,
        formFieldSchema: dynamicFormFieldSchema
      },
      urls: parse.urls
    });
    if (!result?.ok) {
      setBulkButtonsState(false);
      const message = result?.error || 'Could not start bulk import.';
      setStatus(message, 'error');
      pushToast(message, 'error');
      return;
    }

    pushToast(`Bulk import started (${parse.urls.length} URLs).`, 'success');
    startBulkPolling(true);
  });

  bulkStopBtn?.addEventListener('click', async () => {
    const result = await sendRuntimeMessage({ type: 'STOP_BULK_IMPORT' });
    if (!result?.ok) {
      const message = result?.error || 'Could not stop bulk import.';
      setStatus(message, 'error');
      pushToast(message, 'error');
      return;
    }
    setStatus('Stopping bulk import...');
    pushToast('Stop requested. Finishing current URL...', 'info');
    await refreshBulkStatus();
  });

  bulkClearBtn?.addEventListener('click', async () => {
    if (bulkUrlsEl) {
      bulkUrlsEl.value = '';
    }
    await chrome.storage.local.set({ [STORAGE_KEY_BULK_URLS]: '' });
    setStatus('Bulk URL list cleared.', 'success');
  });

  await refreshLogs();
  await refreshBulkStatus();
  startBulkPolling(false);
  if (validateSettings(savedSettings, { requireConnection: true }).ok) {
    await syncCustomFieldSchemaFromPlugin(savedSettings, { silent: true });
  }
  setStatus('Ready.');
}

function setupTabs() {
  const tabs = Array.from(document.querySelectorAll('.epwp-tab'));
  const panes = Array.from(document.querySelectorAll('.epwp-pane'));

  const activate = (name) => {
    tabs.forEach((tab) => {
      const isActive = tab.dataset.tabTarget === name;
      tab.classList.toggle('is-active', isActive);
    });
    panes.forEach((pane) => {
      const isActive = pane.dataset.tabPane === name;
      pane.classList.toggle('is-active', isActive);
    });
  };

  tabs.forEach((tab) => {
    tab.addEventListener('click', () => activate(String(tab.dataset.tabTarget || 'scan')));
  });
}

function normalizeSettings(value) {
  const merged = {
    ...DEFAULT_SETTINGS,
    ...(value && typeof value === 'object' ? value : {})
  };
  return {
    ...merged,
    importStatusDefault: ['draft', 'publish'].includes(String(merged.importStatusDefault || '').trim()) ? String(merged.importStatusDefault || '').trim() : 'draft',
    importMediaPolicy: ['none', 'images', 'video', 'images_video'].includes(String(merged.importMediaPolicy || '').trim()) ? String(merged.importMediaPolicy || '').trim() : 'none',
    importContentMode: ['rewrite', 'verbatim'].includes(String(merged.importContentMode || '').trim()) ? String(merged.importContentMode || '').trim() : 'rewrite',
    useAiByDefault: toBool(merged.useAiByDefault, true) ? '1' : '0',
    autoOpenImportedEvent: toBool(merged.autoOpenImportedEvent, true) ? '1' : '0',
    rememberDraft: toBool(merged.rememberDraft, true) ? '1' : '0',
    verboseLogs: toBool(merged.verboseLogs, false) ? '1' : '0',
    notifyPotentialOwner: toBool(merged.notifyPotentialOwner, false) ? '1' : '0',
    bulkTranslateNonEnglish: toBool(merged.bulkTranslateNonEnglish, false) ? '1' : '0',
    requestTimeoutSeconds: clampNumber(merged.requestTimeoutSeconds, 5, 120, 25).toString(),
    retryAttempts: clampNumber(merged.retryAttempts, 0, 3, 1).toString()
  };
}

function applySettings(settings) {
  const normalized = normalizeSettings(settings);
  applyValues(normalized, SETTINGS_FIELDS);
  SETTINGS_CHECKBOXES.forEach((field) => {
    const el = document.getElementById(field);
    if (!el) {
      return;
    }
    el.checked = normalized[field] === '1';
  });
  const bulkTranslateMainToggle = document.getElementById('bulkTranslateNonEnglish');
  if (bulkTranslateMainToggle && bulkTranslateNonEnglishBulk) {
    bulkTranslateNonEnglishBulk.checked = Boolean(bulkTranslateMainToggle.checked);
  }
}

function collectSettings() {
  const base = collectValues(SETTINGS_FIELDS);
  SETTINGS_CHECKBOXES.forEach((field) => {
    const el = document.getElementById(field);
    base[field] = el && el.checked ? '1' : '0';
  });
  return normalizeSettings(base);
}

function validateSettings(settings, options = {}) {
  if (options.requireConnection) {
    if (!normalizeBaseUrl(settings.pluginBaseUrl)) {
      return { ok: false, error: 'Plugin Site URL is missing or invalid.' };
    }
    if (String(settings.appPass || '').trim().length < 12) {
      return { ok: false, error: 'Extension App Pass appears invalid.' };
    }
  }
  if (options.requireOpenAi) {
    const apiKey = String(settings.openaiApiKey || '').trim();
    if (!apiKey || !/^sk-[A-Za-z0-9]/.test(apiKey)) {
      return { ok: false, error: 'OpenAI API key is missing or invalid.' };
    }
    if (!String(settings.openaiModel || '').trim()) {
      return { ok: false, error: 'OpenAI model is required.' };
    }
  }
  return { ok: true };
}

async function saveSettings(options = {}) {
  const settings = collectSettings();
  const validation = validateSettings(settings, {});
  if (!validation.ok) {
    return validation;
  }
  await chrome.storage.local.set({ [STORAGE_KEY_SETTINGS]: settings });
  if (options.showStatus) {
    setStatus('Settings saved.', 'success');
  }
  return { ok: true, settings };
}

async function syncPluginPreferencesIfPossible(settings) {
  const normalized = normalizeSettings(settings || {});
  const check = validateSettings(normalized, { requireConnection: true });
  if (!check.ok) {
    return { ok: false, error: 'Saved locally. Plugin sync skipped until Site URL and App Pass are valid.' };
  }
  const preferences = {
    import_status: normalized.importStatusDefault,
    media_upload_policy: normalized.importMediaPolicy,
    notify_potential_owner_default: toBool(normalized.notifyPotentialOwner, false),
    content_mode: normalized.importContentMode
  };
  const result = await sendRuntimeMessage({
    type: 'SYNC_PLUGIN_PREFS',
    settings: normalized,
    preferences
  });
  if (!result?.ok) {
    return { ok: false, error: result?.error || 'Saved locally. Plugin sync failed.' };
  }
  return { ok: true };
}

async function syncCustomFieldSchemaFromPlugin(settings, options = {}) {
  const normalized = normalizeSettings(settings || {});
  const check = validateSettings(normalized, { requireConnection: true });
  if (!check.ok) {
    if (!options.silent) {
      setStatus(check.error, 'error');
    }
    return { ok: false, error: check.error };
  }

  if (!options.silent) {
    setStatus('Checking plugin field schema...');
    setTestStatus(pluginTestStatus, null, 'Checking plugin field schema...');
  }
  const result = await sendRuntimeMessage({ type: 'PING_PLUGIN', settings: normalized });
  if (!result?.ok) {
    const error = result?.error || 'Connection failed.';
    if (!options.silent) {
      setStatus(error, 'error');
    }
    return { ok: false, error };
  }

  const pluginName = String(result?.data?.plugin || 'nexdirectory');
  const defaults = result?.data?.defaults && typeof result.data.defaults === 'object' ? result.data.defaults : {};
  if (defaults.import_status) {
    const statusValue = String(defaults.import_status || '').trim();
    if (statusValue === 'draft' || statusValue === 'publish') {
      document.getElementById('importStatusDefault').value = statusValue;
    }
  }
  if (defaults.media_upload_policy) {
    const mediaValue = String(defaults.media_upload_policy || '').trim();
    if (['none', 'images', 'video', 'images_video'].includes(mediaValue)) {
      document.getElementById('importMediaPolicy').value = mediaValue;
    }
  }
  if (defaults.content_mode) {
    const contentModeValue = String(defaults.content_mode || '').trim();
    if (['rewrite', 'verbatim'].includes(contentModeValue)) {
      document.getElementById('importContentMode').value = contentModeValue;
    }
  }
  if (Object.prototype.hasOwnProperty.call(defaults, 'notify_potential_owner_default')) {
    const notifyEl = document.getElementById('notifyPotentialOwner');
    if (notifyEl) {
      notifyEl.checked = toBool(defaults.notify_potential_owner_default, false);
    }
  }

  const previousCustomValues = collectDynamicCustomFieldValues();
  const customSchema = normalizeCustomFieldSchema(result?.data?.custom_fields || []);
  dynamicFieldSchema = customSchema;
  renderDynamicCustomFields(customSchema, previousCustomValues);

  const previousExtraValues = collectDynamicPluginFieldValues();
  const formSchema = normalizePluginFormFieldSchema(result?.data?.form_fields || []);
  dynamicFormFieldSchema = formSchema;
  renderDynamicPluginFields(formSchema, previousExtraValues);
  await chrome.storage.local.set({
    [STORAGE_KEY_DYNAMIC_SCHEMA]: customSchema,
    [STORAGE_KEY_FORM_SCHEMA]: formSchema
  });
  await saveSettings();

  const customFieldCount = customSchema.length;
  const formFieldCount = formSchema.length;
  const coverage = getPluginFieldCoverageDiagnostics(result?.data || {}, formSchema);
  if (!options.silent) {
    let message = `Connected to ${pluginName}. ${customFieldCount} custom field${customFieldCount === 1 ? '' : 's'} and ${formFieldCount} plugin field${formFieldCount === 1 ? '' : 's'} loaded.`;
    if (coverage.missingCount > 0) {
      const preview = coverage.missing.slice(0, 4).join(', ');
      const extra = coverage.missingCount > 4 ? ` (+${coverage.missingCount - 4} more)` : '';
      message += ` Coverage warning: ${coverage.missingCount} field${coverage.missingCount === 1 ? '' : 's'} unmapped (${preview}${extra}).`;
    }
    setStatus(message, 'success');
  }
  return { ok: true, plugin: pluginName, customFieldCount, formFieldCount, coverage };
}

function normalizeCustomFieldSchema(value) {
  const rows = Array.isArray(value) ? value : [];
  const allowedTypes = new Set(['text', 'textarea', 'wysiwyg', 'url', 'email', 'number', 'date', 'datetime-local', 'select', 'checkbox', 'image', 'gallery', 'file']);
  const out = [];
  const seen = new Set();
  rows.forEach((row, index) => {
    if (!row || typeof row !== 'object') {
      return;
    }
    const id = sanitizeKey(row.id || `custom_field_${index + 1}`);
    if (!id || seen.has(id)) {
      return;
    }
    seen.add(id);
    const type = String(row.type || 'text').trim().toLowerCase();
    out.push({
      id,
      label: String(row.label || id).trim() || id,
      type: allowedTypes.has(type) ? type : 'text',
      required: toBool(row.required, false),
      placeholder: String(row.placeholder || '').trim(),
      options: String(row.options || '').trim()
    });
  });
  return out;
}

function normalizePluginFormFieldSchema(value) {
  const rows = Array.isArray(value) ? value : [];
  const out = [];
  const seen = new Set();
  const allowedTypes = new Set(['text', 'textarea', 'url', 'email', 'number', 'date', 'datetime-local', 'select', 'checkbox']);

  rows.forEach((row, index) => {
    if (!row || typeof row !== 'object') {
      return;
    }
    const key = sanitizeKey(row.key || `eep_field_${index + 1}`);
    if (!key || seen.has(key) || CORE_MAPPED_META_KEYS.has(key) || key === 'eep_custom_fields_builder_json') {
      return;
    }
    seen.add(key);
    const rawType = String(row.type || 'text').trim().toLowerCase();
    const type = allowedTypes.has(rawType) ? rawType : 'text';
    out.push({
      key,
      label: String(row.label || key).trim() || key,
      type,
      required: toBool(row.required, false),
      placeholder: String(row.placeholder || '').trim(),
      options: String(row.options || '').trim(),
      source: String(row.source || 'meta').trim() || 'meta'
    });
  });

  out.sort((a, b) => String(a.label || '').localeCompare(String(b.label || '')));
  return out;
}

function getPluginFieldCoverageDiagnostics(pingData, formSchema) {
  const submitFieldNames = Array.isArray(pingData?.submit_fields) ? pingData.submit_fields : [];
  const backendCoreRaw = Array.isArray(pingData?.extension_core_meta_keys) ? pingData.extension_core_meta_keys : [];
  const backendCore = backendCoreRaw
    .map((value) => sanitizeKey(value))
    .filter((value) => value && value.startsWith('eep_'));
  const effectiveCore = new Set([...CORE_MAPPED_META_KEYS, ...backendCore]);
  const dynamicFieldKeys = new Set(
    (Array.isArray(formSchema) ? formSchema : [])
      .map((field) => sanitizeKey(field?.key || ''))
      .filter(Boolean)
  );
  const ignored = new Set([
    'eep_nonce',
    'eep_website',
    'eep_recaptcha_token',
    'eep_autosave_post_id',
    'eep_edit_post_id',
    'eep_custom_fields_builder_json',
    'eep_cover_file',
    'eep_gallery_files',
    'eep_intent',
    'eep_start_date',
    'eep_start_time',
    'eep_end_date',
    'eep_end_time',
    'eep_is_recurring',
    'eep_sessions',
    'eep_youtube_video_id_1',
    'eep_youtube_video_id_2'
  ]);
  const missing = [];
  submitFieldNames.forEach((rawName) => {
    const key = sanitizeKey(rawName);
    if (!key || !key.startsWith('eep_') || ignored.has(key) || key.startsWith('eep_tax_')) {
      return;
    }
    if (effectiveCore.has(key) || dynamicFieldKeys.has(key)) {
      return;
    }
    missing.push(key);
  });
  const uniqueMissing = Array.from(new Set(missing)).sort((a, b) => a.localeCompare(b));
  return {
    checkedCount: submitFieldNames.length,
    missing: uniqueMissing,
    missingCount: uniqueMissing.length
  };
}

function renderDynamicCustomFields(schema, values = {}) {
  if (!customFieldsContainer) {
    return;
  }
  customFieldsContainer.innerHTML = '';
  if (!Array.isArray(schema) || !schema.length) {
    customFieldsContainer.innerHTML = '<p class="epwp-dynamic-empty">No custom fields detected from plugin settings.</p>';
    return;
  }

  schema.forEach((field) => {
    const wrap = document.createElement('label');
    wrap.className = 'full';
    wrap.textContent = field.label + (field.required ? ' *' : '');

    const controlId = getDynamicFieldInputId(field.id);
    const value = Object.prototype.hasOwnProperty.call(values, field.id) ? values[field.id] : '';
    const lowerType = String(field.type || 'text').toLowerCase();

    let input = null;
    if (lowerType === 'textarea' || lowerType === 'wysiwyg' || lowerType === 'gallery') {
      input = document.createElement('textarea');
      input.rows = 3;
      input.value = String(value || '');
    } else if (lowerType === 'select') {
      input = document.createElement('select');
      const empty = document.createElement('option');
      empty.value = '';
      empty.textContent = 'Select option';
      input.appendChild(empty);
      parseDelimitedOptions(field.options).forEach((optionValue) => {
        const option = document.createElement('option');
        option.value = optionValue;
        option.textContent = optionValue;
        if (String(value || '') === optionValue) {
          option.selected = true;
        }
        input.appendChild(option);
      });
    } else if (lowerType === 'checkbox') {
      const inline = document.createElement('div');
      inline.style.marginTop = '6px';
      inline.className = 'epwp-toggle-row';
      const text = document.createElement('span');
      text.className = 'epwp-toggle-text';
      text.textContent = 'Enabled';
      const switchWrap = document.createElement('label');
      switchWrap.className = 'epwp-switch';
      input = document.createElement('input');
      input.type = 'checkbox';
      input.checked = toBool(value, false);
      const slider = document.createElement('span');
      slider.className = 'epwp-slider';
      switchWrap.appendChild(input);
      switchWrap.appendChild(slider);
      inline.appendChild(text);
      inline.appendChild(switchWrap);
      wrap.appendChild(inline);
    } else {
      input = document.createElement('input');
      input.type = mapCustomFieldTypeToInputType(lowerType);
      input.value = String(value || '');
    }

    if (input) {
      input.id = controlId;
      input.dataset.customFieldId = field.id;
      input.dataset.customFieldType = lowerType;
      if (field.placeholder && input.tagName !== 'SELECT') {
        input.placeholder = field.placeholder;
      }
      input.addEventListener('input', debounce(saveDraft, 180));
      input.addEventListener('change', debounce(saveDraft, 180));
      if (lowerType !== 'checkbox') {
        wrap.appendChild(input);
      }
    }
    customFieldsContainer.appendChild(wrap);
  });
}

function renderDynamicPluginFields(schema, values = {}) {
  if (!formFieldsContainer) {
    return;
  }
  formFieldsContainer.innerHTML = '';
  if (!Array.isArray(schema) || !schema.length) {
    formFieldsContainer.innerHTML = '<p class="epwp-dynamic-empty">No extra plugin fields detected.</p>';
    return;
  }

  schema.forEach((field) => {
    const wrap = document.createElement('label');
    wrap.className = 'full';
    wrap.textContent = field.label + (field.required ? ' *' : '');

    const controlId = getPluginFieldInputId(field.key);
    const value = Object.prototype.hasOwnProperty.call(values, field.key) ? values[field.key] : '';
    const lowerType = String(field.type || 'text').toLowerCase();
    let input = null;

    if (lowerType === 'textarea') {
      input = document.createElement('textarea');
      input.rows = 3;
      input.value = String(value || '');
    } else if (lowerType === 'select') {
      input = document.createElement('select');
      const empty = document.createElement('option');
      empty.value = '';
      empty.textContent = 'Select option';
      input.appendChild(empty);
      parseDelimitedOptions(field.options).forEach((optionValue) => {
        const option = document.createElement('option');
        option.value = optionValue;
        option.textContent = optionValue;
        if (String(value || '') === optionValue) {
          option.selected = true;
        }
        input.appendChild(option);
      });
    } else if (lowerType === 'checkbox') {
      const inline = document.createElement('div');
      inline.style.marginTop = '6px';
      inline.className = 'epwp-toggle-row';
      const text = document.createElement('span');
      text.className = 'epwp-toggle-text';
      text.textContent = 'Enabled';
      const switchWrap = document.createElement('label');
      switchWrap.className = 'epwp-switch';
      input = document.createElement('input');
      input.type = 'checkbox';
      input.checked = toBool(value, false);
      const slider = document.createElement('span');
      slider.className = 'epwp-slider';
      switchWrap.appendChild(input);
      switchWrap.appendChild(slider);
      inline.appendChild(text);
      inline.appendChild(switchWrap);
      wrap.appendChild(inline);
    } else {
      input = document.createElement('input');
      input.type = mapCustomFieldTypeToInputType(lowerType);
      input.value = String(value || '');
    }

    if (input) {
      input.id = controlId;
      input.dataset.formFieldKey = field.key;
      input.dataset.formFieldType = lowerType;
      if (field.placeholder && input.tagName !== 'SELECT') {
        input.placeholder = field.placeholder;
      }
      if (field.options && input.tagName !== 'SELECT') {
        input.title = `Suggested values: ${field.options.replace(/\n/g, ', ')}`;
      }
      input.addEventListener('input', debounce(saveDraft, 180));
      input.addEventListener('change', debounce(saveDraft, 180));
      if (lowerType !== 'checkbox') {
        wrap.appendChild(input);
      }
    }
    formFieldsContainer.appendChild(wrap);
  });
}

function applyDynamicCustomFieldValues(values = {}) {
  const map = values && typeof values === 'object' ? values : {};
  dynamicFieldSchema.forEach((field) => {
    const el = document.getElementById(getDynamicFieldInputId(field.id));
    if (!el) {
      return;
    }
    const next = Object.prototype.hasOwnProperty.call(map, field.id) ? map[field.id] : '';
    if (el.type === 'checkbox') {
      el.checked = toBool(next, false);
    } else {
      el.value = String(next || '');
    }
  });
}

function applyDynamicPluginFieldValues(values = {}) {
  const map = values && typeof values === 'object' ? values : {};
  dynamicFormFieldSchema.forEach((field) => {
    const el = document.getElementById(getPluginFieldInputId(field.key));
    if (!el) {
      return;
    }
    const next = Object.prototype.hasOwnProperty.call(map, field.key) ? map[field.key] : '';
    if (el.type === 'checkbox') {
      el.checked = toBool(next, false);
    } else {
      el.value = String(next || '');
    }
  });
}

function clearDynamicCustomFieldValues() {
  dynamicFieldSchema.forEach((field) => {
    const el = document.getElementById(getDynamicFieldInputId(field.id));
    if (!el) {
      return;
    }
    if (el.type === 'checkbox') {
      el.checked = false;
    } else {
      el.value = '';
    }
  });
}

function clearDynamicPluginFieldValues() {
  dynamicFormFieldSchema.forEach((field) => {
    const el = document.getElementById(getPluginFieldInputId(field.key));
    if (!el) {
      return;
    }
    if (el.type === 'checkbox') {
      el.checked = false;
    } else {
      el.value = '';
    }
  });
}

function collectDynamicCustomFieldValues() {
  const out = {};
  dynamicFieldSchema.forEach((field) => {
    const el = document.getElementById(getDynamicFieldInputId(field.id));
    if (!el) {
      return;
    }
    if (el.type === 'checkbox') {
      out[field.id] = el.checked ? '1' : '0';
      return;
    }
    const value = String(el.value || '').trim();
    if (value !== '') {
      out[field.id] = value;
    }
  });
  return out;
}

function collectDynamicPluginFieldValues() {
  const out = {};
  dynamicFormFieldSchema.forEach((field) => {
    const el = document.getElementById(getPluginFieldInputId(field.key));
    if (!el) {
      return;
    }
    if (el.type === 'checkbox') {
      out[field.key] = el.checked ? '1' : '0';
      return;
    }
    const value = String(el.value || '').trim();
    if (value !== '') {
      out[field.key] = value;
    }
  });
  return out;
}

function getDynamicFieldInputId(fieldId) {
  return `custom_field_${sanitizeKey(fieldId)}`;
}

function getPluginFieldInputId(fieldKey) {
  return `plugin_field_${sanitizeKey(fieldKey)}`;
}

function sanitizeKey(value) {
  return String(value || '')
    .toLowerCase()
    .replace(/[^a-z0-9_]+/g, '_')
    .replace(/^_+|_+$/g, '')
    .slice(0, 64);
}

function mapCustomFieldTypeToInputType(type) {
  if (type === 'number') return 'number';
  if (type === 'email') return 'email';
  if (type === 'url' || type === 'image' || type === 'file' || type === 'gallery') return 'url';
  if (type === 'date') return 'date';
  if (type === 'datetime-local') return 'datetime-local';
  return 'text';
}

function parseDelimitedOptions(value) {
  return String(value || '')
    .split(/[\n,|]/)
    .map((item) => item.trim())
    .filter(Boolean);
}

async function saveDraft() {
  const settings = collectSettings();
  if (!toBool(settings.rememberDraft, true)) {
    return;
  }
  const payload = buildPayloadFromForm();
  await chrome.storage.local.set({ [STORAGE_KEY_DRAFT]: payload });
}

function collectValues(fields) {
  const out = {};
  fields.forEach((field) => {
    const el = document.getElementById(field);
    if (!el) {
      return;
    }
    out[field] = String(el.value || '').trim();
  });
  return out;
}

function applyValues(values, fields) {
  fields.forEach((field) => {
    const el = document.getElementById(field);
    if (!el) {
      return;
    }
    if (Object.prototype.hasOwnProperty.call(values, field)) {
      el.value = values[field] || '';
    }
  });
}

function applyEventPayloadToForm(eventData) {
  const normalized = normalizePayloadForForm(eventData || {});
  Object.keys(normalized).forEach((key) => {
    const el = document.getElementById(key);
    if (!el) {
      return;
    }
    el.value = normalized[key];
  });
  applyDynamicCustomFieldValues(normalized.custom_fields || {});
  applyDynamicPluginFieldValues(normalized.extra_fields || {});
}

function normalizePayloadForForm(payload) {
  const customMap = payload?.custom_fields && typeof payload.custom_fields === 'object'
    ? payload.custom_fields
    : (payload?.custom_field_values && typeof payload.custom_field_values === 'object' ? payload.custom_field_values : {});
  const extraMap = payload?.extra_fields && typeof payload.extra_fields === 'object'
    ? payload.extra_fields
    : {};
  const normalizedExtra = {};
  Object.entries(payload || {}).forEach(([rawKey, rawValue]) => {
    const key = sanitizeKey(rawKey);
    if (!key || CORE_MAPPED_META_KEYS.has(key)) {
      return;
    }
    if (!(key.startsWith('eep_') || key.startsWith('eep_tax_'))) {
      return;
    }
    if (rawValue && typeof rawValue === 'object') {
      if (Array.isArray(rawValue)) {
        const values = rawValue.map((item) => String(item || '').trim()).filter(Boolean);
        if (values.length) {
          normalizedExtra[key] = values.join(', ');
        }
      }
      return;
    }
    const cleanValue = String(rawValue || '').trim();
    if (cleanValue !== '') {
      normalizedExtra[key] = cleanValue;
    }
  });
  Object.entries(extraMap || {}).forEach(([rawKey, rawValue]) => {
    const key = sanitizeKey(rawKey);
    if (!key || (!(key.startsWith('eep_') || key.startsWith('eep_tax_')))) {
      return;
    }
    normalizedExtra[key] = String(rawValue ?? '').trim();
  });
  const normalizedTags = sanitizeTags(splitTags(Array.isArray(payload.event_tags) ? payload.event_tags.join(', ') : String(payload.event_tags || '')));
  return {
    title: String(payload.title || ''),
    start: toDateTimeLocalInput(payload.start),
    end: toDateTimeLocalInput(payload.end),
    meta_title: String(payload.meta_title || payload.eep_meta_title || ''),
    meta_description: String(payload.meta_description || payload.eep_meta_description || ''),
    timezone: String(payload.timezone || ''),
    venue: String(payload.venue || ''),
    address: String(payload.address || ''),
    city: String(payload.city || ''),
    country: String(payload.country || ''),
    event_type: String(payload.event_type || ''),
    event_mode: String(payload.event_mode || ''),
    event_price_range: String(payload.event_price_range || ''),
    event_tags: normalizedTags.join(', '),
    capacity: String(payload.capacity || ''),
    price: String(payload.price || ''),
    ticket_sales_enabled: String(payload.ticket_sales_enabled || payload.eep_ticket_sales_enabled || '0') === '1' ? '1' : '0',
    ticket_price: String(payload.ticket_price || payload.eep_ticket_price || ''),
    ticket_button_label: String(payload.ticket_button_label || payload.eep_ticket_button_label || ''),
    recurrence: String(payload.recurrence || ''),
    recurrence_until: toDateTimeLocalInput(payload.recurrence_until),
    external_url: String(payload.external_url || ''),
    stream_url: String(payload.stream_url || payload.video_stream_url || ''),
    contact_name: String(payload.contact_name || ''),
    contact_email: String(payload.contact_email || ''),
    contact_phone: String(payload.contact_phone || ''),
    cover_image_url: String(payload.cover_image_url || ''),
    official_event: String(payload.official_event || '0') === '1' ? '1' : '0',
    excerpt: String(payload.excerpt || ''),
    description: String(payload.description || ''),
    gallery_urls: arrayLikeToMultiline(payload.gallery_urls),
    video_urls: arrayLikeToMultiline(payload.video_urls),
    supporting_docs_urls: arrayLikeToMultiline(payload.supporting_docs_urls || payload.supporting_docs || payload.documents || []),
    sessions: formatSessionsForTextarea(payload.sessions || payload.itinerary || payload.agenda),
    key_speakers: formatKeySpeakersForTextarea(payload.key_speakers || payload.speakers || payload.speaker_profiles),
    custom_fields: customMap,
    extra_fields: normalizedExtra
  };
}

function buildPayloadFromForm() {
  const raw = collectValues(DRAFT_FIELDS);
  const customFields = collectDynamicCustomFieldValues();
  const extraFields = collectDynamicPluginFieldValues();
  return {
    title: raw.title,
    start: fromDateTimeLocalInput(raw.start),
    end: fromDateTimeLocalInput(raw.end),
    meta_title: raw.meta_title,
    meta_description: raw.meta_description,
    timezone: raw.timezone,
    venue: raw.venue,
    address: raw.address,
    city: raw.city,
    country: raw.country,
    event_type: raw.event_type,
    event_mode: raw.event_mode,
    event_price_range: raw.event_price_range,
    event_tags: sanitizeTags(splitTags(raw.event_tags)),
    capacity: raw.capacity,
    price: raw.price,
    ticket_sales_enabled: raw.ticket_sales_enabled === '1' ? '1' : '0',
    ticket_price: raw.ticket_price,
    ticket_button_label: raw.ticket_button_label,
    recurrence: raw.recurrence || 'none',
    recurrence_until: fromDateTimeLocalInput(raw.recurrence_until),
    external_url: raw.external_url,
    stream_url: raw.stream_url,
    contact_name: raw.contact_name,
    contact_email: raw.contact_email,
    contact_phone: raw.contact_phone,
    cover_image_url: raw.cover_image_url,
    official_event: raw.official_event === '1' ? '1' : '0',
    excerpt: raw.excerpt,
    description: raw.description,
    gallery_urls: splitLines(raw.gallery_urls),
    video_urls: splitLines(raw.video_urls),
    supporting_docs_urls: splitLines(raw.supporting_docs_urls),
    sessions: parseSessions(raw.sessions),
    key_speakers: parseKeySpeakers(raw.key_speakers),
    custom_fields: customFields,
    extra_fields: extraFields
  };
}

function splitLines(value) {
  return String(value || '')
    .split(/\r?\n/)
    .map((line) => line.trim())
    .filter(Boolean);
}

function splitTags(value) {
  return String(value || '')
    .split(/[,\n|]/)
    .map((part) => part.trim())
    .filter(Boolean);
}

function sanitizeTags(tags) {
  const rows = Array.isArray(tags) ? tags : [];
  const out = [];
  rows.forEach((tag) => {
    let clean = String(tag || '').trim();
    if (!clean) {
      return;
    }
    const chars = Array.from(clean);
    if (chars.length > 40) {
      clean = chars.slice(0, 40).join('');
    }
    if (!clean || out.includes(clean) || out.length >= 8) {
      return;
    }
    out.push(clean);
  });
  return out;
}

function toDateTimeLocalInput(value) {
  const raw = String(value || '').trim();
  if (!raw) {
    return '';
  }
  if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(raw)) {
    return raw.slice(0, 16);
  }
  if (/^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}/.test(raw)) {
    return raw.replace(/\s+/, 'T').slice(0, 16);
  }
  const match = raw.match(/^(\d{1,2})\/(\d{1,2})\/(\d{2,4})(?:\s+(\d{1,2}):(\d{2}))?$/);
  if (match) {
    const day = String(parseInt(match[1], 10)).padStart(2, '0');
    const month = String(parseInt(match[2], 10)).padStart(2, '0');
    let year = String(match[3] || '').trim();
    if (year.length === 2) {
      year = '20' + year;
    }
    const hour = String(parseInt(match[4] || '0', 10)).padStart(2, '0');
    const minute = String(parseInt(match[5] || '0', 10)).padStart(2, '0');
    return `${year}-${month}-${day}T${hour}:${minute}`;
  }
  const parsed = new Date(raw);
  if (!parsed || Number.isNaN(parsed.getTime())) {
    return '';
  }
  const pad = (n) => String(n).padStart(2, '0');
  return `${parsed.getFullYear()}-${pad(parsed.getMonth() + 1)}-${pad(parsed.getDate())}T${pad(parsed.getHours())}:${pad(parsed.getMinutes())}`;
}

function fromDateTimeLocalInput(value) {
  const raw = String(value || '').trim();
  if (!raw) {
    return '';
  }
  if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(raw)) {
    return raw.slice(0, 16).replace('T', ' ');
  }
  const normalized = toDateTimeLocalInput(raw);
  if (!normalized) {
    return '';
  }
  return normalized.replace('T', ' ');
}

function arrayLikeToMultiline(value) {
  if (Array.isArray(value)) {
    return value.map((item) => String(item || '').trim()).filter(Boolean).join('\n');
  }
  return String(value || '');
}

function formatSessionsForTextarea(value) {
  if (!Array.isArray(value) || !value.length) {
    return '';
  }
  try {
    return JSON.stringify(value, null, 2);
  } catch (_error) {
    return '';
  }
}

function formatKeySpeakersForTextarea(value) {
  if (!Array.isArray(value) || !value.length) {
    return '';
  }
  try {
    return JSON.stringify(value, null, 2);
  } catch (_error) {
    return '';
  }
}

function parseSessions(value) {
  const raw = String(value || '').trim();
  if (!raw) {
    return [];
  }
  try {
    const parsed = JSON.parse(raw);
    if (!Array.isArray(parsed)) {
      return [];
    }
    return parsed
      .map((session) => {
        if (!session || typeof session !== 'object') {
          return null;
        }
        return {
          title: String(session.title || '').trim(),
          start: String(session.start || '').trim(),
          end: String(session.end || '').trim(),
          location: String(session.location || '').trim(),
          speaker: String(session.speaker || '').trim()
        };
      })
      .filter(Boolean);
  } catch (_error) {
    setStatus('Sessions JSON is invalid. It will be ignored.', 'error');
    return [];
  }
}

function parseKeySpeakers(value) {
  const raw = String(value || '').trim();
  if (!raw) {
    return [];
  }
  try {
    const parsed = JSON.parse(raw);
    if (!Array.isArray(parsed)) {
      return [];
    }
    return parsed
      .map((row) => {
        if (!row || typeof row !== 'object') {
          return null;
        }
        return {
          name: String(row.name || '').trim(),
          profession: String(row.profession || row.professional || '').trim(),
          subject: String(row.subject || '').trim(),
          image: String(row.image || row.image_url || '').trim(),
          details: String(row.details || '').trim()
        };
      })
      .filter(Boolean);
  } catch (_error) {
    setStatus('Key speakers JSON is invalid. It will be ignored.', 'error');
    return [];
  }
}

async function refreshLogs() {
  const response = await sendRuntimeMessage({ type: 'GET_LOGS' });
  const logs = Array.isArray(response?.logs) ? response.logs : [];
  renderLogs(logs);
}

function renderLogs(logs) {
  if (!logsContainer) {
    return;
  }
  if (logCountBadge) {
    logCountBadge.textContent = String(logs.length || 0);
  }
  logsContainer.innerHTML = '';
  if (!logs.length) {
    const empty = document.createElement('div');
    empty.className = 'epwp-log-item';
    empty.innerHTML = '<p class="epwp-log-message">No logs yet.</p>';
    logsContainer.appendChild(empty);
    return;
  }

  logs.forEach((entry) => {
    const item = document.createElement('article');
    item.className = 'epwp-log-item';

    const row = document.createElement('div');
    row.className = 'epwp-log-row';

    const level = document.createElement('span');
    const levelName = String(entry?.level || 'info').toLowerCase();
    level.className = `epwp-log-level ${levelName}`;
    level.textContent = levelName;

    const time = document.createElement('span');
    time.className = 'epwp-log-time';
    time.textContent = formatTime(entry?.ts);

    row.appendChild(level);
    row.appendChild(time);

    const message = document.createElement('p');
    message.className = 'epwp-log-message';
    const scope = String(entry?.scope || '').trim();
    const msg = String(entry?.message || '').trim();
    message.textContent = scope ? `[${scope}] ${msg}` : msg;

    item.appendChild(row);
    item.appendChild(message);
    logsContainer.appendChild(item);
  });
}

async function writeLog(level, scope, message, meta = {}) {
  await sendRuntimeMessage({
    type: 'WRITE_LOG',
    entry: {
      level: String(level || 'info'),
      scope: String(scope || ''),
      message: String(message || ''),
      meta
    }
  });
}

function promptNotifyOwnerConfirmation(payload, settings) {
  const modal = document.getElementById('notifyOwnerModal');
  const enabledEl = document.getElementById('notifyOwnerModalEnabled');
  const emailEl = document.getElementById('notifyOwnerModalEmail');
  const cancelBtn = document.getElementById('notifyOwnerModalCancel');
  const continueBtn = document.getElementById('notifyOwnerModalContinue');

  if (!modal || !enabledEl || !emailEl || !cancelBtn || !continueBtn) {
    return Promise.resolve({
      cancelled: false,
      notify: toBool(settings?.notifyPotentialOwner, false),
      email: String(payload?.contact_email || '').trim()
    });
  }

  return new Promise((resolve) => {
    const defaultEmail = String(payload?.contact_email || '').trim();
    const close = (result) => {
      modal.classList.remove('is-open');
      modal.setAttribute('aria-hidden', 'true');
      cancelBtn.removeEventListener('click', onCancel);
      continueBtn.removeEventListener('click', onContinue);
      enabledEl.removeEventListener('change', onToggle);
      resolve(result);
    };
    const onCancel = () => close({ cancelled: true, notify: false, email: '' });
    const onContinue = () => {
      const notify = Boolean(enabledEl.checked);
      const email = String(emailEl.value || '').trim();
      if (notify && !email) {
        setStatus('Recipient email is required when notification is enabled.', 'error');
        emailEl.focus();
        return;
      }
      if (notify && email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
        setStatus('Recipient email format looks invalid.', 'error');
        emailEl.focus();
        return;
      }
      close({ cancelled: false, notify, email: notify ? email : '' });
    };
    const onToggle = () => {
      emailEl.disabled = !enabledEl.checked;
      if (!enabledEl.checked) {
        emailEl.classList.remove('error');
      }
    };

    enabledEl.checked = toBool(settings?.notifyPotentialOwner, false);
    emailEl.value = defaultEmail;
    emailEl.disabled = !enabledEl.checked;
    modal.classList.add('is-open');
    modal.setAttribute('aria-hidden', 'false');

    cancelBtn.addEventListener('click', onCancel);
    continueBtn.addEventListener('click', onContinue);
    enabledEl.addEventListener('change', onToggle);
    if (enabledEl.checked) {
      emailEl.focus();
      emailEl.select();
    } else {
      continueBtn.focus();
    }
  });
}

function pushToast(message, type = 'info', duration = 4800) {
  if (!toastStack) {
    return;
  }
  const text = String(message || '').trim();
  if (!text) {
    return;
  }

  const tone = ['success', 'error', 'info'].includes(String(type || '').trim()) ? String(type).trim() : 'info';
  const icon = tone === 'success' ? '✓' : tone === 'error' ? '!' : 'i';

  const toast = document.createElement('div');
  toast.className = `epwp-toast is-${tone}`;
  toast.setAttribute('role', tone === 'error' ? 'alert' : 'status');
  toast.innerHTML = `
    <span class="epwp-toast-icon" aria-hidden="true">${icon}</span>
    <p class="epwp-toast-message"></p>
  `;
  const messageEl = toast.querySelector('.epwp-toast-message');
  if (messageEl) {
    messageEl.textContent = text;
  }
  toastStack.appendChild(toast);

  requestAnimationFrame(() => {
    toast.classList.add('is-visible');
  });

  window.setTimeout(() => {
    toast.classList.remove('is-visible');
    window.setTimeout(() => {
      toast.remove();
    }, 220);
  }, Math.max(1800, Number.parseInt(String(duration || 4800), 10) || 4800));
}

function setStatus(message, type = '') {
  if (!statusLine) {
    return;
  }
  statusLine.textContent = message;
  statusLine.classList.remove('error', 'success');
  if (type) {
    statusLine.classList.add(type);
  }
}

function setTestStatus(el, passed, message) {
  if (!el) {
    return;
  }
  const labelEl = el.querySelector('.epwp-test-label');
  if (passed === true) {
    el.setAttribute('data-state', 'success');
  } else if (passed === false) {
    el.setAttribute('data-state', 'error');
  } else {
    el.setAttribute('data-state', 'idle');
  }
  if (labelEl) {
    const prefix = el.id === 'openaiTestStatus' ? 'OpenAI: ' : 'Plugin: ';
    labelEl.textContent = prefix + String(message || (passed === true ? 'Pass' : passed === false ? 'Fail' : 'Not tested'));
  }
}

function setBusy(button, busy) {
  if (!button) {
    return;
  }
  button.disabled = busy;
}

function setScanLoadingOverlay(active) {
  if (!scanLoadingOverlay) {
    return;
  }
  if (active) {
    scanLoadingOverlay.classList.add('is-active');
    scanLoadingOverlay.setAttribute('aria-hidden', 'false');
    return;
  }
  scanLoadingOverlay.classList.remove('is-active');
  scanLoadingOverlay.setAttribute('aria-hidden', 'true');
}

function toBool(value, fallback = false) {
  if (typeof value === 'boolean') {
    return value;
  }
  const normalized = String(value || '').trim().toLowerCase();
  if (value === '1' || value === 1 || normalized === 'true' || normalized === 'yes' || normalized === 'on') {
    return true;
  }
  if (value === '0' || value === 0 || normalized === 'false' || normalized === 'no' || normalized === 'off') {
    return false;
  }
  return fallback;
}

function clampNumber(value, min, max, fallback) {
  const n = Number.parseInt(String(value || ''), 10);
  if (Number.isNaN(n)) {
    return fallback;
  }
  if (n < min) {
    return min;
  }
  if (n > max) {
    return max;
  }
  return n;
}

function normalizeBaseUrl(value) {
  const raw = String(value || '').trim();
  if (!raw) {
    return '';
  }
  try {
    const url = new URL(raw.startsWith('http') ? raw : `https://${raw}`);
    return url.origin;
  } catch (_error) {
    return '';
  }
}

function formatTime(ts) {
  if (!ts) {
    return '';
  }
  try {
    return new Date(ts).toLocaleString();
  } catch (_error) {
    return String(ts);
  }
}

function debounce(fn, delay) {
  let timer = null;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}

async function saveBulkUrls() {
  const value = String(bulkUrlsEl?.value || '');
  await chrome.storage.local.set({ [STORAGE_KEY_BULK_URLS]: value });
}

function normalizeBulkUrl(raw) {
  const value = String(raw || '').trim();
  if (!value) {
    return '';
  }
  try {
    const parsed = new URL(/^https?:\/\//i.test(value) ? value : `https://${value}`);
    if (!/^https?:$/i.test(parsed.protocol)) {
      return '';
    }
    parsed.hash = '';
    return parsed.toString();
  } catch (_error) {
    return '';
  }
}

function parseBulkUrls(text) {
  const lines = String(text || '')
    .split(/\r?\n/)
    .map((line) => line.trim())
    .filter(Boolean);

  const urls = [];
  const seen = new Set();
  let invalidCount = 0;
  let duplicateCount = 0;

  lines.forEach((line) => {
    const normalized = normalizeBulkUrl(line);
    if (!normalized) {
      invalidCount += 1;
      return;
    }
    if (seen.has(normalized)) {
      duplicateCount += 1;
      return;
    }
    seen.add(normalized);
    urls.push(normalized);
  });

  return { urls, invalidCount, duplicateCount };
}

function startBulkPolling(immediate = false) {
  if (bulkStatusTimer) {
    clearInterval(bulkStatusTimer);
    bulkStatusTimer = null;
  }
  if (immediate) {
    void refreshBulkStatus();
  }
  bulkStatusTimer = setInterval(() => {
    void refreshBulkStatus();
  }, 1200);
}

async function refreshBulkStatus() {
  const response = await sendRuntimeMessage({ type: 'GET_BULK_STATUS' });
  if (!response?.ok) {
    return;
  }
  renderBulkState(response.state || {});
}

function renderBulkState(state) {
  const running = Boolean(state?.running);
  const stopRequested = Boolean(state?.stopRequested);
  const total = Number.parseInt(String(state?.total || 0), 10) || 0;
  const processed = Number.parseInt(String(state?.processed || 0), 10) || 0;
  const success = Number.parseInt(String(state?.success || 0), 10) || 0;
  const failed = Number.parseInt(String(state?.failed || 0), 10) || 0;
  const percent = total > 0 ? Math.min(100, Math.round((processed / total) * 100)) : 0;
  const current = String(state?.currentUrl || '').trim();
  const results = Array.isArray(state?.results) ? state.results : [];

  if (bulkStatusSummary) {
    if (running) {
      const prefix = stopRequested ? 'Stopping...' : 'Running...';
      bulkStatusSummary.textContent = `${prefix} ${processed}/${total} processed • ${success} success • ${failed} failed`;
    } else if (total > 0) {
      bulkStatusSummary.textContent = `Completed ${processed}/${total} • ${success} success • ${failed} failed`;
    } else {
      bulkStatusSummary.textContent = 'No bulk job running.';
    }
  }
  if (bulkProgressBar) {
    bulkProgressBar.style.width = `${percent}%`;
  }
  if (bulkCurrentUrl) {
    bulkCurrentUrl.textContent = running && current ? `Current: ${current}` : '';
  }
  renderBulkResults(results);
  setBulkButtonsState(running, stopRequested);
}

function renderBulkResults(results) {
  if (!bulkResults) {
    return;
  }
  bulkResults.innerHTML = '';
  if (!Array.isArray(results) || !results.length) {
    const empty = document.createElement('p');
    empty.className = 'epwp-muted-text';
    empty.textContent = 'No bulk URL results yet.';
    bulkResults.appendChild(empty);
    return;
  }

  results.slice(0, 120).forEach((row) => {
    const item = document.createElement('article');
    const tone = String(row?.status || '').trim() === 'success' ? 'success' : 'error';
    item.className = `epwp-bulk-result epwp-bulk-result-${tone}`;

    const url = String(row?.url || '').trim();
    const message = String(row?.message || '').trim();
    const ts = formatTime(row?.ts);
    const permalink = String(row?.permalink || '').trim();

    item.innerHTML = `
      <div class="epwp-bulk-row-head">
        <span class="epwp-bulk-status">${tone === 'success' ? 'Imported' : 'Failed'}</span>
        <span class="epwp-bulk-time">${escapeHtml(ts)}</span>
      </div>
      <p class="epwp-bulk-url">${escapeHtml(url)}</p>
      <p class="epwp-bulk-message">${escapeHtml(message)}</p>
      ${permalink ? `<a class="epwp-bulk-link" href="${escapeHtml(permalink)}" target="_blank" rel="noopener noreferrer">Open listing</a>` : ''}
    `;
    bulkResults.appendChild(item);
  });
}

function setBulkButtonsState(running, stopRequested = false) {
  if (bulkStartBtn) {
    bulkStartBtn.disabled = Boolean(running);
  }
  if (bulkStopBtn) {
    bulkStopBtn.disabled = !running || Boolean(stopRequested);
  }
}

function escapeHtml(value) {
  return String(value || '')
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

function sendRuntimeMessage(message) {
  return new Promise((resolve) => {
    chrome.runtime.sendMessage(message, (response) => {
      if (chrome.runtime.lastError) {
        resolve({ ok: false, error: chrome.runtime.lastError.message });
        return;
      }
      resolve(response || { ok: false, error: 'No response from background worker.' });
    });
  });
}
