'use client';

import React, { useEffect, useRef, useState } from 'react';
import {
  AlignCenterIcon,
  AlignFullWidthIcon,
  AlignLeftIcon,
  AlignRightIcon,
  Delete02Icon,
  Image01Icon,
  Link02Icon,
  PencilEdit01Icon,
  Settings02Icon,
} from '@zen/core/shared/icons';
import { Modal, Input, Textarea, Button } from '@zen/core/shared/components';
import {
  BOX_CLASS,
  ICON_BTN_ACTIVE_CLASS,
  ICON_BTN_CLASS,
  SEPARATOR_VERTICAL_CLASS,
} from '../inline/menuStyles.js';
import { newBlockId } from '../utils/ids.js';
// Imports relatifs pour éviter une dépendance circulaire avec @zen/core
// (le bloc image vit dans `shared` et tire ses composants media depuis
// `features/media`).
import MediaPicker from '../../../../features/media/components/MediaPicker.client.js';
import MediaDetailsModal from '../../../../features/media/components/MediaDetailsModal.client.js';

// Bloc image. État vide = formulaire d'insertion d'URL. État rempli = image
// rendue + toolbar flottante (alignement, lien, remplacer, supprimer).
//
// Champs persistés :
//   - `src`         : URL (interne `/zen/api/media/file/<slug>` ou externe)
//   - `mediaSlug`   : slug du média lié, présent si l'image vient de la
//                     médiathèque. Sert de référence vers `zen_media` et
//                     fait du média la source de vérité pour alt/caption.
//   - `alt`/`caption` : utilisés UNIQUEMENT pour les URL externes (pas de
//                     `mediaSlug`). Quand un mediaSlug existe, alt et
//                     caption sont lus depuis le média via l'enrichissement
//                     serveur (`enrichBlocksWithMedia`) et exposés sur le
//                     bloc dans `_resolvedAlt` / `_resolvedCaption`.
//   - `align`       : 'left' | 'center' | 'right' | 'full'
//   - `href`/`newTab` : lien optionnel
//
// Snapshot d'édition : `_mediaAlt` / `_mediaCaption` portent la copie des
// méta du média captées au moment de l'insertion, pour affichage immédiat
// dans le bandeau lecture-seule sans round-trip réseau. Champs internes
// préfixés `_` — non persistés ; ré-hydratés par l'enrichissement serveur
// au prochain rendu.

const MEDIA_FILE_URL_RE = /^\/zen\/api\/media\/file\/([^/?#]+)$/;

function extractMediaSlug(src) {
  if (typeof src !== 'string') return null;
  const m = MEDIA_FILE_URL_RE.exec(src);
  return m ? m[1] : null;
}

function resolvedAlt(block) {
  return block._resolvedAlt ?? block._mediaAlt ?? block.alt ?? '';
}

function resolvedCaption(block) {
  return block._resolvedCaption ?? block._mediaCaption ?? block.caption ?? '';
}

const ALIGN_OPTIONS = [
  { value: 'left', label: 'Aligner à gauche', Icon: AlignLeftIcon },
  { value: 'center', label: 'Centrer', Icon: AlignCenterIcon },
  { value: 'right', label: 'Aligner à droite', Icon: AlignRightIcon },
  { value: 'full', label: 'Pleine largeur', Icon: AlignFullWidthIcon },
];

const ALIGN_TO_JUSTIFY = {
  left: 'flex-start',
  center: 'center',
  right: 'flex-end',
  full: 'stretch',
};

function getAlign(block) {
  return ALIGN_TO_JUSTIFY[block.align] ? block.align : 'center';
}

function ImageUrlForm({ initialUrl, onSubmit, onCancel, disabled }) {
  const [url, setUrl] = useState(initialUrl ?? '');
  const [pickerOpen, setPickerOpen] = useState(false);
  const inputRef = useRef(null);

  useEffect(() => {
    if (!disabled) inputRef.current?.focus();
  }, [disabled]);

  function submit() {
    const trimmed = url.trim();
    if (!trimmed) return;
    onSubmit?.(trimmed);
  }

  function handleKeyDown(e) {
    if (e.key === 'Enter') {
      e.preventDefault();
      submit();
    } else if (e.key === 'Escape' && onCancel) {
      e.preventDefault();
      onCancel();
    }
  }

  return (
    <>
      <div className="flex items-center gap-2 rounded-lg border border-dashed border-neutral-300 dark:border-neutral-700 bg-neutral-50 dark:bg-neutral-800/40 px-3 py-3">
        <Image01Icon width={18} height={18} className="text-neutral-500" />
        <input
          ref={inputRef}
          type="url"
          placeholder="URL de l'image (https://…)"
          value={url}
          onChange={(e) => setUrl(e.target.value)}
          onKeyDown={handleKeyDown}
          disabled={disabled}
          className="flex-1 px-2 py-1 text-sm rounded border border-neutral-200 dark:border-neutral-700 bg-white dark:bg-neutral-900 text-neutral-900 dark:text-white outline-none focus:border-blue-500"
        />
        <button
          type="button"
          onClick={submit}
          disabled={disabled || !url.trim()}
          className="px-3 py-1 text-sm rounded bg-blue-600 hover:bg-blue-700 disabled:opacity-50 disabled:hover:bg-blue-600 text-white"
        >
          Insérer
        </button>
        <button
          type="button"
          onClick={() => setPickerOpen(true)}
          disabled={disabled}
          className="px-3 py-1 text-sm rounded border border-neutral-300 dark:border-neutral-600 text-neutral-700 dark:text-neutral-200 hover:bg-neutral-100 dark:hover:bg-neutral-700/60 disabled:opacity-50"
        >
          Choisir un média
        </button>
        {onCancel && (
          <button
            type="button"
            onClick={onCancel}
            className="px-2 py-1 text-sm rounded text-neutral-600 dark:text-neutral-400 hover:bg-neutral-200 dark:hover:bg-neutral-700/60"
          >
            Annuler
          </button>
        )}
      </div>
      <MediaPicker
        isOpen={pickerOpen}
        onClose={() => setPickerOpen(false)}
        onSelect={(media) => {
          if (media?.slug) {
            onSubmit?.({
              src: `/zen/api/media/file/${media.slug}`,
              mediaSlug: media.slug,
              mediaAlt: media.alt_text ?? '',
              mediaCaption: media.caption ?? '',
            });
          }
        }}
        accept="image/*"
        visibility="any"
        uploadVisibility="public"
        title="Insérer une image depuis la bibliothèque"
      />
    </>
  );
}

function LinkSubmenu({ initialHref, initialNewTab, onApply, onClose }) {
  const [href, setHref] = useState(initialHref ?? '');
  const [newTab, setNewTab] = useState(!!initialNewTab);
  const ref = useRef(null);
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  useEffect(() => {
    function onDoc(e) {
      if (ref.current && !ref.current.contains(e.target)) onClose?.();
    }
    function onKey(e) {
      if (e.key === 'Escape') { e.preventDefault(); onClose?.(); }
    }
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onKey);
    return () => {
      document.removeEventListener('mousedown', onDoc);
      document.removeEventListener('keydown', onKey);
    };
  }, [onClose]);

  function submit() {
    onApply?.(href.trim(), newTab);
    onClose?.();
  }

  function remove() {
    onApply?.('', false);
    onClose?.();
  }

  return (
    <div
      ref={ref}
      className={`absolute top-full mt-1 right-0 z-50 flex flex-col gap-1.5 p-2 ${BOX_CLASS}`}
    >
      <div className="flex items-center gap-1">
        <input
          ref={inputRef}
          type="url"
          placeholder="https://..."
          value={href}
          onChange={(e) => setHref(e.target.value)}
          onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); submit(); } }}
          className="w-56 px-2 py-1 text-sm rounded border border-neutral-200 dark:border-neutral-700 bg-white dark:bg-neutral-900 text-neutral-900 dark:text-white outline-none focus:border-blue-500"
        />
        <button
          type="button"
          onClick={submit}
          className="px-2 py-1 text-xs rounded bg-blue-600 hover:bg-blue-700 text-white"
        >
          OK
        </button>
        {initialHref && (
          <button
            type="button"
            onClick={remove}
            title="Retirer le lien"
            className="px-2 py-1 text-xs rounded text-red-600 hover:bg-red-50 dark:hover:bg-red-900/30"
          >
            ✕
          </button>
        )}
      </div>
      <label className="flex items-center gap-2 text-xs text-neutral-700 dark:text-neutral-300 select-none cursor-pointer">
        <input
          type="checkbox"
          checked={newTab}
          onChange={(e) => setNewTab(e.target.checked)}
          className="cursor-pointer"
        />
        Ouvrir dans un nouvel onglet
      </label>
    </div>
  );
}

function ImageToolbar({ block, onPatch, onReplace, onEditMetadata, onRemove }) {
  const [linkOpen, setLinkOpen] = useState(false);
  const align = getAlign(block);

  return (
    <div
      className={`absolute top-2 right-2 z-10 flex items-center gap-0.5 px-1 py-1 ${BOX_CLASS} opacity-0 group-hover/image:opacity-100 focus-within:opacity-100 transition-opacity`}
    >
      {ALIGN_OPTIONS.map(({ value, label, Icon }) => (
        <button
          key={value}
          type="button"
          title={label}
          onClick={() => onPatch({ align: value })}
          className={`${ICON_BTN_CLASS} ${align === value ? ICON_BTN_ACTIVE_CLASS : ''}`}
        >
          <Icon width={16} height={16} />
        </button>
      ))}
      <div className={SEPARATOR_VERTICAL_CLASS} />
      <div className="relative">
        <button
          type="button"
          title={block.href ? `Modifier le lien (${block.href})` : 'Ajouter un lien'}
          onClick={() => setLinkOpen(o => !o)}
          className={`${ICON_BTN_CLASS} ${block.href ? ICON_BTN_ACTIVE_CLASS : ''}`}
        >
          <Link02Icon width={16} height={16} />
        </button>
        {linkOpen && (
          <LinkSubmenu
            initialHref={block.href ?? ''}
            initialNewTab={block.newTab ?? true}
            onApply={(href, newTab) => onPatch({ href, newTab: href ? !!newTab : false })}
            onClose={() => setLinkOpen(false)}
          />
        )}
      </div>
      <button
        type="button"
        title="Modifier les métadonnées (alt, légende)"
        onClick={onEditMetadata}
        className={ICON_BTN_CLASS}
      >
        <Settings02Icon width={16} height={16} />
      </button>
      <button
        type="button"
        title="Remplacer l'image"
        onClick={onReplace}
        className={ICON_BTN_CLASS}
      >
        <PencilEdit01Icon width={16} height={16} />
      </button>
      <button
        type="button"
        title="Supprimer l'image"
        onClick={onRemove}
        className={ICON_BTN_CLASS}
      >
        <Delete02Icon width={16} height={16} />
      </button>
    </div>
  );
}

// Modale d'édition des métadonnées d'une image **sans** mediaSlug (URL
// externe). Pour les images liées à la médiathèque, on ouvre directement
// `MediaDetailsModal` qui édite le média en BD.
function ExternalImageMetadataModal({ isOpen, onClose, alt, caption, onSave }) {
  const [draftAlt, setDraftAlt] = useState(alt ?? '');
  const [draftCaption, setDraftCaption] = useState(caption ?? '');

  // Synchronise les drafts à chaque ouverture.
  useEffect(() => {
    if (!isOpen) return;
    setDraftAlt(alt ?? '');
    setDraftCaption(caption ?? '');
  }, [isOpen, alt, caption]);

  if (!isOpen) return null;

  return (
    <Modal
      isOpen={isOpen}
      onClose={onClose}
      title="Métadonnées de l'image"
      size="md"
      footer={
        <div className="flex justify-end gap-2">
          <Button variant="ghost" onClick={onClose}>Annuler</Button>
          <Button
            variant="primary"
            onClick={() => { onSave({ alt: draftAlt, caption: draftCaption }); onClose(); }}
          >
            Enregistrer
          </Button>
        </div>
      }
    >
      <div className="space-y-3">
        <Input
          label="Texte alternatif"
          value={draftAlt}
          onChange={setDraftAlt}
          description="Décrit l'image pour les lecteurs d'écran et le SEO."
        />
        <Textarea
          label="Légende"
          value={draftCaption}
          onChange={setDraftCaption}
          rows={2}
        />
      </div>
    </Modal>
  );
}

function ImageBlock({ block, onChange, disabled }) {
  // `replacing` permet de revenir au formulaire URL sans perdre alt/caption/
  // align/href. Tant qu'il est `true`, on rend `ImageUrlForm` même si
  // `block.src` est non vide.
  const [replacing, setReplacing] = useState(false);
  const [metadataOpen, setMetadataOpen] = useState(false);
  const showForm = !block.src || replacing;
  const linkedSlug = block.mediaSlug || extractMediaSlug(block.src);

  // Le payload reçu peut être :
  //   - une string (URL libre saisie / Entrée dans le champ),
  //   - un objet { src, mediaSlug, mediaAlt, mediaCaption } (issu du picker).
  // Quand on lie un média, alt/caption locaux sont nettoyés (mode strict :
  // le média est la source de vérité). Pour les URL externes, on garde le
  // alt/caption existant et on efface tout snapshot média précédent.
  function handleSetSrc(payload) {
    const isObj = payload && typeof payload === 'object';
    const src = isObj ? payload.src : payload;
    const explicitSlug = isObj ? payload.mediaSlug : null;
    const slug = explicitSlug ?? extractMediaSlug(src);
    const patch = { src: src ?? '', mediaSlug: slug || '' };
    if (slug) {
      patch.alt = '';
      patch.caption = '';
      patch._mediaAlt = isObj ? (payload.mediaAlt ?? '') : '';
      patch._mediaCaption = isObj ? (payload.mediaCaption ?? '') : '';
    } else {
      patch._mediaAlt = '';
      patch._mediaCaption = '';
    }
    onChange?.(patch);
    setReplacing(false);
  }

  // Mise à jour du snapshot après édition du média via MediaDetailsModal :
  // on rafraîchit `_mediaAlt`/`_mediaCaption` pour que la légende affichée
  // sous l'image reflète immédiatement les changements (sans attendre un
  // re-render serveur enrichi).
  function handleMediaUpdated(updatedMedia) {
    if (!updatedMedia) return;
    onChange?.({
      _mediaAlt: updatedMedia.alt_text ?? '',
      _mediaCaption: updatedMedia.caption ?? '',
    });
  }

  function handleRemove() {
    // Vide tous les champs : la prochaine frappe ouvre à nouveau le formulaire.
    onChange?.({
      src: '',
      mediaSlug: '',
      alt: '',
      caption: '',
      href: '',
      newTab: false,
      align: 'center',
      _mediaAlt: '',
      _mediaCaption: '',
    });
    setReplacing(false);
  }

  if (showForm) {
    return (
      <ImageUrlForm
        initialUrl={replacing ? (block.src ?? '') : ''}
        onSubmit={handleSetSrc}
        onCancel={replacing ? () => setReplacing(false) : null}
        disabled={disabled}
      />
    );
  }

  const align = getAlign(block);
  const justify = ALIGN_TO_JUSTIFY[align];
  const imgClass = align === 'full'
    ? 'rounded-lg w-full block'
    : 'rounded-lg max-w-full block';

  const altForRender = resolvedAlt(block);
  const captionForRender = resolvedCaption(block);

  // Mode lecture seule : si href défini, on wrappe l'image dans un <a>.
  // En édition on n'ajoute jamais le <a> — il piégerait les clics dans
  // l'éditeur. Le href est appliqué via la sérialisation HTML / l'export.
  const imgEl = (
    /* eslint-disable-next-line @next/next/no-img-element */
    <img
      src={block.src}
      alt={altForRender}
      className={imgClass}
      draggable={false}
    />
  );
  const wrappedImg = disabled && block.href
    ? (
      <a
        href={block.href}
        target={block.newTab ? '_blank' : undefined}
        rel={block.newTab ? 'noopener noreferrer' : undefined}
        className="block"
      >
        {imgEl}
      </a>
    )
    : imgEl;

  return (
    <div className="flex flex-col gap-1">
      <div
        className="group/image relative flex"
        style={{ justifyContent: justify }}
      >
        <div className={align === 'full' ? 'relative w-full' : 'relative'}>
          {wrappedImg}
          {!disabled && (
            <ImageToolbar
              block={block}
              onPatch={(patch) => onChange?.(patch)}
              onReplace={() => setReplacing(true)}
              onEditMetadata={() => setMetadataOpen(true)}
              onRemove={handleRemove}
            />
          )}
        </div>
      </div>
      {/* Sous l'image : la légende uniquement, et seulement si elle existe.
          Pas de placeholder, pas de bloc gris, pas d'alt — l'alt reste sur
          l'attribut `<img alt>`. L'édition se fait via la modale ouverte
          par le bouton « Métadonnées » de la toolbar. */}
      {captionForRender ? (
        <div className="w-full px-1 py-0.5 text-sm italic text-neutral-600 dark:text-neutral-400">
          {captionForRender}
        </div>
      ) : null}
      {!disabled && metadataOpen && (
        linkedSlug ? (
          <MediaDetailsModal
            isOpen={metadataOpen}
            slug={linkedSlug}
            onClose={() => setMetadataOpen(false)}
            onUpdated={handleMediaUpdated}
            canEdit
          />
        ) : (
          <ExternalImageMetadataModal
            isOpen={metadataOpen}
            alt={block.alt ?? ''}
            caption={block.caption ?? ''}
            onSave={(patch) => onChange?.(patch)}
            onClose={() => setMetadataOpen(false)}
          />
        )
      )}
    </div>
  );
}


const Image = {
  type: 'image',
  label: 'Image',
  icon: <Image01Icon width={18} height={18} />,
  keywords: ['image', 'photo', 'picture', 'img'],
  isText: false,
  create(init = {}) {
    return {
      id: newBlockId(),
      type: 'image',
      src: '',
      mediaSlug: '',
      alt: '',
      caption: '',
      align: 'center',
      href: '',
      newTab: false,
      ...init,
    };
  },
  Component: ImageBlock,
};

export default Image;
