import { useEffect, useMemo, useState, useCallback, useRef } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import {
  PanelBody,
  ComboboxControl,
  TextareaControl,
  Spinner,
  Notice,
  Button,
  SelectControl,
} from '@wordpress/components';
import apiFetch from '@wordpress/api-fetch';

export default function Edit({ clientId, attributes, setAttributes }) {
  const { smartContentId = 0, attrs = {}, viewMode = 'default' } = attributes;

  const [options, setOptions] = useState([]); // [{label, value}]
  const [loadingList, setLoadingList] = useState(false);
  const [previewHtml, setPreviewHtml] = useState('');
  const [loadingPreview, setLoadingPreview] = useState(false);
  const [errorMsg, setErrorMsg] = useState('');
  const [attrsJsonError, setAttrsJsonError] = useState('');

  const searchDebounce = useRef(0);
  const pickerRef = useRef(null);

  const loadPosts = useCallback(async (searchTerm = '') => {
    setLoadingList(true);

    const buildQS = (ctx) => {
      const params = new URLSearchParams({
        per_page: '20',
        _fields: 'id,title',
        status: 'any',
      });
      if (searchTerm) params.set('search', searchTerm);
      if (ctx) params.set('context', ctx);
      return `/wp/v2/smart-content?${params.toString()}`;
    };

    try {
      let posts = await apiFetch({ path: buildQS('edit') });
      if (!Array.isArray(posts)) posts = [];
      setOptions(
        posts.map((p) => ({
          label: p?.title?.rendered || `#${p?.id}`,
          value: String(p?.id),
        }))
      );
    } catch (e) {
      if (e?.code === 'rest_forbidden' || e?.data?.status === 401 || e?.data?.status === 403) {
        try {
          const posts = await apiFetch({ path: buildQS('view') });
          setOptions(
            (posts || []).map((p) => ({
              label: p?.title?.rendered || `#${p?.id}`,
              value: String(p?.id),
            }))
          );
        } catch (err2) {
          console.error('Failed loading Smart Content list (view)', err2);
          setOptions([]);
        }
      } else {
        console.error('Failed loading Smart Content list (edit)', e);
        setOptions([]);
      }
    } finally {
      setLoadingList(false);
    }
  }, []);

  useEffect(() => {
    loadPosts('');
  }, [loadPosts]);

  const hostPostId = useMemo(() => {
    return window?.wp?.data?.select('core/editor')?.getCurrentPostId?.() || 0;
  }, []);

  const fetchPreview = useCallback(async () => {
    setErrorMsg('');
    setPreviewHtml('');
    if (!smartContentId) return;

    setLoadingPreview(true);
    try {
      const data = await apiFetch({
        path: '/smart-content-sync/v1/render',
        method: 'POST',
        data: {
          smartContentId,
          attrs,
          hostPostId,
          _wpnonce: window.wpApiSettings?.nonce || window.wp?.nonce || '',
        },
      });
      setPreviewHtml(data?.html || '');
    } catch (e) {
      setErrorMsg(e?.message || __('Failed to render preview.', 'smart-content-sync'));
    } finally {
      setLoadingPreview(false);
    }
  }, [smartContentId, attrs, hostPostId]);

  useEffect(() => {
    fetchPreview();
  }, [fetchPreview]);

  const attrsJson = useMemo(() => {
    try {
      return JSON.stringify(attrs || {}, null, 2);
    } catch {
      return '{}';
    }
  }, [attrs]);

  // ---- JSON textarea glue (commit on blur OR click outside) ----
  const attrsRef = useRef(attrsJson); // current raw textarea string
  const attrsWrapRef = useRef(null); // wrapper to detect outside clicks
  useEffect(() => {
    attrsRef.current = attrsJson;
  }, [attrsJson]);

  const commitAttrsJson = useCallback(() => {
    const v = attrsRef.current ?? '';
    try {
      const parsed = JSON.parse(v || '{}');
      if (parsed && typeof parsed === 'object') {
        setAttributes({ attrs: parsed });
        setAttrsJsonError('');
      } else {
        setAttrsJsonError(
          __('JSON must be an object. Example: { "days": 30 }', 'smart-content-sync')
        );
      }
    } catch {
      setAttrsJsonError(
        __(
          'Invalid JSON. Example: { "rawHtml": "<strong>Hi</strong>", "days": 30 }',
          'smart-content-sync'
        )
      );
    }
  }, [setAttributes]);

  // Commit when clicking anywhere outside the textarea (covers empty canvas area)
  useEffect(() => {
    const onGlobalPointerDown = (evt) => {
      const wrap = attrsWrapRef.current;
      if (!wrap) return;
      if (wrap.contains(evt.target)) return; // click inside → ignore
      // Only commit if we have an error or the ref differs from current attrsJson (changed)
      if (attrsRef.current !== attrsJson || attrsJsonError) {
        commitAttrsJson();
      }
    };
    // capture phase so it runs even if click target isn't focusable
    window.addEventListener('pointerdown', onGlobalPointerDown, true);
    return () => window.removeEventListener('pointerdown', onGlobalPointerDown, true);
  }, [commitAttrsJson, attrsJson, attrsJsonError]);

  const onFilterValueChange = (text) => {
    clearTimeout(searchDebounce.current);
    searchDebounce.current = setTimeout(() => loadPosts(text || ''), 180);
  };

  const openBlockInspector = useCallback(() => {
    try {
      window?.wp?.data?.dispatch('core/block-editor')?.selectBlock(clientId);
    } catch {}
    try {
      window?.wp?.data?.dispatch('core/edit-post')?.openGeneralSidebar('edit-post/block');
    } catch {}
    try {
      window?.wp?.data?.dispatch('core/edit-site')?.openSidebar('blockInspector');
    } catch {}
    try {
      window?.wp?.data?.dispatch('core/editor')?.openGeneralSidebar?.('edit-post/block');
    } catch {}

    setTimeout(() => {
      if (pickerRef.current?.focus) {
        pickerRef.current.focus();
        return;
      }
      const el = document.querySelector('[aria-label="Select Smart Content"]');
      el?.focus?.();
    }, 50);
  }, [clientId]);

  const selectedLabel =
    options.find((o) => Number(o.value) === Number(smartContentId))?.label || null;
  const actionLabel = smartContentId
    ? __('Change', 'smart-content-sync')
    : __('Pick item', 'smart-content-sync');

  const wrapperClassName =
    viewMode === 'minimal' ? 'smart-content-sync-block is-minimal' : 'smart-content-sync-block';
  const mergedProps = useBlockProps({ className: wrapperClassName });

  return (
    <>
      <InspectorControls>
        <PanelBody title={__('Smart Content', 'smart-content-sync')} initialOpen>
          <ComboboxControl
            ref={pickerRef}
            label={__('Select Smart Content', 'smart-content-sync')}
            value={smartContentId ? String(smartContentId) : undefined}
            onChange={(newId) => setAttributes({ smartContentId: Number(newId || 0) })}
            onFilterValueChange={onFilterValueChange}
            options={options}
            help={__('Start typing a title to search.', 'smart-content-sync')}
          />
          {loadingList && <Spinner />}

          <div ref={attrsWrapRef}>
            <TextareaControl
              key={attrsJson} // reseed when attrs updates externally
              label={__('Shortcode Attributes (JSON)', 'smart-content-sync')}
              defaultValue={attrsJson} // uncontrolled
              onChange={(v) => {
                attrsRef.current = v;
                setAttrsJsonError('');
              }}
              onBlur={commitAttrsJson}
              help={__(
                'Example: { "rawHtml":"<strong>Hi</strong>", "days":30 }',
                'smart-content-sync'
              )}
              rows={6}
            />
          </div>

          {attrsJsonError && (
            <Notice status="error" isDismissible={false}>
              {attrsJsonError}
            </Notice>
          )}

          <SelectControl
            label={__('Display style', 'smart-content-sync')}
            value={viewMode}
            options={[
              { label: __('Default (header visible)', 'smart-content-sync'), value: 'default' },
              { label: __('Minimal (header on hover)', 'smart-content-sync'), value: 'minimal' },
            ]}
            onChange={(val) => setAttributes({ viewMode: val })}
          />
        </PanelBody>
      </InspectorControls>

      <div {...mergedProps}>
        <div className="smart-content-sync-header">
          <div className="smart-content-sync-header__icon" aria-hidden="true">
            SC
          </div>
          <div className="smart-content-sync-header__meta">
            <div className="smart-content-sync-header__title">
              {__('Smart Content', 'smart-content-sync')}
            </div>
            {smartContentId ? (
              <div className="smart-content-sync-header__sub">
                {(selectedLabel || __('Selected item', 'smart-content-sync')) +
                  ' • ID ' +
                  String(smartContentId)}
              </div>
            ) : (
              <div className="smart-content-sync-header__sub">
                {__('Not selected', 'smart-content-sync')}
              </div>
            )}
          </div>
          <Button
            className="smart-content-sync-header__action is-compact"
            variant="primary"
            onClick={openBlockInspector}
            aria-label={actionLabel}
          >
            {actionLabel}
          </Button>
        </div>

        {!smartContentId ? (
          <div className="smart-content-sync-card smart-content-sync-empty">
            <div className="smart-content-sync-empty__hint">
              {__(
                'Choose a Smart Content item from the sidebar to see a live preview.',
                'smart-content-sync'
              )}
            </div>
          </div>
        ) : loadingPreview ? (
          <div className="smart-content-sync-preview smart-content-sync-preview--loading">
            <Spinner /> {__('Rendering preview…', 'smart-content-sync')}
          </div>
        ) : errorMsg ? (
          <Notice status="error" isDismissible={false}>
            {errorMsg}
          </Notice>
        ) : (
          <div
            className="smart-content-sync-preview"
            dangerouslySetInnerHTML={{ __html: previewHtml }}
          />
        )}
      </div>
    </>
  );
}
