import { FileDown, Lightbulb, Play } from 'lucide-react';
import React from 'react';
import { LearnVideoPlayer } from '../learn/blocks/learn-video-player';
import type { InteractiveContentBlock, InteractiveContentDocument } from './types';

function text(data:Record<string,unknown>,key:string){return typeof data[key]==='string'?String(data[key]):'';}
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 record(value:unknown):Record<string,unknown>{return value&&typeof value==='object'&&!Array.isArray(value)?value as Record<string,unknown>:{};}
export function safeInteractiveInlineHtml(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 if(/^https?:/u.test(href)){el.setAttribute('rel','noopener noreferrer');el.setAttribute('target','_blank');}}}return doc.body.firstElementChild?.innerHTML||'';}
function RichText({html}:{html:string}){return <span dangerouslySetInnerHTML={{__html:safeInteractiveInlineHtml(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:[]} : record(raw);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>;}

export type InteractiveContentRunResult={exitCode:number;stdout:string;stderr:string};
export type InteractiveContentRendererProps={
  document:InteractiveContentDocument;
  interactive?:boolean;
  resolveMediaUrl?:(stored:string)=>string;
  onRunCode?:(block:InteractiveContentBlock,code:string,stdin:string)=>Promise<InteractiveContentRunResult>;
  staticPrint?:boolean;
};

function mediaUrl(data:Record<string,unknown>,resolve?:(stored:string)=>string,idKey='mediaId',urlKey='mediaUrl'){const raw=text(data,urlKey)||text(data,'assetPath')||text(data,'url');if(raw)return resolve?.(raw)||raw;const id=number(data,idKey);return id>0?`/api/learn/media/${id}`:'';}

function RunnableCode({block,interactive,run}:{block:InteractiveContentBlock;interactive:boolean;run?:InteractiveContentRendererProps['onRunCode']}){const d=block.data;const [code,setCode]=React.useState(text(d,'code'));const [stdin,setStdin]=React.useState(text(d,'stdin'));const [busy,setBusy]=React.useState(false);const [result,setResult]=React.useState<InteractiveContentRunResult>();const [error,setError]=React.useState('');const runnable=d.runnable===true;const allowStdin=d.allowStdin===true;const execute=async()=>{if(!run)return;setBusy(true);setError('');try{setResult(await run(block,code,allowStdin?stdin:''));}catch(reason){setError(reason instanceof Error?reason.message:String(reason));}finally{setBusy(false);}};return <section className="interactive-learn-code interactive-doc-code" data-block-id={block.id}><header><span>{text(d,'filename')||text(d,'language')||'Code'}</span>{interactive&&runnable&&run?<button type="button" onClick={()=>void execute()} disabled={busy}><Play className="size-4"/>{busy?'Running…':'Run'}</button>:null}</header>{interactive&&runnable?<textarea value={code} onChange={(event)=>setCode(event.currentTarget.value)} spellCheck={false} className="interactive-learn-code-editor"/>:<pre><code>{code}</code></pre>}{interactive&&runnable&&allowStdin?<label className="interactive-learn-code-stdin"><span>Standard input</span><textarea value={stdin} onChange={(event)=>setStdin(event.currentTarget.value)} spellCheck={false}/></label>:null}{error?<p className="interactive-error-copy">{error}</p>:null}{result?<div className={result.exitCode===0?'interactive-learn-feedback is-correct':'interactive-learn-feedback is-incorrect'}><strong>Exit {result.exitCode}</strong>{result.stdout?<pre>{result.stdout}</pre>:null}{result.stderr?<pre>{result.stderr}</pre>:null}</div>:null}</section>;}

function NestedBlocks({blocks,props,parentId}:{blocks:unknown[];props:InteractiveContentRendererProps;parentId:string}){return <div className="interactive-learn-nested-blocks">{blocks.slice(0,100).map((raw,index)=>{const item=record(raw);const block:InteractiveContentBlock={id:String(item.id||`${parentId}-nested-${index}`),type:String(item.type||'paragraph'),data:record(item.data)};return <Block key={block.id} block={block} props={props}/>;})}</div>;}
function Tabs({block,props}:{block:InteractiveContentBlock;props:InteractiveContentRendererProps}){const tabs=array(block.data,'tabs').map((raw,index)=>{const tab=record(raw);return{id:String(tab.id||`tab-${index}`),title:String(tab.title||`Tab ${index+1}`),blocks:Array.isArray(tab.blocks)?tab.blocks:[]};});const [active,setActive]=React.useState(0);const buttons=React.useRef<Array<HTMLButtonElement|null>>([]);if(!tabs.length)return null;const current=tabs[Math.min(active,tabs.length-1)];const activate=(index:number)=>{const next=(index+tabs.length)%tabs.length;setActive(next);requestAnimationFrame(()=>buttons.current[next]?.focus());};const onKeyDown=(event:React.KeyboardEvent,index:number)=>{if(event.key==='ArrowRight'){event.preventDefault();activate(index+1);}else if(event.key==='ArrowLeft'){event.preventDefault();activate(index-1);}else if(event.key==='Home'){event.preventDefault();activate(0);}else if(event.key==='End'){event.preventDefault();activate(tabs.length-1);}};return <section className="interactive-learn-tabs"><div role="tablist" className="interactive-learn-tabs-nav">{tabs.map((tab,index)=>{const tabId=`tab-${block.id}-${tab.id}`;const panelId=`panel-${block.id}-${tab.id}`;return <button key={tab.id} ref={(node)=>{buttons.current[index]=node;}} id={tabId} type="button" role="tab" aria-controls={panelId} aria-selected={active===index} tabIndex={active===index?0:-1} className={active===index?'is-active':''} onKeyDown={(event)=>onKeyDown(event,index)} onClick={()=>setActive(index)}>{tab.title}</button>;})}</div><div id={`panel-${block.id}-${current.id}`} role="tabpanel" aria-labelledby={`tab-${block.id}-${current.id}`} className="interactive-learn-tabs-panel"><NestedBlocks blocks={current.blocks} props={props} parentId={block.id}/></div></section>;}
function Accordion({block,props}:{block:InteractiveContentBlock;props:InteractiveContentRendererProps}){return <div className="interactive-learn-accordion">{array(block.data,'items').map((raw,index)=>{const item=record(raw);return <details key={String(item.id||index)} open={props.staticPrint||undefined}><summary>{String(item.title||`Section ${index+1}`)}</summary><NestedBlocks blocks={Array.isArray(item.blocks)?item.blocks:[]} props={props} parentId={`${block.id}-${index}`}/></details>;})}</div>;}

function staticStoredMediaUrl(data:Record<string,unknown>,idKey='mediaId',urlKey='mediaUrl'){const raw=text(data,urlKey)||text(data,'assetPath')||text(data,'url');if(raw)return raw.startsWith('project-asset:')?raw.slice('project-asset:'.length):raw;const id=number(data,idKey);return id>0?`/api/learn/media/${id}`:'';}
function staticVideoUrl(data:Record<string,unknown>){const source=String(data.source||'upload');if(source==='youtube'&&text(data,'externalId'))return`https://www.youtube.com/watch?v=${encodeURIComponent(text(data,'externalId'))}`;if(source==='wistia'&&text(data,'externalId'))return`https://home.wistia.com/medias/${encodeURIComponent(text(data,'externalId'))}`;if(source==='direct')return text(data,'directUrl');return staticStoredMediaUrl(data);}
function StaticMediaLink({label,url,kind}:{label:string;url:string;kind:string}){return <div className="interactive-doc-static-media"><strong>{label||kind}</strong>{url?<span>{url}</span>:null}</div>;}

function Block({block,props}:{block:InteractiveContentBlock;props:InteractiveContentRendererProps}){const d=block.data;const resolve=props.resolveMediaUrl;switch(block.type){
  case'paragraph':return <p data-block-id={block.id}><RichText html={text(d,'text')}/></p>;
  case'header':return React.createElement(`h${Math.min(4,Math.max(2,number(d,'level')||2))}`,{'data-block-id':block.id},<RichText html={text(d,'text')}/>);
  case'list':return <div className="interactive-learn-list" data-block-id={block.id}><NestedList items={array(d,'items')} ordered={String(d.style)==='ordered'}/></div>;
  case'checklist':return <div className="interactive-learn-checklist" data-block-id={block.id}>{array(d,'items').map((raw,index)=>{const item=record(raw);return <label key={index}><input type="checkbox" checked={Boolean(item.checked)} readOnly/><RichText html={String(item.text??item.content??'')}/></label>;})}</div>;
  case'quote':return <blockquote data-block-id={block.id}><RichText html={text(d,'text')}/>{text(d,'caption')?<cite>{text(d,'caption')}</cite>:null}</blockquote>;
  case'delimiter':return <hr data-block-id={block.id}/>;
  case'table':return <div className="interactive-learn-table-wrap" data-block-id={block.id}><table><tbody>{array(d,'content').map((raw,rowIndex)=>{const row=Array.isArray(raw)?raw:[];return <tr key={rowIndex}>{row.map((cell,columnIndex)=>d.withHeadings!==false&&rowIndex===0?<th key={columnIndex} scope="col"><RichText html={String(cell??'')}/></th>:<td key={columnIndex}><RichText html={String(cell??'')}/></td>)}</tr>;})}</tbody></table></div>;
  case'callout':return <aside className={`interactive-learn-callout tone-${String(d.tone||'info')}`} data-block-id={block.id}><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 src=mediaUrl(d,resolve);return src?<figure className={`interactive-learn-image align-${text(d,'alignment')||'center'}`} data-block-id={block.id}><img style={{width:`${Math.min(100,Math.max(25,number(d,'widthPercent')||100))}%`}} src={src} alt={text(d,'alt')}/>{text(d,'caption')?<figcaption>{text(d,'caption')}</figcaption>:null}</figure>:null;}
  case'gallery':{const values=(array(d,'mediaUrls').length?array(d,'mediaUrls'):array(d,'assetPaths')).map(String);return <figure className="interactive-learn-gallery" data-block-id={block.id}>{values.map((stored,index)=><img key={`${stored}-${index}`} src={resolve?.(stored)||stored} alt={String(array(d,'alts')[index]||`Gallery image ${index+1}`)}/>)}</figure>;}
  case'video':{const source=String(d.source||'upload');if(props.staticPrint){const url=staticVideoUrl(d);return <StaticMediaLink label={text(d,'title')||'Video'} url={url} kind="Video"/>;}if(source==='youtube'&&text(d,'externalId'))return <div className="interactive-learn-video" data-block-id={block.id}><iframe title={text(d,'title')||'YouTube video'} src={`https://www.youtube-nocookie.com/embed/${encodeURIComponent(text(d,'externalId'))}`} allowFullScreen/></div>;if(source==='wistia'&&text(d,'externalId'))return <div className="interactive-learn-video" data-block-id={block.id}><LearnVideoPlayer provider="wistia" externalId={text(d,'externalId')} title={text(d,'title')||'Video'}/></div>;const src=source==='direct'?text(d,'directUrl'):mediaUrl(d,resolve);return src?<video className="interactive-learn-video" data-block-id={block.id} controls src={src}/>:null;}
  case'audio':{const src=mediaUrl(d,resolve);return props.staticPrint?<StaticMediaLink label={text(d,'title')||'Audio'} url={staticStoredMediaUrl(d)} kind="Audio"/>:src?<audio data-block-id={block.id} controls src={src}/>:null;}
  case'file':{const src=mediaUrl(d,resolve);if(props.staticPrint)return <StaticMediaLink label={text(d,'label')||text(d,'filename')||'File'} url={staticStoredMediaUrl(d)} kind="File"/>;return src?<a className="interactive-learn-file" data-block-id={block.id} href={src} 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>:null;}
  case'code':return <RunnableCode block={block} interactive={props.interactive!==false&&!props.staticPrint} run={props.onRunCode}/>;
  case'tabs':return props.staticPrint?<section className="interactive-doc-static-sections">{array(d,'tabs').map((raw,index)=>{const tab=record(raw);return <section key={String(tab.id||index)}><h3>{String(tab.title||`Tab ${index+1}`)}</h3><NestedBlocks blocks={Array.isArray(tab.blocks)?tab.blocks:[]} props={props} parentId={`${block.id}-${index}`}/></section>;})}</section>:<Tabs block={block} props={props}/>;
  case'accordion':return <Accordion block={block} props={props}/>;
  default:return null;
}}

export function InteractiveContentRenderer(props:InteractiveContentRendererProps){return <div className={`interactive-learn-renderer interactive-content-renderer${props.staticPrint?' is-static-print':''}`}>{props.document.blocks.map((block)=><Block key={block.id} block={block} props={props}/>)}</div>;}
