import React from 'react';
import type { InteractiveContentBlock, InteractiveContentDocument } from './types';
import { editorBlocksFromDocument } from '../learn/editor/editor-tools';

type SavedEditorBlock={id?:string;type:string;data:Record<string,unknown>};
type SavedEditorData={time?:number;blocks:SavedEditorBlock[]};
type EditorBlockApi={
  insert:(type:string,data?:Record<string,unknown>,config?:Record<string,unknown>,index?:number,needToFocus?:boolean,replace?:boolean)=>unknown;
  render?:(data:SavedEditorData)=>Promise<void>;
  getCurrentBlockIndex?:()=>number;
  getBlocksCount?:()=>number;
  delete?:(index?:number)=>unknown;
};
type EditorInstance={
  destroy:()=>void;
  isReady:Promise<void>;
  saver:{save:()=>Promise<SavedEditorData>};
  blocks:EditorBlockApi;
};
type EditorConstructor=new(config:Record<string,unknown>)=>EditorInstance;

export type InteractiveContentAiContext={insertionIndex?:number;nearbyBlocks?:InteractiveContentBlock[]};
export type InteractiveContentEditorController={
  applyExternalDocument:(document:InteractiveContentDocument,notice?:string)=>Promise<boolean>;
  save:()=>Promise<void>;
};

function blockId(block:{id?:string;type:string},index:number){return block.id||`${block.type}-${globalThis.crypto?.randomUUID?.()||`${Date.now()}-${index}-${Math.random().toString(36).slice(2,8)}`}`;}
function cloneDocumentData(value:SavedEditorData):SavedEditorData{return structuredClone(value);}
function documentBlocksFromSaved(saved:SavedEditorData):InteractiveContentBlock[]{return saved.blocks.filter((block)=>block.type!=='askAi').map((block,index)=>({id:blockId(block,index),type:block.type,data:block.data||{}}));}
type AskAiToolApi={blocks:{getCurrentBlockIndex?:()=>number;delete?:(index?:number)=>unknown}};
function createAskAiCommandTool(onActivate:(index?:number)=>void){
  return class AskAiCommandTool{
    static get toolbox(){return{title:'Ask AI',icon:'<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M12 3l1.4 4.1L17.5 8.5l-4.1 1.4L12 14l-1.4-4.1-4.1-1.4 4.1-1.4L12 3z M18 14l.8 2.2L21 17l-2.2.8L18 20l-.8-2.2L15 17l2.2-.8L18 14z"/></svg>'};}
    static get isReadOnlySupported(){return true;}
    api:AskAiToolApi;activated=false;
    constructor({api}:{api:AskAiToolApi}){this.api=api;}
    render(){const root=document.createElement('div');root.className='interactive-content-ai-command';root.textContent='Opening AI Writer…';queueMicrotask(()=>{if(this.activated)return;this.activated=true;const index=this.api.blocks.getCurrentBlockIndex?.();if(Number.isInteger(index))this.api.blocks.delete?.(Number(index));onActivate(Number.isInteger(index)?Number(index):undefined);});return root;}
    save(){return{};}
  };
}
function editableTarget(target:EventTarget|null):boolean{
  if(!(target instanceof HTMLElement))return false;
  if(target.closest('input,textarea,select,[contenteditable="true"],[role="textbox"],.monaco-editor,.xterm'))return true;
  return false;
}
function preserveNativeSelectAll(target:EventTarget|null):boolean{
  if(!(target instanceof HTMLElement))return false;
  if(target.closest('input,textarea,select,[role="textbox"],.monaco-editor,.xterm'))return true;
  const editable=target.closest<HTMLElement>('[contenteditable="true"]');
  if(!editable)return false;
  return !editable.matches('.ce-paragraph,.interactive-content-direct-heading');
}

function stripEditorHtml(value:unknown):string{
  const raw=String(value??'');
  if(typeof document==='undefined')return raw.replace(/<[^>]+>/gu,' ').replace(/\s+/gu,' ').trim();
  const host=document.createElement('div');host.innerHTML=raw;return (host.textContent||'').replace(/\s+/gu,' ').trim();
}
function nestedItemsPlainText(value:unknown,depth=0):string[]{
  if(!Array.isArray(value))return[];const lines:string[]=[];
  for(const item of value){
    if(!item||typeof item!=='object')continue;const row=item as Record<string,unknown>;
    lines.push(`${'  '.repeat(depth)}- ${stripEditorHtml(row.content??row.text)}`);
    lines.push(...nestedItemsPlainText(row.items,depth+1));
  }
  return lines;
}
function blockPlainText(block:SavedEditorBlock):string{
  const data=block.data||{};
  if(block.type==='list')return nestedItemsPlainText(data.items).join('\n');
  if(block.type==='checklist'&&Array.isArray(data.items))return data.items.map((item)=>{const row=item&&typeof item==='object'?item as Record<string,unknown>:{};return `- [${row.checked?'x':' '}] ${stripEditorHtml(row.text)}`;}).join('\n');
  if(block.type==='code')return String(data.code??data.source??'');
  if(block.type==='table'&&Array.isArray(data.content))return data.content.map((row)=>Array.isArray(row)?row.map(stripEditorHtml).join('\t'):'').join('\n');
  if(block.type==='delimiter')return '---';
  return stripEditorHtml(data.text??data.title??data.caption??data.url??'');
}

/** Shared Editor.js shell used by Learn, Blog, Docs and Interactive Doc. */
export function InteractiveContentEditor({
  value,onChange,tools,placeholder='Start writing, or type `/` for blocks',hint='Paste or drag media directly into the editor. Code and interactive blocks use the Learnitive runtime.',instanceKey='content',onAskAi,onPreviewBlock,controllerRef,
}:{
  value:InteractiveContentDocument;
  onChange:(document:InteractiveContentDocument)=>void;
  tools:Record<string,unknown>;
  placeholder?:string;
  hint?:string;
  instanceKey?:string|number;
  onAskAi?:(blocks:InteractiveContentBlock[],mode:'selection'|'document'|'empty',context?:InteractiveContentAiContext)=>void;
  onPreviewBlock?:(block:InteractiveContentBlock)=>void;
  controllerRef?:React.MutableRefObject<InteractiveContentEditorController|null>;
}){
  const holderRef=React.useRef<HTMLDivElement|null>(null);
  const editorRef=React.useRef<EditorInstance|null>(null);
  const shellRef=React.useRef<HTMLDivElement|null>(null);
  const changeRef=React.useRef(onChange);
  const askAiRef=React.useRef(onAskAi);
  const selectionAnchorRef=React.useRef<number|null>(null);
  const undoRef=React.useRef<SavedEditorData[]>([]);
  const redoRef=React.useRef<SavedEditorData[]>([]);
  const changeTimerRef=React.useRef<ReturnType<typeof globalThis.setTimeout>>();
  const [error,setError]=React.useState('');
  const [selected,setSelected]=React.useState<Set<number>>(()=>new Set());
  const [announcement,setAnnouncement]=React.useState('');
  changeRef.current=onChange;
  askAiRef.current=onAskAi;

  const saveAndEmit=React.useCallback(async()=>{
    if(changeTimerRef.current!==undefined){globalThis.clearTimeout(changeTimerRef.current);changeTimerRef.current=undefined;}
    const editor=editorRef.current;if(!editor)return;
    await editor.isReady;
    const saved=await editor.saver.save();
    const blocks=documentBlocksFromSaved(saved);
    changeRef.current({schemaVersion:1,time:Number(saved.time||Date.now()),blocks});
    return saved;
  },[]);

  const scheduleChange=React.useCallback((api:{saver:{save:()=>Promise<SavedEditorData>}},expected:EditorInstance)=>{
    if(changeTimerRef.current!==undefined)globalThis.clearTimeout(changeTimerRef.current);
    changeTimerRef.current=globalThis.setTimeout(()=>{
      changeTimerRef.current=undefined;
      if(editorRef.current!==expected)return;
      void expected.isReady.then(()=>{
        if(editorRef.current!==expected)return undefined;
        return api.saver.save();
      }).then((saved)=>{
        if(!saved||editorRef.current!==expected)return;
        const blocks=documentBlocksFromSaved(saved);
        changeRef.current({schemaVersion:1,time:Number(saved.time||Date.now()),blocks});
      }).catch((reason)=>{if(editorRef.current===expected)setError(reason instanceof Error?reason.message:String(reason));});
    },140);
  },[]);

  const renderSnapshot=React.useCallback(async(snapshot:SavedEditorData)=>{
    const editor=editorRef.current;if(!editor?.blocks.render)return;
    await editor.isReady;if(editorRef.current!==editor)return;
    await editor.blocks.render(cloneDocumentData(snapshot));
    setSelected(new Set());
    selectionAnchorRef.current=null;
    await saveAndEmit();
  },[saveAndEmit]);

  const runStructuralMutation=React.useCallback(async(mutator:(draft:SavedEditorData)=>SavedEditorData|void,notice:string)=>{
    const editor=editorRef.current;if(!editor?.blocks.render)return;
    await editor.isReady;if(editorRef.current!==editor)return;
    const before=await editor.saver.save();
    const draft=cloneDocumentData(before);
    const changed=mutator(draft)||draft;
    if(!changed.blocks.length)changed.blocks=[{type:'paragraph',data:{text:''}}];
    undoRef.current.push(cloneDocumentData(before));
    if(undoRef.current.length>40)undoRef.current.shift();
    redoRef.current=[];
    await editor.blocks.render(changed);
    setSelected(new Set());selectionAnchorRef.current=null;
    await saveAndEmit();
    setAnnouncement(notice);
  },[saveAndEmit]);

  const undoStructural=React.useCallback(async()=>{
    const editor=editorRef.current;if(!editor?.blocks.render)return false;
    await editor.isReady;if(editorRef.current!==editor)return false;
    const snapshot=undoRef.current.pop();if(!snapshot)return false;
    const current=await editor.saver.save();redoRef.current.push(cloneDocumentData(current));
    await renderSnapshot(snapshot);setAnnouncement('Change undone.');return true;
  },[renderSnapshot]);
  const redoStructural=React.useCallback(async()=>{
    const editor=editorRef.current;if(!editor?.blocks.render)return false;
    const snapshot=redoRef.current.pop();if(!snapshot)return false;
    const current=await editor.saver.save();undoRef.current.push(cloneDocumentData(current));
    await renderSnapshot(snapshot);setAnnouncement('Change restored.');return true;
  },[renderSnapshot]);

  const syncSelectionClasses=React.useCallback((next:Set<number>)=>{
    const shell=shellRef.current;if(!shell)return;
    const blocks=[...shell.querySelectorAll<HTMLElement>('.ce-block')];
    blocks.forEach((block,index)=>{block.classList.toggle('interactive-content-block-selected',next.has(index));block.setAttribute('aria-selected',String(next.has(index)));});
  },[]);
  React.useEffect(()=>syncSelectionClasses(selected),[selected,syncSelectionClasses]);
  React.useEffect(()=>{
    if(!selected.size)return;
    const onPointerDown=(event:PointerEvent)=>{const shell=shellRef.current;if(!shell)return;const target=event.target;if(target instanceof Node&&!shell.contains(target)){setSelected(new Set());selectionAnchorRef.current=null;}};
    document.addEventListener('pointerdown',onPointerDown,true);
    return()=>document.removeEventListener('pointerdown',onPointerDown,true);
  },[selected.size]);

  const selectedBlocks=React.useCallback(async()=>{
    const editor=editorRef.current;if(!editor)return[];await editor.isReady;if(editorRef.current!==editor)return[];const saved=await editor.saver.save();
    return [...selected].sort((a,b)=>a-b).map((index)=>saved.blocks[index]).filter((block):block is SavedEditorBlock=>Boolean(block)).map((block,index)=>({id:blockId(block,index),type:block.type,data:block.data||{}}));
  },[selected]);

  const aiInsertionContext=React.useCallback(async():Promise<InteractiveContentAiContext>=>{
    const editor=editorRef.current;if(!editor)return{};await editor.isReady;if(editorRef.current!==editor)return{};const saved=await editor.saver.save();
    const current=editor.blocks.getCurrentBlockIndex?.();const insertionIndex=Number.isInteger(current)?Math.min(saved.blocks.length,Number(current)+1):saved.blocks.length;
    const start=Math.max(0,insertionIndex-3);
    const nearbyBlocks=saved.blocks.slice(start,insertionIndex).map((block,index)=>({id:blockId(block,start+index),type:block.type,data:block.data||{}}));
    return{insertionIndex,nearbyBlocks};
  },[]);

  const deleteSelection=React.useCallback(async()=>{
    if(!selected.size)return;
    const indexes=new Set(selected);const count=selected.size;
    await runStructuralMutation((draft)=>{draft.blocks=draft.blocks.filter((_,index)=>!indexes.has(index));},`${count} block${count===1?'':'s'} deleted — Undo available.`);
  },[runStructuralMutation,selected]);
  const duplicateSelection=React.useCallback(async()=>{
    if(!selected.size)return;
    const indexes=[...selected].sort((a,b)=>a-b);const count=indexes.length;
    await runStructuralMutation((draft)=>{
      const after=indexes[indexes.length-1];const copies=indexes.map((index)=>{const source=draft.blocks[index];return{...structuredClone(source),id:`${source?.type||'block'}-${globalThis.crypto?.randomUUID?.()||`${Date.now()}-${index}`}`};});
      draft.blocks.splice(after+1,0,...copies);
    },`${count} block${count===1?'':'s'} duplicated.`);
  },[runStructuralMutation,selected]);
  const moveSelection=React.useCallback(async(direction:-1|1)=>{
    if(!selected.size)return;const indexes=[...selected].sort((a,b)=>a-b);
    const min=indexes[0];const max=indexes[indexes.length-1];if((direction<0&&min===0)||(direction>0&&max===(editorRef.current?.blocks.getBlocksCount?.()||0)-1))return;
    const selectedSet=new Set(indexes);
    await runStructuralMutation((draft)=>{
      const group=draft.blocks.filter((_,index)=>selectedSet.has(index));
      const rest=draft.blocks.filter((_,index)=>!selectedSet.has(index));
      const insertion=direction<0?Math.max(0,min-1):Math.min(rest.length,max-indexes.length+2);
      rest.splice(insertion,0,...group);draft.blocks=rest;
    },`Block${indexes.length===1?'':'s'} moved.`);
  },[runStructuralMutation,selected]);
  const copySelection=React.useCallback(async()=>{
    const editor=editorRef.current;if(!editor||!selected.size)return;await editor.isReady;if(editorRef.current!==editor)return;const saved=await editor.saver.save();
    const indexes=[...selected].sort((a,b)=>a-b);
    const selectedSaved=indexes.map((index)=>saved.blocks[index]).filter((block):block is SavedEditorBlock=>Boolean(block));
    const text=selectedSaved.map(blockPlainText).filter(Boolean).join('\n\n');
    const structured=JSON.stringify({version:1,blocks:selectedSaved.map((block,index)=>({id:blockId(block,index),type:block.type,data:block.data||{}}))});
    try{
      if(typeof ClipboardItem!=='undefined'&&navigator.clipboard?.write){
        const item=new ClipboardItem({
          'application/x-learnitive-blocks+json':new Blob([structured],{type:'application/x-learnitive-blocks+json'}),
          'text/plain':new Blob([text||structured],{type:'text/plain'}),
        });
        await navigator.clipboard.write([item]);
      }else{
        if(!text)throw new Error('No plain-text clipboard fallback is available.');
        await navigator.clipboard.writeText(text);
      }
      setAnnouncement(`${selected.size} block${selected.size===1?'':'s'} copied.`);
    }catch{setAnnouncement('Copy failed. Your browser blocked clipboard access.');}
  },[selected]);

  const convertSelection=React.useCallback(async(target:'paragraph'|'header'|'quote'|'callout'|'list'|'checklist')=>{
    if(selected.size!==1)return;const index=[...selected][0];
    const allowed=new Set(Object.keys(tools));allowed.add('paragraph');if(!allowed.has(target))return;
    await runStructuralMutation((draft)=>{
      const block=draft.blocks[index];if(!block||block.type===target)return;const data=block.data||{};
      const text=String(data.text??data.title??'');
      if(target==='paragraph'){
        const listText=block.type==='list'?nestedItemsPlainText(data.items).map((line)=>line.replace(/^\s*-\s*/u,'')).join('<br>'):block.type==='checklist'&&Array.isArray(data.items)?data.items.map((item)=>stripEditorHtml(item&&typeof item==='object'?(item as Record<string,unknown>).text:'')).join('<br>'):text;
        draft.blocks[index]={...block,type:'paragraph',data:{text:listText}};return;
      }
      if(target==='header'){draft.blocks[index]={...block,type:'header',data:{text:stripEditorHtml(text),level:2}};return;}
      if(target==='quote'){draft.blocks[index]={...block,type:'quote',data:{text,caption:''}};return;}
      if(target==='callout'){draft.blocks[index]={...block,type:'callout',data:{tone:'info',title:'',text,required:false}};return;}
      if(target==='list'){
        const items=block.type==='checklist'&&Array.isArray(data.items)?data.items.map((item)=>({content:stripEditorHtml(item&&typeof item==='object'?(item as Record<string,unknown>).text:''),items:[]})):[{content:text,items:[]}];
        draft.blocks[index]={...block,type:'list',data:{style:'unordered',items}};return;
      }
      if(target==='checklist'){
        if(block.type==='list'&&Array.isArray(data.items)){
          const rows=data.items as Array<Record<string,unknown>>;if(rows.some((item)=>Array.isArray(item.items)&&item.items.length))return;
          draft.blocks[index]={...block,type:'checklist',data:{items:rows.map((item)=>({text:stripEditorHtml(item.content),checked:false}))}};return;
        }
        draft.blocks[index]={...block,type:'checklist',data:{items:[{text:stripEditorHtml(text),checked:false}]}};
      }
    },`Block converted to ${target==='header'?'heading':target}.`);
  },[runStructuralMutation,selected,tools]);


  const activateAiToolbox=React.useCallback(async(preferredIndex?:number)=>{
    const editor=editorRef.current;if(!editor||!askAiRef.current)return;
    try{await editor.isReady;const saved=await editor.saver.save();const blocks=documentBlocksFromSaved(saved);const insertionIndex=Number.isInteger(preferredIndex)?Math.max(0,Math.min(blocks.length,Number(preferredIndex))):blocks.length;const start=Math.max(0,insertionIndex-3);askAiRef.current([], 'empty', {insertionIndex,nearbyBlocks:blocks.slice(start,insertionIndex)});}catch(reason){setError(reason instanceof Error?reason.message:String(reason));}
  },[]);

  React.useEffect(()=>{
    if(!controllerRef)return;
    controllerRef.current={
      applyExternalDocument:async(document,notice='Document updated.')=>{
        const editor=editorRef.current;if(!editor?.blocks.render)return false;
        await editor.isReady;if(editorRef.current!==editor)return false;
        const before=await editor.saver.save();if(editorRef.current!==editor)return false;
        undoRef.current.push(cloneDocumentData(before));if(undoRef.current.length>40)undoRef.current.shift();redoRef.current=[];
        await editor.blocks.render({time:document.time||Date.now(),blocks:editorBlocksFromDocument(document.blocks||[])});
        if(editorRef.current!==editor)return false;
        setSelected(new Set());selectionAnchorRef.current=null;await saveAndEmit();setAnnouncement(notice);return true;
      },
      save:async()=>{await saveAndEmit();},
    };
    return()=>{if(controllerRef.current)controllerRef.current=null;};
  },[controllerRef,saveAndEmit]);

  React.useEffect(()=>{
    let disposed=false;let instance:EditorInstance|null=null;setError('');undoRef.current=[];redoRef.current=[];setSelected(new Set());const holder=holderRef.current;if(!holder){setError('Editor.js holder is unavailable.');return;}
    void import('@editorjs/editorjs').then(module=>{
      if(disposed)return;const EditorJS=(module.default||module) as unknown as EditorConstructor;
      const configuredTools=askAiRef.current?{...tools,askAi:{class:createAskAiCommandTool((index)=>{void activateAiToolbox(index);})}}:tools;
      instance=new EditorJS({
        holder,autofocus:false,minHeight:180,defaultBlock:'paragraph',
        data:{time:value.time||Date.now(),blocks:editorBlocksFromDocument(value.blocks||[])},tools:configuredTools,placeholder,
        onChange:(api:{saver:{save:()=>Promise<SavedEditorData>}})=>{if(instance&&editorRef.current===instance)scheduleChange(api,instance);},
      });
      editorRef.current=instance;void instance.isReady.catch(reason=>{if(!disposed)setError(reason instanceof Error?reason.message:String(reason));});
    }).catch(reason=>{if(!disposed)setError(`Editor.js could not load: ${reason instanceof Error?reason.message:String(reason)}`)});
    return()=>{disposed=true;if(changeTimerRef.current!==undefined){globalThis.clearTimeout(changeTimerRef.current);changeTimerRef.current=undefined;}const active=instance;if(editorRef.current===active)editorRef.current=null;if(active){void active.isReady.catch(()=>undefined).finally(()=>{try{active.destroy()}catch{/* Editor.js may already be torn down by React DOM removal. */}})}};
    // Editor.js owns its DOM. Parent autosave updates must not remount it.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  },[instanceKey]);

  React.useEffect(()=>{
    const shell=shellRef.current;if(!shell)return;
    const onClick=(event:MouseEvent)=>{
      const target=event.target as HTMLElement|null;const block=target?.closest<HTMLElement>('.ce-block');if(!block||!shell.contains(block)){if(selected.size)setSelected(new Set());return;}
      const blocks=[...shell.querySelectorAll<HTMLElement>('.ce-block')];const index=blocks.indexOf(block);if(index<0)return;
      if(event.shiftKey){event.preventDefault();const anchor=selectionAnchorRef.current??index;const [from,to]=anchor<index?[anchor,index]:[index,anchor];setSelected(new Set(Array.from({length:to-from+1},(_,offset)=>from+offset)));return;}
      if(event.metaKey||event.ctrlKey){event.preventDefault();selectionAnchorRef.current=index;setSelected((current)=>{const next=new Set(current);if(next.has(index))next.delete(index);else next.add(index);return next;});return;}
      if(selected.size){setSelected(new Set());selectionAnchorRef.current=null;}
    };
    const onKeyDown=(event:KeyboardEvent)=>{
      const mod=event.metaKey||event.ctrlKey;
      const target=event.target instanceof HTMLElement?event.target:null;
      if(!mod&&!event.altKey&&target?.classList.contains('ce-paragraph')&&(event.key===' '||event.key==='Enter')){
        const marker=(target.textContent||'').trim();
        const available=new Set(Object.keys(tools));
        const shortcut=marker==='#'?{type:'header',data:{text:'',level:2}}:marker==='##'?{type:'header',data:{text:'',level:3}}:marker==='###'?{type:'header',data:{text:'',level:4}}:marker==='-'?{type:'list',data:{style:'unordered',items:[{content:'',items:[]}]}}:marker==='1.'?{type:'list',data:{style:'ordered',items:[{content:'',items:[]}]}}:marker==='>'?{type:'quote',data:{text:'',caption:''}}:marker==='[ ]'?{type:'checklist',data:{items:[{text:'',checked:false}]}}:marker==='---'&&event.key==='Enter'?{type:'delimiter',data:{}}:marker==='```'&&event.key==='Enter'?{type:'code',data:{runnable:false}}:undefined;
        if(shortcut&&available.has(shortcut.type)){event.preventDefault();const index=editorRef.current?.blocks.getCurrentBlockIndex?.();editorRef.current?.blocks.insert(shortcut.type,shortcut.data,undefined,Number.isInteger(index)?Number(index):undefined,true,Number.isInteger(index));return;}
      }
      if(mod&&event.shiftKey&&event.key.toLocaleLowerCase()==='a'){event.preventDefault();const index=editorRef.current?.blocks.getCurrentBlockIndex?.();if(Number.isInteger(index)&&Number(index)>=0){const next=new Set([Number(index)]);selectionAnchorRef.current=Number(index);setSelected(next);setAnnouncement('Current block selected.');}return;}
      if(mod&&event.key.toLocaleLowerCase()==='a'&&!preserveNativeSelectAll(event.target)){event.preventDefault();const count=shell.querySelectorAll('.ce-block').length;const next=new Set(Array.from({length:count},(_,index)=>index));selectionAnchorRef.current=count?0:null;setSelected(next);setAnnouncement(`${count} block${count===1?'':'s'} selected.`);return;}
      if(mod&&event.key.toLocaleLowerCase()==='j'&&askAiRef.current){event.preventDefault();void(async()=>{const blocks=selected.size?await selectedBlocks():[];const total=editorRef.current?.blocks.getBlocksCount?.()||0;const mode=selected.size&&selected.size===total?'document':selected.size?'selection':'empty';askAiRef.current?.(blocks,mode,mode==='empty'?await aiInsertionContext():undefined);})();return;}
      if(mod&&event.shiftKey&&event.key.toLocaleLowerCase()==='d'&&selected.size){event.preventDefault();void duplicateSelection();return;}
      if((event.key==='Delete'||event.key==='Backspace')&&selected.size&&!editableTarget(event.target)){event.preventDefault();void deleteSelection();return;}
      if(event.key==='Escape'&&selected.size){event.preventDefault();setSelected(new Set());selectionAnchorRef.current=null;return;}
      if(event.altKey&&(event.key==='ArrowUp'||event.key==='ArrowDown')&&selected.size){event.preventDefault();void moveSelection(event.key==='ArrowUp'?-1:1);return;}
      if(mod&&event.key.toLocaleLowerCase()==='z'&&!editableTarget(event.target)){event.preventDefault();void(event.shiftKey?redoStructural():undoStructural());return;}
      if(mod&&event.key.toLocaleLowerCase()==='y'&&!editableTarget(event.target)){event.preventDefault();void redoStructural();}
    };
    shell.addEventListener('click',onClick,true);shell.addEventListener('keydown',onKeyDown,true);
    return()=>{shell.removeEventListener('click',onClick,true);shell.removeEventListener('keydown',onKeyDown,true);};
  },[aiInsertionContext,deleteSelection,duplicateSelection,moveSelection,redoStructural,selected,selectedBlocks,tools,undoStructural]);



  return <div ref={shellRef} className="interactive-learn-editorjs-shell interactive-content-editorjs-shell" tabIndex={-1}>
    {hint?<div className="interactive-learn-editorjs-hint">{hint}</div>:null}
    {error?<div className="interactive-error-copy" role="alert">{error}</div>:null}
    {selected.size?<div className="interactive-content-selection-toolbar" role="toolbar" aria-label="Selected blocks">
      <strong>{selected.size} selected</strong>
      <button type="button" onClick={()=>void moveSelection(-1)} aria-label="Move selected blocks up">↑ Move</button>
      <button type="button" onClick={()=>void moveSelection(1)} aria-label="Move selected blocks down">↓ Move</button>
      <button type="button" onClick={()=>void duplicateSelection()}>Duplicate</button>
      <button type="button" onClick={()=>void copySelection()}>Copy</button>
      {selected.size===1&&onPreviewBlock?<button type="button" onClick={()=>void selectedBlocks().then((blocks)=>{if(blocks[0])onPreviewBlock(blocks[0]);})}>Preview</button>:null}
      {selected.size===1?<select aria-label="Convert selected block" defaultValue="" onChange={(event)=>{const value=event.currentTarget.value as 'paragraph'|'header'|'quote'|'callout'|'list'|'checklist';event.currentTarget.value='';if(value)void convertSelection(value);}}><option value="" disabled>Convert…</option><option value="paragraph">Paragraph</option>{tools.header?<option value="header">Heading</option>:null}{tools.quote?<option value="quote">Quote</option>:null}{tools.callout?<option value="callout">Callout</option>:null}{tools.list?<option value="list">List</option>:null}{tools.checklist?<option value="checklist">Checklist</option>:null}</select>:null}
      {onAskAi?<button type="button" onClick={()=>void selectedBlocks().then((blocks)=>onAskAi(blocks,'selection'))}>Ask AI</button>:null}
      <button type="button" onClick={()=>void deleteSelection()}>Delete</button>
      <button type="button" onClick={()=>{setSelected(new Set());selectionAnchorRef.current=null;}}>Clear</button>
    </div>:null}
    <div ref={holderRef} className="interactive-learn-editorjs interactive-content-editorjs"/>
    <span className="sr-only" aria-live="polite">{announcement}</span>
  </div>;
}
