(function (wp) {
  'use strict';

  const { __ } = wp.i18n;
  const { registerPlugin } = wp.plugins;
  const { PluginSidebar, PluginSidebarMoreMenuItem, PluginDocumentSettingPanel } = wp.editPost || {};
  const { PanelBody, ToggleControl, TextControl, TextareaControl, CheckboxControl, Notice, Button, BaseControl } = wp.components;
  const { MediaUpload, MediaUploadCheck } = wp.blockEditor || wp.editor || {};
  const { useSelect, useDispatch } = wp.data;
  const { createElement: el, Fragment } = wp.element;

  const cfg = window.CALLIOPE_SP_EDITOR || {};
  const metaKeys = cfg.metaKeys || {};
  const destinations = cfg.destinations || [];
  const i18n = cfg.i18n || {};
  const features = cfg.features || {};
  const proStatus = cfg.proStatus || {};
  const mediaCapabilities = cfg.mediaCapabilities || {};
  const youtubeCfg = cfg.youtube || {};
  const remoteWordPressCfg = cfg.remoteWordPress || {};
  const customWebhookCfg = cfg.customWebhook || {};
  const schedulerCfg = cfg.scheduler || {};
  const supportedPostTypes = Array.isArray(cfg.supportedPostTypes) ? cfg.supportedPostTypes : ['post', 'page'];

  function t(key, fallback) { return i18n[key] || fallback; }

  function supportedMediaNetworks(mode) {
    const labels = { discord: 'Discord', instagram: 'Instagram', threads: 'Threads', youtube: 'YouTube', youtube_shorts: 'YouTube Shorts', wordpress_remote: 'Remote WordPress', custom_webhook: 'Custom webhook' };
    return Object.keys(mediaCapabilities).filter((network) => {
      const definition = mediaCapabilities[network] || {};
      return !!definition[mode] && !!labels[network];
    }).map((network) => labels[network]);
  }

  function normalizeHashtags(value) {
    const raw = String(value || '').replace(/[\r\n\t,;]+/gu, ' ').replace(/\s+/gu, ' ').trim();
    if (!raw) return '';
    const normalized = [];
    const seen = new Set();
    raw.split(' ').forEach((part) => {
      const tag = String(part || '')
        .replace(/^[#\s,;]+/gu, '')
        .replace(/[#\s,;]+$/gu, '')
        .replace(/[^\p{L}\p{M}\p{N}_]+/gu, '');
      if (!tag) return;
      const key = tag.toLocaleLowerCase();
      if (seen.has(key)) return;
      seen.add(key);
      normalized.push('#' + tag);
    });
    return normalized.join(' ');
  }

  function normalizeIds(value, max) {
    const seen = new Set();
    const ids = [];
    (Array.isArray(value) ? value : []).forEach((raw) => {
      const id = Number.parseInt(raw, 10);
      if (!Number.isInteger(id) || id <= 0 || seen.has(id) || ids.length >= max) return;
      seen.add(id);
      ids.push(id);
    });
    return ids;
  }


  function twitterWeightedLength(value) {
    const textValue = String(value || '').replace(/\r\n?/g, '\n').replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, '');
    const regex = /https?:\/\/[^\s<>"']+/giu;
    let total = 0, last = 0, match;
    while ((match = regex.exec(textValue)) !== null) {
      total += Array.from(textValue.slice(last, match.index)).length + 23;
      last = match.index + match[0].length;
    }
    return total + Array.from(textValue.slice(last)).length;
  }

  function twitterFinalText(title, content, link, hashtags) {
    const main = String(content || '').trim() || String(title || '').trim();
    const parts = [];
    if (main) parts.push(main);
    if (link && main.indexOf(link) === -1) parts.push(link);
    if (hashtags) parts.push(hashtags);
    return parts.join('\n\n');
  }

  function CalliopeSPPanel() {
    const postType = useSelect((select) => select('core/editor').getCurrentPostType());
    const postLink = useSelect((select) => select('core/editor').getEditedPostAttribute('link') || '');
    const isSupported = supportedPostTypes.includes(postType);
    const meta = useSelect((select) => select('core/editor').getEditedPostAttribute('meta') || {});
    const { editPost } = useDispatch('core/editor');

    const enabled = !!meta[metaKeys.enabled];
    const posted = !!meta[metaKeys.posted];
    const timingMode = metaKeys.timingMode && meta[metaKeys.timingMode] === 'scheduled' ? 'scheduled' : 'on_publish';
    const scheduledLocal = metaKeys.scheduledLocal ? (meta[metaKeys.scheduledLocal] || '') : '';
    const title = meta[metaKeys.title] || '';
    const text = meta[metaKeys.text] || '';
    const hashtags = meta[metaKeys.hashtags] || '';
    const normalizedHashtags = normalizeHashtags(hashtags);
    const dest = Array.isArray(meta[metaKeys.destinations]) ? meta[metaKeys.destinations] : [];
    const lastLogRaw = metaKeys.lastLog ? meta[metaKeys.lastLog] : '';
    const storedMediaMode = ['single_image', 'multiple_images', 'video', 'none'].includes(meta[metaKeys.mediaMode])
      ? meta[metaKeys.mediaMode]
      : 'single_image';
    const mediaIds = normalizeIds(meta[metaKeys.mediaIds], 10);
    let externalMetadata = {};
    if (metaKeys.externalMetadata && typeof meta[metaKeys.externalMetadata] === 'string' && meta[metaKeys.externalMetadata]) {
      try { externalMetadata = JSON.parse(meta[metaKeys.externalMetadata]) || {}; } catch (e) { externalMetadata = {}; }
    }
    const youtubeMeta = externalMetadata.youtube || {
      privacy: youtubeCfg.privacy || 'private', category_id: youtubeCfg.categoryId || '22',
      audience: youtubeCfg.audience || 'unset', notify_subscribers: youtubeCfg.notifySubscribers !== false, tags: []
    };
    const youtubeSelected = dest.includes('youtube');
    const shortsSelected = dest.includes('youtube_shorts');
    const youtubeModeRequired = !!youtubeCfg.available && (youtubeSelected || shortsSelected);
    const remoteWordPressSelected = dest.includes('wordpress_remote');
    const customWebhookSelected = dest.includes('custom_webhook');
    const remoteWordPressMeta = { account_id: '', post_type: 'posts', status: 'draft', slug: '', excerpt: '', category_ids: [], tag_ids: [], first_image_featured: true, append_gallery: true, append_video: true, ...(externalMetadata.wordpress_remote || {}) };
    const customWebhookMeta = { endpoint_id: '', ...(externalMetadata.custom_webhook || {}) };

    const multipleAvailable = !!proStatus.license_active && features.multiple_images && features.multiple_images.state === 'available';
    const videoAvailable = !!proStatus.license_active && features.video && features.video.state === 'available';
    const mediaMode = (storedMediaMode === 'multiple_images' && !multipleAvailable) || (storedMediaMode === 'video' && !videoAvailable)
      ? 'single_image'
      : storedMediaMode;

    const mediaRecords = useSelect((select) => {
      if (!mediaIds.length || !select('core')) return [];
      return mediaIds.map((id) => select('core').getMedia(id)).filter(Boolean);
    }, [mediaIds.join(',')]);

    let lastLog = null;
    if (typeof lastLogRaw === 'string' && lastLogRaw) {
      try { lastLog = JSON.parse(lastLogRaw); } catch (e) { lastLog = null; }
    }
    const lastSummary = lastLog && typeof lastLog.summary === 'string' ? lastLog.summary : '';
    const lastNoticeStatus = lastLog && typeof lastLog.notice_type === 'string' ? lastLog.notice_type : (posted ? 'success' : 'warning');

    function setMeta(key, value) { editPost({ meta: { ...meta, [key]: value } }); }
    function setMedia(mode, ids) {
      if (youtubeModeRequired && mode !== 'video') return;
      editPost({ meta: { ...meta, [metaKeys.mediaMode]: mode, [metaKeys.mediaIds]: normalizeIds(ids, 10) } });
    }
    function setYouTubeMeta(patch) {
      const next = { ...externalMetadata, youtube: { ...youtubeMeta, ...patch } };
      setMeta(metaKeys.externalMetadata, JSON.stringify(next));
    }
    function setRemoteWordPressMeta(patch) {
      const next = { ...externalMetadata, wordpress_remote: { ...remoteWordPressMeta, ...patch } };
      setMeta(metaKeys.externalMetadata, JSON.stringify(next));
    }
    function setCustomWebhookMeta(patch) {
      const next = { ...externalMetadata, custom_webhook: { ...customWebhookMeta, ...patch } };
      setMeta(metaKeys.externalMetadata, JSON.stringify(next));
    }
    function toggleDest(key, checked) {
      const next = new Set(dest);
      if (checked) {
        next.add(key);
        if (key === 'youtube') next.delete('youtube_shorts');
        if (key === 'youtube_shorts') next.delete('youtube');
      } else next.delete(key);
      const nextMeta = { ...meta, [metaKeys.destinations]: Array.from(next) };
      if (checked && (key === 'youtube' || key === 'youtube_shorts')) {
        nextMeta[metaKeys.mediaMode] = 'video';
      }
      editPost({ meta: nextMeta });
    }

    function moveMedia(index, direction) {
      const target = index + direction;
      if (target < 0 || target >= mediaIds.length) return;
      const next = mediaIds.slice();
      const value = next[index];
      next[index] = next[target];
      next[target] = value;
      setMedia(mediaMode, next);
    }
    function removeMedia(id) { setMedia(mediaMode, mediaIds.filter((item) => item !== id)); }
    function mediaLabel(record, id) {
      if (!record) return __('Attachment', 'calliope-social-publisher') + ' #' + id;
      if (record.title && record.title.rendered) return record.title.rendered;
      return record.slug || record.filename || ('Attachment #' + id);
    }

    function mediaSize(record) {
      if (!record) return '';
      const raw = Number(
        record.filesizeInBytes ||
        record.filesize ||
        (record.media_details && record.media_details.filesize) ||
        0
      );
      if (!Number.isFinite(raw) || raw <= 0) return '';
      return raw >= 1024 * 1024
        ? (Math.round((raw / 1024 / 1024) * 10) / 10) + ' MB'
        : Math.round(raw / 1024) + ' KB';
    }

    if (!isSupported) return null;

    const mediaModeControl = el(BaseControl, { label: t('media', __('Media', 'calliope-social-publisher')), className: 'calliope-sp-gb-media' },
      el('fieldset', { className: 'calliope-sp-gb-media-modes' },
        el('legend', { className: 'screen-reader-text' }, t('media', __('Media', 'calliope-social-publisher'))),
        el('label', { className: 'calliope-sp-gb-media-mode' },
          el('input', {
            type: 'radio', name: 'calliope-sp-gb-media-mode', value: 'single_image', checked: mediaMode === 'single_image', disabled: youtubeModeRequired,
            onChange: () => setMedia('single_image', [])
          }),
          el('span', null, t('oneImage', __('One image', 'calliope-social-publisher'))),
          el('small', null, __('Uses the post featured image and remains available in Free.', 'calliope-social-publisher'))
        ),
        multipleAvailable ? el('label', { className: 'calliope-sp-gb-media-mode' },
          el('input', {
            type: 'radio', name: 'calliope-sp-gb-media-mode', value: 'multiple_images', checked: mediaMode === 'multiple_images', disabled: youtubeModeRequired,
            onChange: () => setMedia('multiple_images', mediaMode === 'multiple_images' ? mediaIds : [])
          }),
          el('span', null, t('multipleImages', __('Multiple images / Carousel', 'calliope-social-publisher')) + ' PRO'),
          el('small', null, (() => { const names = supportedMediaNetworks('multiple_images'); return names.length ? __('Select and order 2 to 10 images. Compatible: ', 'calliope-social-publisher') + names.join(', ') : __('Select and order 2 to 10 images.', 'calliope-social-publisher'); })())
        ) : el('div', { className: 'calliope-sp-gb-media-mode is-locked', 'aria-disabled': true },
          el('span', null, t('multipleImages', __('Multiple images / Carousel', 'calliope-social-publisher')) + ' PRO'),
          el('small', null, t('proRequired', __('Requires Pro and an active license', 'calliope-social-publisher')))
        ),
        videoAvailable ? el('label', { className: 'calliope-sp-gb-media-mode' },
          el('input', {
            type: 'radio', name: 'calliope-sp-gb-media-mode', value: 'video', checked: mediaMode === 'video',
            onChange: () => setMedia('video', mediaMode === 'video' ? mediaIds.slice(0, 1) : [])
          }),
          el('span', null, t('video', __('Video', 'calliope-social-publisher')) + ' PRO'),
          el('small', null, (() => { const names = supportedMediaNetworks('video'); return names.length ? __('Select one local video attachment. Compatible: ', 'calliope-social-publisher') + names.join(', ') : __('Select one local video attachment.', 'calliope-social-publisher'); })())
        ) : el('div', { className: 'calliope-sp-gb-media-mode is-locked', 'aria-disabled': true },
          el('span', null, t('video', __('Video', 'calliope-social-publisher')) + ' PRO'),
          el('small', null, t('proRequired', __('Requires Pro and an active license', 'calliope-social-publisher')))
        )
      ),
      mediaMode === 'multiple_images' && multipleAvailable && MediaUpload ? el('div', { className: 'calliope-sp-gb-media-picker' },
        el(MediaUploadCheck || Fragment, null,
          el(MediaUpload, {
            allowedTypes: ['image'], multiple: true, gallery: true, value: mediaIds,
            onSelect: (selection) => {
              const selected = Array.isArray(selection) ? selection : [selection];
              setMedia('multiple_images', selected.map((item) => item && item.id).filter(Boolean).slice(0, 10));
            },
            render: ({ open }) => el(Button, { variant: 'secondary', onClick: open }, t('selectImages', __('Select images', 'calliope-social-publisher')))
          })
        ),
        el('p', { className: 'description' }, mediaIds.length + ' / 10'),
        el('ul', { className: 'calliope-sp-gb-media-list' }, mediaIds.map((id, index) => {
          const record = mediaRecords.find((item) => Number(item.id) === id);
          const source = record && record.source_url ? record.source_url : '';
          return el('li', { key: id },
            source ? el('img', { src: source, alt: record.alt_text || '' }) : null,
            el('span', null, mediaLabel(record, id)),
            el('div', null,
              el(Button, { size: 'small', disabled: index === 0, onClick: () => moveMedia(index, -1), label: t('moveUp', __('Move up', 'calliope-social-publisher')) }, '↑'),
              el(Button, { size: 'small', disabled: index === mediaIds.length - 1, onClick: () => moveMedia(index, 1), label: t('moveDown', __('Move down', 'calliope-social-publisher')) }, '↓'),
              el(Button, { size: 'small', isDestructive: true, onClick: () => removeMedia(id) }, t('remove', __('Remove', 'calliope-social-publisher')))
            )
          );
        }))
      ) : null,
      mediaMode === 'video' && videoAvailable && MediaUpload ? el('div', { className: 'calliope-sp-gb-media-picker' },
        el(MediaUploadCheck || Fragment, null,
          el(MediaUpload, {
            allowedTypes: ['video'], multiple: false, value: mediaIds[0] || 0,
            onSelect: (selection) => setMedia('video', selection && selection.id ? [selection.id] : []),
            render: ({ open }) => el(Button, { variant: 'secondary', onClick: open }, mediaIds.length ? t('replaceVideo', __('Replace video', 'calliope-social-publisher')) : t('selectVideo', __('Select video', 'calliope-social-publisher')))
          })
        ),
        mediaIds[0] ? el('div', { className: 'calliope-sp-gb-video-summary' },
          el('strong', null, mediaLabel(mediaRecords[0], mediaIds[0])),
          mediaRecords[0] && mediaRecords[0].mime_type ? el('span', null, mediaRecords[0].mime_type) : null,
          mediaSize(mediaRecords[0]) ? el('span', null, mediaSize(mediaRecords[0])) : null,
          el(Button, { isDestructive: true, size: 'small', onClick: () => setMedia('video', []) }, t('remove', __('Remove', 'calliope-social-publisher')))
        ) : null
      ) : null
    );

    const youtubeControls = (youtubeSelected || shortsSelected) && youtubeCfg.available ? el('div', { className: 'calliope-sp-gb-youtube' },
      el(Notice, { status: 'info', isDismissible: false }, __('YouTube uploads are processed through the existing publication queue.', 'calliope-social-publisher')),
      el('p', null, el('strong', null, __('Connected channel:', 'calliope-social-publisher') + ' '), youtubeCfg.channel || __('Not connected', 'calliope-social-publisher')),
      el('label', null, __('Privacy', 'calliope-social-publisher'), el('select', { value: youtubeMeta.privacy || 'private', onChange: (e) => setYouTubeMeta({ privacy: e.target.value }) },
        ['private','unlisted','public'].map((value) => el('option', { key: value, value }, value))
      )),
      el('label', null, __('Audience', 'calliope-social-publisher'), el('select', { value: youtubeMeta.audience || 'unset', onChange: (e) => setYouTubeMeta({ audience: e.target.value }) },
        el('option', { value: 'unset' }, __('Select audience', 'calliope-social-publisher')),
        el('option', { value: 'made_for_kids' }, __('Made for kids', 'calliope-social-publisher')),
        el('option', { value: 'not_made_for_kids' }, __('Not made for kids', 'calliope-social-publisher'))
      )),
      el('label', null, __('Category', 'calliope-social-publisher'), el('select', { value: youtubeMeta.category_id || '22', onChange: (e) => setYouTubeMeta({ category_id: e.target.value }) },
        (Array.isArray(youtubeCfg.categories) && youtubeCfg.categories.length ? youtubeCfg.categories : [{ id: youtubeMeta.category_id || '22', title: youtubeMeta.category_id || '22', assignable: true }])
          .filter((item) => item && item.assignable !== false)
          .map((item) => el('option', { key: item.id, value: item.id }, item.title || item.id))
      )),
      el(TextControl, { label: __('Additional tags', 'calliope-social-publisher'), value: Array.isArray(youtubeMeta.tags) ? youtubeMeta.tags.join(', ') : '', onChange: (value) => setYouTubeMeta({ tags: String(value || '').split(/[,;\n]+/).map((tag) => tag.trim()).filter(Boolean) }) }),
      el(CheckboxControl, { label: __('Notify subscribers', 'calliope-social-publisher'), checked: youtubeMeta.notify_subscribers !== false, onChange: (value) => setYouTubeMeta({ notify_subscribers: !!value }) }),
      shortsSelected && mediaRecords[0] ? el('p', { className: 'description' }, (() => {
        const details = mediaRecords[0].media_details || {};
        const duration = Number(details.length || details.duration || 0);
        const width = Number(details.width || 0); const height = Number(details.height || 0);
        if (!duration || !width || !height) return __('Short metadata pending server validation.', 'calliope-social-publisher');
        return duration <= Math.max(1, Number(youtubeCfg.shortMaxDuration || 180)) && width <= height ? __('This video appears eligible as a Short.', 'calliope-social-publisher') : __('This video does not appear eligible as a Short.', 'calliope-social-publisher');
      })()) : null
    ) : null;

    const remoteWordPressControls = remoteWordPressSelected && remoteWordPressCfg.available ? el('div', { className: 'calliope-sp-gb-owned-channel' },
      el(Notice, { status: 'info', isDismissible: false }, __('Remote WordPress publishing is processed through the existing queue.', 'calliope-social-publisher')),
      el('label', null, __('Remote account', 'calliope-social-publisher'), el('select', { value: remoteWordPressMeta.account_id || '', onChange: (e) => setRemoteWordPressMeta({ account_id: e.target.value }) },
        el('option', { value: '' }, __('Select an account', 'calliope-social-publisher')),
        (Array.isArray(remoteWordPressCfg.accounts) ? remoteWordPressCfg.accounts : []).map((account) => el('option', { key: account.account_id, value: account.account_id }, (account.label || account.account_id) + ' — ' + (account.base_host || '')))
      )),
      el('label', null, __('Content type', 'calliope-social-publisher'), el('select', { value: remoteWordPressMeta.post_type || 'posts', onChange: (e) => setRemoteWordPressMeta({ post_type: e.target.value }) }, el('option', { value: 'posts' }, __('Post', 'calliope-social-publisher')), el('option', { value: 'pages' }, __('Page', 'calliope-social-publisher')))),
      el('label', null, __('Status', 'calliope-social-publisher'), el('select', { value: remoteWordPressMeta.status || 'draft', onChange: (e) => setRemoteWordPressMeta({ status: e.target.value }) }, ['draft','pending','private','publish'].map((value) => el('option', { key: value, value }, value)))),
      el(TextControl, { label: __('Slug', 'calliope-social-publisher'), value: remoteWordPressMeta.slug || '', onChange: (value) => setRemoteWordPressMeta({ slug: value }) }),
      el(TextareaControl, { label: __('Excerpt', 'calliope-social-publisher'), value: remoteWordPressMeta.excerpt || '', onChange: (value) => setRemoteWordPressMeta({ excerpt: value }) }),
      remoteWordPressMeta.post_type === 'posts' ? el(Fragment, null,
        el(TextControl, { label: __('Remote category IDs', 'calliope-social-publisher'), value: Array.isArray(remoteWordPressMeta.category_ids) ? remoteWordPressMeta.category_ids.join(', ') : '', onChange: (value) => setRemoteWordPressMeta({ category_ids: String(value || '').split(',').map((id) => Number.parseInt(id,10)).filter((id) => id > 0).slice(0,50) }) }),
        el(TextControl, { label: __('Remote tag IDs', 'calliope-social-publisher'), value: Array.isArray(remoteWordPressMeta.tag_ids) ? remoteWordPressMeta.tag_ids.join(', ') : '', onChange: (value) => setRemoteWordPressMeta({ tag_ids: String(value || '').split(',').map((id) => Number.parseInt(id,10)).filter((id) => id > 0).slice(0,50) }) })
      ) : null,
      el(CheckboxControl, { label: __('Use first image as featured image', 'calliope-social-publisher'), checked: remoteWordPressMeta.first_image_featured !== false, onChange: (value) => setRemoteWordPressMeta({ first_image_featured: !!value }) }),
      el(CheckboxControl, { label: __('Append ordered gallery', 'calliope-social-publisher'), checked: remoteWordPressMeta.append_gallery !== false, disabled: mediaMode !== 'multiple_images', onChange: (value) => setRemoteWordPressMeta({ append_gallery: !!value }) }),
      el(CheckboxControl, { label: __('Append video to content', 'calliope-social-publisher'), checked: remoteWordPressMeta.append_video !== false, disabled: mediaMode !== 'video', onChange: (value) => setRemoteWordPressMeta({ append_video: !!value }) })
    ) : null;

    const customWebhookControls = customWebhookSelected && customWebhookCfg.available ? el('div', { className: 'calliope-sp-gb-owned-channel' },
      el(Notice, { status: 'info', isDismissible: false }, __('The selected endpoint receives a versioned JSON payload. Binary files and saved secrets are never stored in post metadata.', 'calliope-social-publisher')),
      el('label', null, __('Webhook endpoint', 'calliope-social-publisher'), el('select', { value: customWebhookMeta.endpoint_id || '', onChange: (e) => setCustomWebhookMeta({ endpoint_id: e.target.value }) },
        el('option', { value: '' }, __('Select an endpoint', 'calliope-social-publisher')),
        (Array.isArray(customWebhookCfg.endpoints) ? customWebhookCfg.endpoints.filter((endpoint) => endpoint.enabled) : []).map((endpoint) => el('option', { key: endpoint.endpoint_id, value: endpoint.endpoint_id }, (endpoint.label || endpoint.endpoint_id) + ' — ' + (endpoint.host || '')))
      )),
      el('p', { className: 'description' }, __('Schema:', 'calliope-social-publisher') + ' ' + (customWebhookCfg.schema || 'calliope.social-publisher.webhook.v1'))
    ) : null;

    const timingControls = schedulerCfg.available && metaKeys.timingMode && metaKeys.scheduledLocal
      ? el(BaseControl, { label: __('Social publishing timing', 'calliope-social-publisher'), className: 'calliope-sp-gb-timing' },
          el('fieldset', null,
            el('legend', { className: 'screen-reader-text' }, __('Social publishing timing', 'calliope-social-publisher')),
            el('label', { className: 'calliope-sp-gb-timing-option' },
              el('input', {
                type: 'radio',
                name: 'calliope-sp-gb-timing',
                value: 'on_publish',
                checked: timingMode === 'on_publish',
                onChange: () => setMeta(metaKeys.timingMode, 'on_publish')
              }),
              el('span', null, __('Publish when the WordPress post is published', 'calliope-social-publisher'))
            ),
            el('label', { className: 'calliope-sp-gb-timing-option' },
              el('input', {
                type: 'radio',
                name: 'calliope-sp-gb-timing',
                value: 'scheduled',
                checked: timingMode === 'scheduled',
                onChange: () => setMeta(metaKeys.timingMode, 'scheduled')
              }),
              el('span', null, __('Schedule social publication', 'calliope-social-publisher') + ' PRO')
            )
          ),
          timingMode === 'scheduled' ? el(Fragment, null,
            el(TextControl, {
              label: __('Date and time', 'calliope-social-publisher'),
              type: 'datetime-local',
              value: scheduledLocal,
              onChange: (value) => setMeta(metaKeys.scheduledLocal, value)
            }),
            el('p', { className: 'description' }, __('WordPress timezone:', 'calliope-social-publisher') + ' ' + String(schedulerCfg.timezone || ''))
          ) : null
        )
      : null;

    const content = el(PanelBody, { initialOpen: true, className: 'calliope-sp-editor-panel' },
      posted ? el(Notice, { status: 'success', isDismissible: false }, lastSummary || t('completedSuccess', __('Social Publisher has already published successfully to all selected destinations.', 'calliope-social-publisher'))) : null,
      (!posted && lastLog && Number(lastLog.failed_count || 0) > 0) ? el(Notice, { status: lastNoticeStatus === 'error' ? 'error' : 'warning', isDismissible: false }, lastSummary || t('partialFailure', __('Some destinations failed in the last automatic publishing attempt.', 'calliope-social-publisher'))) : null,
      el(ToggleControl, { label: t('enabled', __('Publish to social networks when this post is published', 'calliope-social-publisher')), checked: enabled, onChange: (v) => setMeta(metaKeys.enabled, !!v) }),
      timingControls,
      el(TextControl, { label: t('title', __('Title (optional)', 'calliope-social-publisher')), value: title, onChange: (v) => setMeta(metaKeys.title, v) }),
      el(TextareaControl, { label: t('content', __('Content (optional)', 'calliope-social-publisher')), help: t('contentHelp', __('If left empty, the excerpt will be used.', 'calliope-social-publisher')), value: text, onChange: (v) => setMeta(metaKeys.text, v) }),
      el(TextControl, {
        label: t('hashtags', __('Hashtags', 'calliope-social-publisher')),
        help: normalizedHashtags ? t('normalizedTags', __('Normalized preview:', 'calliope-social-publisher')) + ' ' + normalizedHashtags : t('hashtagsHelp', __('You can separate hashtags with spaces, commas, semicolons, or line breaks.', 'calliope-social-publisher')),
        placeholder: '#seo #wordpress', value: hashtags,
        onChange: (v) => setMeta(metaKeys.hashtags, v),
        onBlur: () => { if (normalizedHashtags && normalizedHashtags !== hashtags) setMeta(metaKeys.hashtags, normalizedHashtags); }
      }),
      mediaModeControl,
      youtubeControls,
      remoteWordPressControls,
      customWebhookControls,
      (youtubeSelected && shortsSelected) ? el(Notice, { status: 'error', isDismissible: false }, __('Select either YouTube or YouTube Shorts, not both.', 'calliope-social-publisher')) : null,
      el('div', { style: { marginTop: '8px' } }, el('strong', null, t('destinations', __('Destinations', 'calliope-social-publisher')))),
      destinations.map((d) => el(CheckboxControl, { key: d.key, label: d.label, checked: dest.includes(d.key), onChange: (checked) => toggleDest(d.key, checked) }))
    );

    if (!PluginDocumentSettingPanel && !PluginSidebar) return null;
    return el(Fragment, null,
      (PluginSidebar && PluginSidebarMoreMenuItem) ? el(Fragment, null,
        el(PluginSidebarMoreMenuItem, { target: 'calliope-social-publisher-sidebar' }, t('pluginName', __('Social Publisher', 'calliope-social-publisher'))),
        el(PluginSidebar, { name: 'calliope-social-publisher-sidebar', title: t('pluginName', __('Social Publisher', 'calliope-social-publisher')), icon: 'share' }, content)
      ) : null,
      PluginDocumentSettingPanel ? el(PluginDocumentSettingPanel, { name: 'calliope-social-publisher', title: t('pluginName', __('Social Publisher', 'calliope-social-publisher')), className: 'shp-gb-panel' }, content) : null
    );
  }

  registerPlugin('calliope-social-publisher-editor', { render: CalliopeSPPanel, icon: 'share' });
})(window.wp);
