import { Check, Clipboard, Code2, Eye, FileDown, Image as ImageIcon, Lightbulb, Play, RotateCcw, SquareTerminal, Video } from 'lucide-react';
import React from 'react';
import { LinuxTerminalPro } from '../../components/linux-terminal-pro';
import { checkCodeExercise, getSolution, gradeCheckpoint, resetTerminalTask, runCodeExercise, runLearnCode, saveBlockProgress, startTerminalTask, verifyTerminalTask } from '../api';
import { storeLearnExample } from '../runtime-handoff';
import type { LearnBlock, LearnStarter, PublicLearnLesson } from '../types';
import { LearnVideoPlayer } from './learn-video-player';

function text(data:Record<string,unknown>,key:string){return typeof data[key]==='string'?data[key] as string:'';}
function number(data:Record<string,unknown>,key:string){return Number(data[key]||0);}
function array(data:Record<string,unknown>,key:string){return Array.isArray(data[key])?data[key] as unknown[]:[];}
function safeInlineHtml(input:string):string{
  if(typeof document==='undefined')return input.replace(/<[^>]*>/gu,'');
  const doc=new DOMParser().parseFromString(`<div>${input}</div>`,'text/html');const allowed=new Set(['B','STRONG','I','EM','U','S','CODE','A','BR','MARK','SUB','SUP']);
  for(const el of [...doc.body.querySelectorAll('*')]){if(el.tagName==='DIV')continue;if(!allowed.has(el.tagName)){el.replaceWith(...el.childNodes);continue;}for(const attr of [...el.attributes])if(!(el.tagName==='A'&&attr.name==='href'))el.removeAttribute(attr.name);if(el.tagName==='A'){const href=el.getAttribute('href')||'';if(!/^(https?:|mailto:|\/)/u.test(href))el.removeAttribute('href');else{el.setAttribute('rel','noopener noreferrer');el.setAttribute('target','_blank');}}}
  return doc.body.firstElementChild?.innerHTML||'';
}
function RichText({html}:{html:string}){return <span dangerouslySetInnerHTML={{__html:safeInlineHtml(html)}}/>;}
function NestedList({items,ordered=false}:{items:unknown[];ordered?:boolean}){const Tag=ordered?'ol':'ul';return <Tag>{items.map((raw,index)=>{const item=typeof raw==='string'?{content:raw,items:[]}:(raw||{}) as {content?:string;text?:string;items?:unknown[]};const children=Array.isArray(item.items)?item.items:[];return <li key={index}><RichText html={String(item.content||item.text||'')}/>{children.length?<NestedList items={children} ordered={ordered}/>:null}</li>})}</Tag>;}
function StarterButton({block,lesson}:{block:LearnBlock;lesson:PublicLearnLesson}){const starter=block.data.starter as LearnStarter|undefined;const mode=String(block.data.projectModeId||lesson.projectModeId||'');if(!starter||!mode||!Object.keys(starter.files||{}).length)return null;return <button className="interactive-button interactive-button-primary" type="button" onClick={()=>{const token=storeLearnExample(text(block.data,'title')||lesson.title,mode,starter,{courseSlug:lesson.course.slug,lessonSlug:lesson.slug,blockId:block.id});window.location.href=`/try/${encodeURIComponent(token)}`}}><Code2 className="size-4"/> Open starter</button>}

function Checkpoint({block,lesson,onLessonCompleted}:{block:LearnBlock;lesson:PublicLearnLesson;onLessonCompleted?:()=>void}){
  const options=array(block.data,'options').map(String);const multiple=String(block.data.kind)==='multiple';const [selected,setSelected]=React.useState<number[]>([]);const [result,setResult]=React.useState<{correct:boolean;explanation?:string}>();const [busy,setBusy]=React.useState(false);
  const toggle=(index:number)=>setSelected(current=>multiple?(current.includes(index)?current.filter(v=>v!==index):[...current,index]):[index]);
  const submit=async()=>{if(!selected.length||busy)return;setBusy(true);try{const response=await gradeCheckpoint(lesson.course.slug,lesson.slug,block.id,multiple?selected:selected[0]);setResult(response.result);if(response.result.lessonCompleted)onLessonCompleted?.();}catch(error){setResult({correct:false,explanation:error instanceof Error?error.message:String(error)});}finally{setBusy(false)}};
  return <section className="interactive-learn-checkpoint"><span className="interactive-learn-block-label">Checkpoint</span><h3>{text(block.data,'question')}</h3><div className="interactive-learn-checkpoint-options">{options.map((option,index)=><label key={`${index}-${option}`}><input type={multiple?'checkbox':'radio'} name={`checkpoint-${block.id}`} checked={selected.includes(index)} onChange={()=>toggle(index)}/><span>{option}</span></label>)}</div><button type="button" className="interactive-button interactive-button-primary" disabled={!selected.length||busy} onClick={()=>void submit()}>{busy?'Checking…':'Check answer'}</button>{result?<div className={result.correct?'interactive-learn-feedback is-correct':'interactive-learn-feedback is-incorrect'}><strong>{result.correct?'Correct':'Try again'}</strong>{result.explanation?<p>{result.explanation}</p>:null}</div>:null}</section>;
}
function Solution({block,lesson}:{block:LearnBlock;lesson:PublicLearnLesson}){const [value,setValue]=React.useState<{title:string;content:string}>();const [error,setError]=React.useState('');return <section className="interactive-learn-solution"><button type="button" className="interactive-button interactive-button-secondary" onClick={()=>void getSolution(lesson.course.slug,lesson.slug,block.id).then(setValue).catch(reason=>setError(reason instanceof Error?reason.message:String(reason)))}><Eye className="size-4"/> {value?'Solution':'Reveal solution'}</button>{value?<div><h3>{value.title}</h3><pre>{value.content}</pre></div>:null}{error?<p className="interactive-error-copy">{error}</p>:null}</section>}
function VideoBlock({block,lesson,onLessonCompleted}:{block:LearnBlock;lesson:PublicLearnLesson;onLessonCompleted?:()=>void}){const source=String(block.data.source||'upload') as 'upload'|'youtube'|'wistia'|'direct';const required=number(block.data,'requiredWatchPercent');const doneRef=React.useRef(lesson.blockProgress?.[block.id]?.status==='completed');const lastSaved=React.useRef(0);const state=lesson.blockProgress?.[block.id]?.state||{};const initialTime=Number(state.currentTime||0);const onProgress=React.useCallback((progress:{percent:number;currentTime:number;duration:number})=>{if(doneRef.current)return;if(required>0&&progress.percent>=required){doneRef.current=true;void saveBlockProgress(lesson.course.slug,lesson.slug,block.id,'completed',{watchedPercent:Math.max(required,Math.floor(progress.percent)),currentTime:progress.currentTime,duration:progress.duration}).then(value=>{if(value.lessonCompleted)onLessonCompleted?.()}).catch(()=>{doneRef.current=false});return}if(progress.currentTime-lastSaved.current>=10){lastSaved.current=progress.currentTime;void saveBlockProgress(lesson.course.slug,lesson.slug,block.id,'started',{watchedPercent:Math.floor(progress.percent),currentTime:progress.currentTime,duration:progress.duration}).catch(()=>{})}},[block.id,lesson.course.slug,lesson.slug,onLessonCompleted,required]);const credit=text(block.data,'credit');const creditUrl=text(block.data,'creditUrl');return <div className="interactive-learn-video"><LearnVideoPlayer provider={source} mediaId={number(block.data,'mediaId')||undefined} externalId={text(block.data,'externalId')||undefined} directUrl={text(block.data,'directUrl')||undefined} title={text(block.data,'title')||'Lesson video'} posterUrl={number(block.data,'posterMediaId')?`/api/learn/media/${number(block.data,'posterMediaId')}`:undefined} captionUrl={number(block.data,'captionMediaId')?`/api/learn/media/${number(block.data,'captionMediaId')}`:undefined} initialTime={initialTime} onProgress={onProgress}/>{credit?<p className="interactive-learn-video-credit">Video courtesy of {creditUrl?<a href={creditUrl} target="_blank" rel="noreferrer">{credit}</a>:credit}</p>:null}</div>}

function AudioBlock({block,lesson,onLessonCompleted}:{block:LearnBlock;lesson:PublicLearnLesson;onLessonCompleted?:()=>void}){const required=number(block.data,'requiredListenPercent');const audioRef=React.useRef<HTMLAudioElement>(null);const doneRef=React.useRef(false);const onTime=()=>{if(!required||doneRef.current)return;const audio=audioRef.current;if(!audio||!audio.duration)return;if(audio.currentTime/audio.duration*100>=required){doneRef.current=true;void saveBlockProgress(lesson.course.slug,lesson.slug,block.id,'completed',{listenedPercent:required}).then(progress=>{if(progress.lessonCompleted)onLessonCompleted?.();}).catch(()=>{});}};return <audio ref={audioRef} controls src={`/api/learn/media/${number(block.data,'mediaId')}`} onTimeUpdate={onTime}/>}
function RunnableCodeBlock({block,lesson}:{block:LearnBlock;lesson:PublicLearnLesson}){
  const initial=text(block.data,'code');
  const initialStdin=text(block.data,'stdin');
  const allowStdin=block.data.allowStdin===true;
  const [code,setCode]=React.useState(initial);
  const [stdin,setStdin]=React.useState(initialStdin);
  const [running,setRunning]=React.useState(false);
  const [result,setResult]=React.useState<{status:string;exitCode:number;stdout:string;stderr:string}>();
  const [error,setError]=React.useState('');
  React.useEffect(()=>{setCode(initial);setStdin(initialStdin);setResult(undefined);setError('')},[initial,initialStdin]);
  const runnable=block.data.runnable!==false;
  const run=async()=>{setRunning(true);setError('');try{setResult(await runLearnCode(lesson.course.slug,lesson.slug,block.id,code,allowStdin?stdin:''))}catch(reason){setError(reason instanceof Error?reason.message:String(reason))}finally{setRunning(false)}};
  return <section className="interactive-learn-code interactive-learn-code-runnable"><header><span>{text(block.data,'filename')||text(block.data,'language')||'Code'}</span><div><button type="button" onClick={()=>void navigator.clipboard.writeText(code)}><Clipboard className="size-4"/> Copy</button>{runnable?<button type="button" disabled={running} onClick={()=>void run()}><Play className="size-4"/>{running?'Running…':'Run'}</button>:null}</div></header>{runnable?<><textarea className="interactive-learn-code-editor" spellCheck={false} value={code} onChange={event=>setCode(event.currentTarget.value)}/>{allowStdin?<label className="interactive-learn-code-stdin"><span>Standard input</span><textarea spellCheck={false} value={stdin} placeholder="Input passed to the program when you run it" onChange={event=>setStdin(event.currentTarget.value)}/></label>:null}</>:<pre><code>{code}</code></pre>}{error?<p className="interactive-error-copy">{error}</p>:null}{result?<div className={`interactive-learn-code-result ${result.exitCode===0?'is-success':'is-error'}`}><strong>{result.exitCode===0?'Finished':'Exited with an error'} · code {result.exitCode}</strong>{result.stdout?<pre>{result.stdout}</pre>:null}{result.stderr?<pre>{result.stderr}</pre>:null}{!result.stdout&&!result.stderr?<pre>No output</pre>:null}</div>:null}</section>;
}

function CodeExercise({block,lesson,onLessonCompleted}:{block:LearnBlock;lesson:PublicLearnLesson;onLessonCompleted?:()=>void}){
  const starter=(block.data.starter||{files:{}}) as LearnStarter;
  const initialFiles=React.useMemo(()=>({...starter.files}),[starter]);
  const paths=Object.keys(initialFiles);
  const defaultPath=String(block.data.entryFile||starter.entryFile||paths[0]||'');
  const [files,setFiles]=React.useState<Record<string,string>>(initialFiles);
  const [activePath,setActivePath]=React.useState(defaultPath);
  const [stdin,setStdin]=React.useState(text(block.data,'exampleStdin'));
  const [busy,setBusy]=React.useState<'run'|'check'|null>(null);
  const [runResult,setRunResult]=React.useState<{status:string;exitCode:number;stdout:string;stderr:string}>();
  const [checkResult,setCheckResult]=React.useState<{passed:boolean;exitCode:number;stdout:string;stderr:string;visibleTests:Array<{index:number;label:string;passed:boolean;exitCode:number;stdout:string;stderr:string}>;hiddenTests:{passed:number;total:number}}>();
  const [error,setError]=React.useState('');
  const visibleTests=array(block.data,'visibleTests') as Array<Record<string,unknown>>;const hints=array(block.data,'hints').map(String);
  React.useEffect(()=>{setFiles(initialFiles);setActivePath(defaultPath);setStdin(text(block.data,'exampleStdin'));setRunResult(undefined);setCheckResult(undefined);setError('')},[initialFiles,defaultPath,block.data.exampleStdin]);
  const run=async()=>{setBusy('run');setError('');setCheckResult(undefined);try{setRunResult(await runCodeExercise(lesson.course.slug,lesson.slug,block.id,files,activePath||defaultPath,stdin))}catch(reason){setError(reason instanceof Error?reason.message:String(reason))}finally{setBusy(null)}};
  const check=async()=>{setBusy('check');setError('');try{const value=await checkCodeExercise(lesson.course.slug,lesson.slug,block.id,files,activePath||defaultPath);setCheckResult(value);if(value.lessonCompleted)onLessonCompleted?.()}catch(reason){setError(reason instanceof Error?reason.message:String(reason))}finally{setBusy(null)}};
  const reset=()=>{setFiles(initialFiles);setActivePath(defaultPath);setStdin(text(block.data,'exampleStdin'));setRunResult(undefined);setCheckResult(undefined);setError('')};
  return <section className="interactive-learn-exercise"><div><span className="interactive-learn-block-label">Interactive exercise</span><h3>{text(block.data,'title')||'Code exercise'}</h3><p>{text(block.data,'instructions')}</p></div>{paths.length?<div className="interactive-learn-exercise-editor"><header>{paths.length>1?<select value={activePath} onChange={event=>setActivePath(event.currentTarget.value)}>{paths.map(path=><option key={path} value={path}>{path}</option>)}</select>:<strong>{activePath}</strong>}<button type="button" onClick={reset}><RotateCcw className="size-4"/> Reset</button></header><textarea spellCheck={false} value={files[activePath]||''} onChange={event=>{const value=event.currentTarget.value;setFiles(current=>({...current,[activePath]:value}));}}/></div>:null}<label className="interactive-learn-exercise-stdin"><span>Input for Run</span><textarea value={stdin} onChange={event=>setStdin(event.currentTarget.value)} placeholder="stdin for a practice run"/></label>{visibleTests.length?<details className="interactive-learn-visible-tests"><summary>Visible tests ({visibleTests.length})</summary><div>{visibleTests.map((raw,index)=>{const spec=raw||{};return <article key={index}><strong>{String(spec.label||`Test ${index+1}`)}</strong>{String(spec.stdin||'')?<pre><span>stdin</span>{String(spec.stdin)}</pre>:null}{String(spec.expectedStdout||'')?<pre><span>expected stdout</span>{String(spec.expectedStdout)}</pre>:null}</article>})}</div></details>:null}{hints.length?<details className="interactive-learn-exercise-hints"><summary>Hints</summary><ul>{hints.map((hint,index)=><li key={index}>{hint}</li>)}</ul></details>:null}<div className="interactive-learn-exercise-actions"><StarterButton block={block} lesson={lesson}/><button type="button" className="interactive-button interactive-button-secondary" disabled={Boolean(busy)||!paths.length} onClick={()=>void run()}><Play className="size-4"/>{busy==='run'?'Running…':'Run'}</button><button type="button" className="interactive-button interactive-button-primary" disabled={Boolean(busy)||!paths.length} onClick={()=>void check()}><Check className="size-4"/>{busy==='check'?'Checking…':'Check'}</button></div>{error?<p className="interactive-error-copy">{error}</p>:null}{runResult?<div className={runResult.status==='success'&&runResult.exitCode===0?'interactive-learn-feedback is-correct':'interactive-learn-feedback is-incorrect'}><strong>Run result · exit {runResult.exitCode}</strong>{runResult.stdout?<pre>{runResult.stdout}</pre>:null}{runResult.stderr?<pre>{runResult.stderr}</pre>:null}</div>:null}{checkResult?<div className={checkResult.passed?'interactive-learn-feedback is-correct':'interactive-learn-feedback is-incorrect'}><strong>{checkResult.passed?'All checks passed':'Checks need attention'}</strong>{checkResult.visibleTests.length?<ul>{checkResult.visibleTests.map(test=><li key={test.index}>{test.passed?'✓':'✕'} {test.label}{!test.passed&&test.stdout?<pre>{test.stdout}</pre>:null}</li>)}</ul>:null}{checkResult.hiddenTests.total?<p>Hidden checks: {checkResult.hiddenTests.passed}/{checkResult.hiddenTests.total}</p>:null}</div>:null}</section>;
}
function TerminalBlock({block,lesson}:{block:LearnBlock;lesson:PublicLearnLesson}){const [open,setOpen]=React.useState(true);const starter=(block.data.starter||{files:{}}) as LearnStarter;const directory=text(block.data,'workingDirectory')||starter.cwd||'';return <section className="interactive-learn-terminal"><div className="interactive-learn-terminal-head"><div><span className="interactive-learn-block-label"><SquareTerminal className="size-4"/> Terminal</span><h3>{text(block.data,'title')||'Terminal exercise'}</h3><p>{text(block.data,'instructions')}</p></div><StarterButton block={block} lesson={lesson}/></div><LinuxTerminalPro open={open} onOpenChange={setOpen} context="homepage" embedded initialFiles={starter.files||{}} initialDirectory={directory||undefined} initialCommand={text(block.data,'setupCommand')||undefined}/></section>}

function terminalTaskLearnerInstructions(input:string){return String(input||'').replace(/\/workspace\//gu,'./').replace(/\/workspace(?=\s|$|[.,;:!?])/gu,'.');}
function TerminalTaskBlock({block,lesson,onLessonCompleted}:{block:LearnBlock;lesson:PublicLearnLesson;onLessonCompleted?:()=>void}){
  const [open,setOpen]=React.useState(true);const [attempt,setAttempt]=React.useState<{id:string;attemptNumber:number;projectId:string;generation:number}>();const [busy,setBusy]=React.useState<'verify'|'reset'|''>('');const [result,setResult]=React.useState<{passed:boolean;message?:string;checks?:Array<{label:string;passed:boolean}>}>();const [error,setError]=React.useState('');
  const sessionFactory=React.useCallback(async()=>{const created=await startTerminalTask(lesson.course.slug,lesson.slug,block.id);setAttempt(created.attempt);setResult(undefined);setError('');return created.terminalSession},[block.id,lesson.course.slug,lesson.slug]);
  const verify=async()=>{if(!attempt||busy)return;setBusy('verify');setError('');try{const checked=await verifyTerminalTask(lesson.course.slug,lesson.slug,block.id,attempt.id);setResult(checked);if(checked.lessonCompleted)onLessonCompleted?.()}catch(reason){setError(reason instanceof Error?reason.message:String(reason))}finally{setBusy('')}};
  const reset=async()=>{if(!attempt||busy)return;setBusy('reset');setError('');try{await resetTerminalTask(lesson.course.slug,lesson.slug,block.id,attempt.id);setResult({passed:false,message:'Environment reset to the task starter state.'})}catch(reason){setError(reason instanceof Error?reason.message:String(reason))}finally{setBusy('')}};
  const directory=text(block.data,'workingDirectory');return <section className="interactive-learn-terminal interactive-learn-terminal-task"><div className="interactive-learn-terminal-head"><div><span className="interactive-learn-block-label"><SquareTerminal className="size-4"/> Terminal Task</span><h3>{text(block.data,'title')||'Terminal task'}</h3><p>{terminalTaskLearnerInstructions(text(block.data,'instructions'))}</p><p className="interactive-learn-terminal-task-workspace-hint"><strong>Task workspace</strong> Commands run from this task’s isolated workspace. Use relative paths such as <code>backups/status.txt</code>.</p>{attempt?<small>Attempt {attempt.attemptNumber}</small>:null}</div></div><LinuxTerminalPro open={open} onOpenChange={setOpen} context="homepage" embedded sessionFactory={sessionFactory} initialDirectory={directory&&directory!=='.'?directory:undefined}/><div className="interactive-learn-terminal-task-actions"><button type="button" className="interactive-button interactive-button-primary" disabled={!attempt||Boolean(busy)} onClick={()=>void verify()}><Check className="size-4"/>{busy==='verify'?'Verifying…':'Verify Task'}</button>{String(block.data.resetPolicy)!=='disabled'?<button type="button" className="interactive-button interactive-button-secondary" disabled={!attempt||Boolean(busy)} onClick={()=>void reset()}><RotateCcw className="size-4"/>{busy==='reset'?'Resetting…':'Reset environment'}</button>:null}</div>{result?<div className={result.passed?'interactive-learn-feedback is-correct':'interactive-learn-feedback is-incorrect'}><strong>{result.passed?'Task complete':result.message||'Task not complete yet.'}</strong>{result.checks?.length?<ul>{result.checks.map((check,index)=><li key={index}>{check.passed?'✓':'✕'} {check.label}</li>)}</ul>:null}</div>:null}{error?<p className="interactive-error-copy">{error}</p>:null}</section>;
}

function NestedBlocksView({blocks}:{blocks:unknown[]}){return <div className="interactive-learn-nested-blocks">{blocks.slice(0,100).map((raw,index)=>{const block=(raw&&typeof raw==='object'&&!Array.isArray(raw)?raw:{}) as {id?:string;type?:string;data?:Record<string,unknown>};const d=block.data||{};const key=block.id||String(index);switch(block.type){case'paragraph':return <p key={key}><RichText html={text(d,'text')}/></p>;case'header':{const level=Math.min(4,Math.max(2,number(d,'level')||3));return React.createElement(`h${level}`,{key},<RichText html={text(d,'text')}/>)}case'quote':return <blockquote key={key}><RichText html={text(d,'text')}/>{text(d,'caption')?<cite>{text(d,'caption')}</cite>:null}</blockquote>;case'callout':return <aside key={key} className={`interactive-learn-callout tone-${String(d.tone||'info')}`}><Lightbulb className="size-5"/><div>{text(d,'title')?<strong>{text(d,'title')}</strong>:null}<p><RichText html={text(d,'text')}/></p></div></aside>;case'code':return <pre key={key} className="interactive-learn-nested-code"><code>{text(d,'code')}</code></pre>;case'list':return <div key={key} className="interactive-learn-list"><NestedList items={array(d,'items')} ordered={String(d.style)==='ordered'}/></div>;case'image':{const width=Math.min(100,Math.max(25,number(d,'widthPercent')||100));const alignment=['left','center','right'].includes(text(d,'alignment'))?text(d,'alignment'):'center';return <figure key={key} className={`interactive-learn-image align-${alignment}`}><img style={{width:`${width}%`}} src={`/api/learn/media/${number(d,'mediaId')}`} alt={text(d,'alt')}/>{text(d,'caption')?<figcaption>{text(d,'caption')}</figcaption>:null}</figure>}case'file':return <a key={key} className="interactive-learn-file" href={`/api/learn/media/${number(d,'mediaId')}`} target="_blank" rel="noreferrer"><FileDown className="size-5"/><span><strong>{text(d,'label')||text(d,'filename')||'Download file'}</strong>{text(d,'description')?<small>{text(d,'description')}</small>:null}</span></a>;default:return null}})}</div>}
function TabsBlock({data}:{data:Record<string,unknown>}){const tabs=array(data,'tabs').map((raw,index)=>{const tab=(raw&&typeof raw==='object'&&!Array.isArray(raw)?raw:{}) as {id?:string;title?:string;blocks?:unknown[];content?:string};return{id:tab.id||`tab-${index}`,title:tab.title||`Tab ${index+1}`,blocks:Array.isArray(tab.blocks)?tab.blocks:(tab.content?[{id:`legacy-${index}`,type:'paragraph',data:{text:tab.content}}]:[])}});const [active,setActive]=React.useState(0);const refs=React.useRef<Array<HTMLButtonElement|null>>([]);React.useEffect(()=>{if(active>=tabs.length)setActive(0)},[active,tabs.length]);if(!tabs.length)return null;const select=(index:number,focus=false)=>{const next=(index+tabs.length)%tabs.length;setActive(next);if(focus)queueMicrotask(()=>refs.current[next]?.focus())};const keyDown=(event:React.KeyboardEvent,index:number)=>{if(event.key==='ArrowRight'){event.preventDefault();select(index+1,true)}else if(event.key==='ArrowLeft'){event.preventDefault();select(index-1,true)}else if(event.key==='Home'){event.preventDefault();select(0,true)}else if(event.key==='End'){event.preventDefault();select(tabs.length-1,true)}};const activeTab=tabs[active];const panelId=`lesson-tabpanel-${activeTab.id}`,tabId=`lesson-tab-${activeTab.id}`;return <section className="interactive-learn-tabs"><div className="interactive-learn-tabs-nav" role="tablist" aria-label="Lesson tabs">{tabs.map((tab,index)=>{const id=`lesson-tab-${tab.id}`,controls=`lesson-tabpanel-${tab.id}`;return <button ref={node=>{refs.current[index]=node}} id={id} key={tab.id} type="button" role="tab" aria-selected={active===index} aria-controls={controls} tabIndex={active===index?0:-1} className={active===index?'is-active':''} onClick={()=>select(index)} onKeyDown={event=>keyDown(event,index)}>{tab.title}</button>})}</div><div id={panelId} role="tabpanel" aria-labelledby={tabId} tabIndex={0} className="interactive-learn-tabs-panel"><NestedBlocksView blocks={activeTab?.blocks||[]}/></div></section>}
function AccordionBlock({data}:{data:Record<string,unknown>}){return <div className="interactive-learn-accordion">{array(data,'items').map((raw,index)=>{const item=(raw&&typeof raw==='object'&&!Array.isArray(raw)?raw:{}) as {id?:string;title?:string;blocks?:unknown[];content?:string};const blocks=Array.isArray(item.blocks)?item.blocks:(item.content?[{id:`legacy-${index}`,type:'paragraph',data:{text:item.content}}]:[]);return <details key={item.id||index}><summary>{item.title||`Section ${index+1}`}</summary><NestedBlocksView blocks={blocks}/></details>})}</div>}

export function LessonRenderer({lesson,onLessonCompleted}:{lesson:PublicLearnLesson;onLessonCompleted?:()=>void}){const blocks=lesson.document?.blocks||[];return <div className="interactive-learn-renderer">{blocks.map(block=>{const d=block.data;switch(block.type){
  case'paragraph':return <p key={block.id}><RichText html={text(d,'text')}/></p>;
  case'header':{const level=Math.min(4,Math.max(2,number(d,'level')||2));return React.createElement(`h${level}`,{key:block.id},<RichText html={text(d,'text')}/>)}
  case'list':{const style=String(d.style||'unordered');if(style==='checklist')return <div key={block.id} className="interactive-learn-checklist">{array(d,'items').map((raw,index)=>{const item=typeof raw==='string'?{content:raw}:(raw||{}) as {content?:string;text?:string};return <label key={index}><input type="checkbox" readOnly/><RichText html={String(item.content||item.text||'')}/></label>})}</div>;return <div key={block.id} className="interactive-learn-list"><NestedList items={array(d,'items')} ordered={style==='ordered'}/></div>}
  case'checklist':return <div key={block.id} className="interactive-learn-checklist">{array(d,'items').map((raw,index)=>{const item=raw as {text?:string;checked?:boolean};return <label key={index}><input type="checkbox" checked={Boolean(item.checked)} readOnly/><RichText html={String(item.text||'')}/></label>})}</div>;
  case'quote':return <blockquote key={block.id}><RichText html={text(d,'text')}/>{text(d,'caption')?<cite>{text(d,'caption')}</cite>:null}</blockquote>;
  case'delimiter':return <hr key={block.id}/>;
  case'table':{const rows=array(d,'content').map(row=>Array.isArray(row)?row.map(cell=>String(cell??'')):[]);const headings=d.withHeadings!==false;return <div key={block.id} className="interactive-learn-table-wrap"><table><tbody>{rows.map((row,rowIndex)=><tr key={rowIndex}>{row.map((cell,columnIndex)=>headings&&rowIndex===0?<th key={columnIndex} scope="col"><RichText html={cell}/></th>:<td key={columnIndex}><RichText html={cell}/></td>)}</tr>)}</tbody></table></div>}
  case'callout':return <aside key={block.id} className={`interactive-learn-callout tone-${String(d.tone||'info')}`}><Lightbulb className="size-5"/><div>{text(d,'title')?<strong>{text(d,'title')}</strong>:null}<p><RichText html={text(d,'text')}/></p></div></aside>;
  case'image':{const width=Math.min(100,Math.max(25,number(d,'widthPercent')||100));const alignment=['left','center','right'].includes(text(d,'alignment'))?text(d,'alignment'):'center';return <figure key={block.id} className={`interactive-learn-image align-${alignment}`}><img style={{width:`${width}%`}} src={`/api/learn/media/${number(d,'mediaId')}`} alt={text(d,'alt')}/>{text(d,'caption')?<figcaption>{text(d,'caption')}</figcaption>:null}</figure>}
  case'gallery':return <div key={block.id} className="interactive-learn-gallery">{array(d,'mediaIds').map((id,index)=><img key={String(id)} src={`/api/learn/media/${Number(id)}`} alt={String(array(d,'alts')[index]||`Gallery image ${index+1}`)}/>)}</div>;
  case'video':return <VideoBlock key={block.id} block={block} lesson={lesson} onLessonCompleted={onLessonCompleted}/>;
  case'audio':return <AudioBlock key={block.id} block={block} lesson={lesson} onLessonCompleted={onLessonCompleted}/>;
  case'file':return <a key={block.id} className="interactive-learn-file" href={`/api/learn/media/${number(d,'mediaId')}`} target="_blank" rel="noreferrer"><FileDown className="size-5"/><span><strong>{text(d,'label')||text(d,'filename')||'Download file'}</strong>{text(d,'description')?<small>{text(d,'description')}</small>:null}</span></a>;
  case'code':return <RunnableCodeBlock key={block.id} block={block} lesson={lesson}/>;
  case'terminal':return <TerminalBlock key={block.id} block={block} lesson={lesson}/>;
  case'terminalTask':return <TerminalTaskBlock key={block.id} block={block} lesson={lesson} onLessonCompleted={onLessonCompleted}/>;
  case'codeExercise':return <CodeExercise key={block.id} block={block} lesson={lesson} onLessonCompleted={onLessonCompleted}/>;
  case'checkpoint':return <Checkpoint key={block.id} block={block} lesson={lesson} onLessonCompleted={onLessonCompleted}/>;
  case'solution':return <Solution key={block.id} block={block} lesson={lesson}/>;
  case'tabs':return <TabsBlock key={block.id} data={d}/>;
  case'accordion':return <AccordionBlock key={block.id} data={d}/>;
  case'legacyImport':return <section key={block.id} className="interactive-learn-callout interactive-learn-callout-warning"><strong>Legacy content requires administrator review</strong><p>This content could not be converted automatically during migration.</p></section>;
  default:return null;
}})}</div>}
