import {
  Button,
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
  Input,
  Label,
  toast,
} from '@learnitive/ui-components';
import { ArchiveRestore, ExternalLink, FileText, History, Pencil, Plus, Search, Trash2 } from 'lucide-react';
import React from 'react';
import { requestPageConfirm } from '../components/page-modal-service';
import { InteractiveContentEditor } from '../interactive-content/editor';
import type { InteractiveContentEditorAdapter, InteractiveContentEditorMedia, InteractiveContentMediaKind } from '../interactive-content/editor-adapter';
import { CmsInteractiveContent, type CmsInteractiveRuntime } from '../interactive-content/cms-renderer';
import { adminRunLearnCode } from '../learn/api';
import { createInteractiveContentTools } from '../learn/editor/editor-tools';
import type { LessonDocument } from '../learn/types';
import {
  SaasApiError,
  adminCmsPreviewRuntime,
  adminCreateCmsRevision,
  adminDeleteCmsMedia,
  adminGet,
  adminListCmsMedia,
  adminListCmsRevisions,
  adminMutate,
  adminPreviewInteractiveContent,
  adminPurgeCmsContent,
  adminRestoreCmsContent,
  adminRestoreCmsRevision,
  adminUploadBlogCover,
  adminUploadCmsMediaWithProgress,
  type AdminCmsDetail,
  type AdminCmsListItem,
  type CmsInteractiveKind,
  type CmsMediaAsset,
  type CmsRevision,
} from './saas-api';

const EMPTY_DOCUMENT:LessonDocument={schemaVersion:1,time:Date.now(),blocks:[]};
const PAGE_SIZE=25;
type SaveState='saved'|'dirty'|'saving'|'conflict'|'error';

function safeDate(value?:string){if(!value)return '—';const date=new Date(value);return Number.isNaN(date.getTime())?value:date.toLocaleDateString(undefined,{year:'numeric',month:'short',day:'numeric'});}
function AdminCmsHead({kind,editing}:{kind:CmsInteractiveKind;editing?:boolean}){const title=kind==='blog'?'Blog':'Docs';return <div className="interactive-admin-page-head"><div><span className="interactive-eyebrow">Interactive content</span><h1 className="interactive-display">{editing?`Edit ${kind==='blog'?'post':'doc'}`:`${title} CMS`}</h1><p>{editing?`Build this ${kind==='blog'?'article':'documentation page'} with the same Editor.js blocks used by Interactive Learn.`:`Manage ${kind==='blog'?'articles':'documentation pages'} here. Editing opens in a dedicated interactive builder.`}</p></div>{!editing?<div className="interactive-admin-page-actions"><a className="interactive-button interactive-button-primary" href={`/admin/${kind}/new`}><Plus className="size-4"/> New {kind==='blog'?'post':'doc'}</a></div>:null}</div>;}
function useCmsAdminList(kind:CmsInteractiveKind){const [items,setItems]=React.useState<AdminCmsListItem[]>([]);const [error,setError]=React.useState('');const [loading,setLoading]=React.useState(true);const refresh=React.useCallback(async()=>{setLoading(true);setError('');try{const result=await adminGet<{items:AdminCmsListItem[]}>(`/api/admin/${kind}`);setItems(result.items);}catch(reason){setError(reason instanceof Error?reason.message:String(reason));}finally{setLoading(false)}},[kind]);React.useEffect(()=>{void refresh()},[refresh]);return{items,error,loading,refresh};}

export function AdminCmsListPage({kind}:{kind:CmsInteractiveKind}){
  const {items,error,loading,refresh}=useCmsAdminList(kind);const [query,setQuery]=React.useState('');const [status,setStatus]=React.useState<'all'|'draft'|'published'>('all');const [category,setCategory]=React.useState('');const [view,setView]=React.useState<'active'|'trash'>('active');const [page,setPage]=React.useState(1);
  const categories=React.useMemo(()=>[...new Set(items.map(item=>item.category||'').filter(Boolean))].sort(),[items]);
  const filtered=React.useMemo(()=>items.filter(item=>{if(view==='active'&&item.deleted_at)return false;if(view==='trash'&&!item.deleted_at)return false;if(status!=='all'&&item.status!==status)return false;if(kind==='docs'&&category&&item.category!==category)return false;const needle=query.trim().toLowerCase();return !needle||`${item.title} ${item.slug} ${item.category||''}`.toLowerCase().includes(needle)}),[category,items,kind,query,status,view]);
  React.useEffect(()=>setPage(1),[query,status,category,view]);
  const pageCount=Math.max(1,Math.ceil(filtered.length/PAGE_SIZE));const visible=filtered.slice((Math.min(page,pageCount)-1)*PAGE_SIZE,Math.min(page,pageCount)*PAGE_SIZE);
  const trash=async(item:AdminCmsListItem)=>{const confirmed=await requestPageConfirm({title:`Move “${item.title}” to Trash?`,description:'The public URL will stop resolving immediately. You can restore it later from Trash.',confirmText:'Move to Trash',tone:'destructive'});if(!confirmed)return;try{await adminMutate(`/api/admin/${kind}/${item.id}`,'DELETE');toast.success('Moved to Trash.');await refresh();}catch(reason){toast.error(reason instanceof Error?reason.message:String(reason));}};
  const restore=async(item:AdminCmsListItem)=>{try{await adminRestoreCmsContent(kind,item.id);toast.success('Content restored.');await refresh();}catch(reason){toast.error(reason instanceof Error?reason.message:String(reason));}};
  const purge=async(item:AdminCmsListItem)=>{const confirmed=await requestPageConfirm({title:`Permanently delete “${item.title}”?`,description:'This deletes structured content, revisions, interactive attempts and slug history. This cannot be undone.',confirmText:'Delete permanently',tone:'destructive'});if(!confirmed)return;try{await adminPurgeCmsContent(kind,item.id);toast.success('Content permanently deleted.');await refresh();}catch(reason){toast.error(reason instanceof Error?reason.message:String(reason));}};
  return <><AdminCmsHead kind={kind}/><div className="interactive-admin-template-editor"><div className="interactive-admin-content-toolbar"><div className="interactive-admin-content-toolbar-tabs"><Button type="button" variant={view==='active'?'default':'outline'} onClick={()=>setView('active')}>Active</Button><Button type="button" variant={view==='trash'?'default':'outline'} onClick={()=>setView('trash')}>Trash</Button></div><label className="interactive-admin-content-toolbar-search"><Search className="size-4" aria-hidden/><Input aria-label={`Search ${kind}`} placeholder={`Search ${kind}…`} value={query} onChange={event=>setQuery(event.currentTarget.value)}/></label><select aria-label="Filter status" value={status} onChange={event=>setStatus(event.currentTarget.value as typeof status)}><option value="all">All statuses</option><option value="draft">Draft</option><option value="published">Published</option></select>{kind==='docs'?<select aria-label="Filter category" value={category} onChange={event=>setCategory(event.currentTarget.value)}><option value="">All categories</option>{categories.map(value=><option key={value}>{value}</option>)}</select>:null}</div></div>{error?<div className="interactive-error" role="alert">{error}</div>:null}{loading?<div className="interactive-loading-card">Loading…</div>:visible.length?<><div className="interactive-admin-table-wrap"><table className="interactive-admin-table"><thead><tr><th scope="col">Title</th>{kind==='docs'?<th scope="col">Category</th>:null}<th scope="col">Status</th><th scope="col">Updated</th><th scope="col">Actions</th></tr></thead><tbody>{visible.map(item=><tr key={item.id}><td><strong>{item.title}</strong><div className="interactive-muted">/{kind}/{item.slug}</div></td>{kind==='docs'?<td>{item.category||'Documentation'}</td>:null}<td><span className={`interactive-status-badge status-${item.status}`}>{item.status}</span></td><td>{safeDate(item.updated_at)}</td><td className="interactive-admin-actions">{view==='active'?<><a className="interactive-button interactive-button-secondary interactive-button-sm" href={`/admin/${kind}/${item.id}/edit`}><Pencil className="size-4"/> Edit</a>{item.status==='published'?<a className="interactive-button interactive-button-secondary interactive-button-sm" href={`/${kind}/${item.slug}`} target="_blank" rel="noreferrer"><ExternalLink className="size-4"/> View</a>:null}<button className="interactive-button interactive-button-ghost interactive-button-sm" type="button" onClick={()=>void trash(item)}><Trash2 className="size-4"/> Trash</button></>:<><button className="interactive-button interactive-button-secondary interactive-button-sm" type="button" onClick={()=>void restore(item)}><ArchiveRestore className="size-4"/> Restore</button><button className="interactive-button interactive-button-ghost interactive-button-sm" type="button" onClick={()=>void purge(item)}><Trash2 className="size-4"/> Delete permanently</button></>}</td></tr>)}</tbody></table></div><div className="interactive-admin-page-actions"><span className="interactive-muted">{filtered.length} item{filtered.length===1?'':'s'} · Page {Math.min(page,pageCount)} of {pageCount}</span><div className="interactive-inline-form"><Button type="button" variant="outline" disabled={page<=1} onClick={()=>setPage(value=>Math.max(1,value-1))}>Previous</Button><Button type="button" variant="outline" disabled={page>=pageCount} onClick={()=>setPage(value=>Math.min(pageCount,value+1))}>Next</Button></div></div></>:<div className="interactive-empty-state"><FileText className="size-6"/><h2>No matching {kind==='blog'?'blog posts':'documentation pages'}.</h2>{view==='active'?<a className="interactive-button interactive-button-primary" href={`/admin/${kind}/new`}>Create one</a>:null}</div>}</>;
}

type CmsForm={title:string;slug:string;excerpt:string;category:string;sortOrder:string;tags:string;status:'draft'|'published';coverImageUrl:string;coverImageAlt:string;coverImageWidth:number;coverImageHeight:number;seoTitle:string;seoDescription:string;noindex:boolean;document:LessonDocument;editVersion:number;migrationWarnings:string[]};
function blankForm(kind:CmsInteractiveKind):CmsForm{return{title:'',slug:'',excerpt:'',category:kind==='docs'?'Getting Started':'',sortOrder:'0',tags:'',status:'draft',coverImageUrl:'',coverImageAlt:'',coverImageWidth:0,coverImageHeight:0,seoTitle:'',seoDescription:'',noindex:false,document:{...EMPTY_DOCUMENT,time:Date.now()},editVersion:0,migrationWarnings:[]};}
function detailForm(kind:CmsInteractiveKind,item:AdminCmsDetail):CmsForm{return{title:item.title||'',slug:item.slug||'',excerpt:item.excerpt||'',category:item.category||'Getting Started',sortOrder:String(item.sort_order??0),tags:(item.tags||[]).join(', '),status:item.status==='published'?'published':'draft',coverImageUrl:item.cover_image_url||'',coverImageAlt:item.cover_image_alt||'',coverImageWidth:Number(item.cover_image_width||0),coverImageHeight:Number(item.cover_image_height||0),seoTitle:item.seo_title||'',seoDescription:item.seo_description||'',noindex:Boolean(item.noindex),document:item.content_document||{...EMPTY_DOCUMENT,time:Date.now()},editVersion:Number(item.edit_version||1),migrationWarnings:item.migration_warnings||[]};}
function mediaAsEditor(asset:CmsMediaAsset):InteractiveContentEditorMedia{return{id:asset.id,url:asset.url,originalName:asset.originalName||asset.original_name||'CMS media',mimeType:asset.mimeType||asset.mime_type,sizeBytes:asset.sizeBytes||asset.size_bytes,width:asset.width,height:asset.height};}

export function AdminCmsEditorPage({kind,id}:{kind:CmsInteractiveKind;id?:number}){
  const editing=Boolean(id);const [form,setForm]=React.useState<CmsForm>(()=>blankForm(kind));const [loading,setLoading]=React.useState(editing);const [saveState,setSaveState]=React.useState<SaveState>(editing?'saved':'dirty');const [preview,setPreview]=React.useState<LessonDocument>();const [previewOpen,setPreviewOpen]=React.useState(false);const [uploading,setUploading]=React.useState(false);const [historyOpen,setHistoryOpen]=React.useState(false);const [revisions,setRevisions]=React.useState<CmsRevision[]>([]);const [media,setMedia]=React.useState<CmsMediaAsset[]>([]);const [mediaOpen,setMediaOpen]=React.useState(false);const changeVersion=React.useRef(0);const saveInFlight=React.useRef(false);const formRef=React.useRef(form);React.useEffect(()=>{formRef.current=form},[form]);
  const markChanged=React.useCallback((apply:(current:CmsForm)=>CmsForm)=>{changeVersion.current+=1;setSaveState('dirty');setForm(apply);setPreview(undefined);},[]);
  React.useEffect(()=>{if(!id){setForm(blankForm(kind));setLoading(false);setSaveState('dirty');return;}let active=true;setLoading(true);void adminGet<{item:AdminCmsDetail}>(`/api/admin/${kind}/${id}`).then(result=>{if(active){setForm(detailForm(kind,result.item));setSaveState('saved');changeVersion.current=0;}}).catch(reason=>toast.error(reason instanceof Error?reason.message:String(reason))).finally(()=>{if(active)setLoading(false)});return()=>{active=false};},[id,kind]);
  const populateMediaPicker=React.useCallback(async(host:HTMLElement,mediaKind:InteractiveContentMediaKind,onPick:(media:InteractiveContentEditorMedia)=>void)=>{host.replaceChildren();const assets=await adminListCmsMedia(kind,mediaKind);const grid=document.createElement('div');grid.className='interactive-admin-cms-media-picker';if(!assets.length){const empty=document.createElement('p');empty.className='interactive-muted';empty.textContent='No reusable media uploaded yet.';host.append(empty);return;}for(const asset of assets){const button=document.createElement('button');button.type='button';button.className='interactive-admin-cms-media-choice';if(mediaKind==='image'){const image=document.createElement('img');image.src=asset.url;image.alt='';button.append(image);}const label=document.createElement('span');label.textContent=asset.originalName||asset.original_name||asset.url;button.append(label);button.addEventListener('click',()=>onPick(mediaAsEditor(asset)));grid.append(button);}host.append(grid);},[kind]);
  const adapter=React.useMemo<InteractiveContentEditorAdapter>(()=>({context:kind,runtimeDefaultLabel:'Use block runtime',requiredLabel:'Required interactive activity',populateMediaPicker,uploadMedia:async(mediaKind,file,onProgress,signal)=>{const uploaded=await adminUploadCmsMediaWithProgress(kind,mediaKind,file,onProgress||(()=>{}),signal);return{url:uploaded.url,originalName:uploaded.originalName||file.name,mimeType:uploaded.mimeType||file.type,sizeBytes:uploaded.sizeBytes??file.size,width:uploaded.width,height:uploaded.height};},runCodePreview:input=>adminRunLearnCode(input)}),[kind,populateMediaPicker]);
  const tools=React.useMemo(()=>createInteractiveContentTools(adapter),[adapter]);
  const payload=React.useCallback((value:CmsForm,withVersion=true)=>({title:value.title,slug:value.slug,excerpt:value.excerpt,category:value.category,sortOrder:Number(value.sortOrder)||0,tags:value.tags.split(',').map(entry=>entry.trim()).filter(Boolean),status:value.status,coverImageUrl:value.coverImageUrl||null,coverImageAlt:value.coverImageAlt||null,coverImageWidth:value.coverImageWidth||null,coverImageHeight:value.coverImageHeight||null,seoTitle:value.seoTitle||null,seoDescription:value.seoDescription||null,noindex:value.noindex,contentDocument:value.document,...(editing&&withVersion?{expectedVersion:value.editVersion}:{})}),[editing]);
  const persist=React.useCallback(async({notify=false}:{notify?:boolean}={})=>{if(!editing||!id||saveInFlight.current||saveState==='conflict')return;const snapshot=formRef.current;if(!snapshot.title.trim())return;const versionAtStart=changeVersion.current;saveInFlight.current=true;setSaveState('saving');try{const result=await adminMutate<{item:{id:number;editVersion?:number}}>(`/api/admin/${kind}/${id}`,'PATCH',payload(snapshot));const nextVersion=Number(result.item.editVersion||snapshot.editVersion+1);setForm(current=>({...current,editVersion:nextVersion}));if(changeVersion.current===versionAtStart)setSaveState('saved');else setSaveState('dirty');if(notify)toast.success('Changes saved.');}catch(reason){if(reason instanceof SaasApiError&&reason.status===409){setSaveState('conflict');toast.error('This content changed elsewhere. Reload or restore a checkpoint before continuing.');}else{setSaveState('error');if(notify)toast.error(reason instanceof Error?reason.message:String(reason));}}finally{saveInFlight.current=false;}},[editing,id,kind,payload,saveState]);
  React.useEffect(()=>{if(!editing||saveState!=='dirty')return;const timer=window.setTimeout(()=>void persist(),900);return()=>window.clearTimeout(timer);},[editing,persist,saveState,form]);
  React.useEffect(()=>{const handler=(event:BeforeUnloadEvent)=>{if(saveState==='dirty'||saveState==='saving'){event.preventDefault();event.returnValue='';}};window.addEventListener('beforeunload',handler);return()=>window.removeEventListener('beforeunload',handler);},[saveState]);
  const create=async(event:React.FormEvent)=>{event.preventDefault();if(editing){await persist({notify:true});return;}if(!form.title.trim())return;setSaveState('saving');try{const result=await adminMutate<{item:{id:number}}>(`/api/admin/${kind}`,'POST',payload(form,false));toast.success('Content created.');window.location.href=`/admin/${kind}/${result.item.id}/edit`;}catch(reason){setSaveState('error');toast.error(reason instanceof Error?reason.message:String(reason));}};
  const uploadCover=async(file?:File)=>{if(!file)return;setUploading(true);try{const uploaded=await adminUploadBlogCover(file);markChanged(current=>({...current,coverImageUrl:uploaded.url,coverImageWidth:uploaded.width,coverImageHeight:uploaded.height}));toast.success('Cover image uploaded.');}catch(reason){toast.error(reason instanceof Error?reason.message:String(reason));}finally{setUploading(false)}};
  const openPreview=async()=>{try{const result=await adminPreviewInteractiveContent(form.document);setPreview(result.contentDocument);setPreviewOpen(true);}catch(reason){toast.error(reason instanceof Error?reason.message:String(reason));}};
  const previewRuntime=React.useMemo<CmsInteractiveRuntime>(()=>({
    runCode:async(blockId,code,stdin)=>(await adminCmsPreviewRuntime(kind,formRef.current.document,blockId,'run-code',{code,stdin})).result as Awaited<ReturnType<CmsInteractiveRuntime['runCode']>>,
    runExercise:async(blockId,files,entryFile,stdin)=>(await adminCmsPreviewRuntime(kind,formRef.current.document,blockId,'run',{files,entryFile,stdin})).result as Awaited<ReturnType<CmsInteractiveRuntime['runExercise']>>,
    checkExercise:async(blockId,files,entryFile)=>(await adminCmsPreviewRuntime(kind,formRef.current.document,blockId,'check',{files,entryFile})).result as Awaited<ReturnType<CmsInteractiveRuntime['checkExercise']>>,
    gradeCheckpoint:async(blockId,answer)=>(await adminCmsPreviewRuntime(kind,formRef.current.document,blockId,'checkpoint',{answer})).result as Awaited<ReturnType<CmsInteractiveRuntime['gradeCheckpoint']>>,
    getSolution:async blockId=>(await adminCmsPreviewRuntime(kind,formRef.current.document,blockId,'solution')).solution as Awaited<ReturnType<CmsInteractiveRuntime['getSolution']>>,
    startTerminalTask:async blockId=>await adminCmsPreviewRuntime(kind,formRef.current.document,blockId,'terminal-task/start') as Awaited<ReturnType<CmsInteractiveRuntime['startTerminalTask']>>,
    verifyTerminalTask:async(blockId,attemptId)=>(await adminCmsPreviewRuntime(kind,formRef.current.document,blockId,'terminal-task/verify',{attemptId})).result as Awaited<ReturnType<CmsInteractiveRuntime['verifyTerminalTask']>>,
    resetTerminalTask:async(blockId,attemptId)=>{await adminCmsPreviewRuntime(kind,formRef.current.document,blockId,'terminal-task/reset',{attemptId});},
  }),[kind]);
  const loadHistory=async()=>{if(!id)return;try{setRevisions(await adminListCmsRevisions(kind,id));setHistoryOpen(true);}catch(reason){toast.error(reason instanceof Error?reason.message:String(reason));}};
  const checkpoint=async()=>{if(!id)return;await persist();try{await adminCreateCmsRevision(kind,id);toast.success('Checkpoint saved.');setRevisions(await adminListCmsRevisions(kind,id));setHistoryOpen(true);}catch(reason){toast.error(reason instanceof Error?reason.message:String(reason));}};
  const restoreRevision=async(revision:CmsRevision)=>{if(!id)return;const confirmed=await requestPageConfirm({title:`Restore checkpoint from ${safeDate(revision.created_at)}?`,description:'The current content will be replaced by this checkpoint. Save a checkpoint first if you need to preserve it.',confirmText:'Restore checkpoint'});if(!confirmed)return;try{await adminRestoreCmsRevision(kind,id,revision.id);const refreshed=await adminGet<{item:AdminCmsDetail}>(`/api/admin/${kind}/${id}`);setForm(detailForm(kind,refreshed.item));changeVersion.current=0;setSaveState('saved');setHistoryOpen(false);toast.success('Checkpoint restored.');}catch(reason){toast.error(reason instanceof Error?reason.message:String(reason));}};
  const openMedia=async()=>{try{setMedia(await adminListCmsMedia(kind));setMediaOpen(true);}catch(reason){toast.error(reason instanceof Error?reason.message:String(reason));}};
  const removeMedia=async(asset:CmsMediaAsset)=>{try{await adminDeleteCmsMedia(kind,asset.id);setMedia(current=>current.filter(item=>item.id!==asset.id));toast.success('Unused media deleted.');}catch(reason){toast.error(reason instanceof Error?reason.message:String(reason));}};
  const patch=<K extends keyof CmsForm>(key:K,value:CmsForm[K])=>markChanged(current=>({...current,[key]:value}));
  if(loading)return <div className="interactive-loading-card">Loading interactive editor…</div>;
  const saveCopy=saveState==='saving'?'Saving…':saveState==='dirty'?'Unsaved changes':saveState==='conflict'?'Save conflict':saveState==='error'?'Save failed':'Saved';
  return <><AdminCmsHead kind={kind} editing/><div className="interactive-admin-page-actions interactive-admin-cms-editor-top-actions"><a className="interactive-button interactive-button-secondary" href={`/admin/${kind}`}>← Back to {kind==='blog'?'Blog':'Docs'}</a><div className="interactive-inline-form">{editing?<><Button type="button" variant="outline" onClick={()=>void openMedia()}>Media library</Button><Button type="button" variant="outline" onClick={()=>void checkpoint()}>Save checkpoint</Button><Button type="button" variant="outline" onClick={()=>void loadHistory()}><History className="size-4"/> History</Button></>:null}<Button type="button" variant="outline" onClick={()=>void openPreview()}>Preview</Button></div></div>{form.migrationWarnings.length?<div className="interactive-learn-import-warning"><strong>Migration review</strong><ul>{form.migrationWarnings.map((warning,index)=><li key={`${warning}-${index}`}>{warning}</li>)}</ul></div>:null}<form className="interactive-admin-cms-editor-page" onSubmit={event=>void create(event)}><section className="interactive-admin-template-editor"><div className="interactive-admin-form-grid"><div><Label htmlFor={`${kind}-title`}>Title</Label><Input id={`${kind}-title`} required value={form.title} onChange={event=>patch('title',event.currentTarget.value)}/></div><div><Label htmlFor={`${kind}-slug`}>Slug</Label><Input id={`${kind}-slug`} value={form.slug} onChange={event=>patch('slug',event.currentTarget.value)}/></div>{kind==='blog'?<div className="interactive-admin-form-span"><Label htmlFor="blog-excerpt">Excerpt</Label><Input id="blog-excerpt" value={form.excerpt} onChange={event=>patch('excerpt',event.currentTarget.value)}/></div>:<><div><Label htmlFor="docs-category">Category</Label><Input id="docs-category" value={form.category} onChange={event=>patch('category',event.currentTarget.value)}/></div><div><Label htmlFor="docs-sort">Sort order</Label><Input id="docs-sort" type="number" value={form.sortOrder} onChange={event=>patch('sortOrder',event.currentTarget.value)}/></div></>}<div className="interactive-admin-form-span"><Label htmlFor={`${kind}-tags`}>Tags</Label><Input id={`${kind}-tags`} value={form.tags} onChange={event=>patch('tags',event.currentTarget.value)} placeholder="interactive, code, tutorial"/></div><div><Label htmlFor={`${kind}-status`}>Status</Label><select id={`${kind}-status`} value={form.status} onChange={event=>patch('status',event.currentTarget.value as 'draft'|'published')}><option value="draft">Draft</option><option value="published">Published</option></select></div></div>{kind==='blog'?<section className="interactive-blog-cover-admin"><div className="interactive-blog-cover-admin-head"><div><h3>Cover image</h3><p>Used on Blog cards and article metadata. Interactive body media belongs inside the content builder.</p></div><label className="interactive-button interactive-button-secondary interactive-button-sm">{uploading?'Uploading…':form.coverImageUrl?'Replace image':'Upload image'}<input className="sr-only" type="file" accept="image/png,image/jpeg,image/webp" disabled={uploading} onChange={event=>{void uploadCover(event.currentTarget.files?.[0]);event.currentTarget.value='';}}/></label></div>{form.coverImageUrl?<div className="interactive-blog-cover-admin-preview"><img src={form.coverImageUrl} alt={form.coverImageAlt}/><Button type="button" size="sm" variant="ghost" onClick={()=>markChanged(current=>({...current,coverImageUrl:'',coverImageAlt:'',coverImageWidth:0,coverImageHeight:0}))}>Remove</Button></div>:null}<Label htmlFor="blog-cover-alt">Cover alt text</Label><Input id="blog-cover-alt" value={form.coverImageAlt} onChange={event=>patch('coverImageAlt',event.currentTarget.value)}/></section>:null}<section className="interactive-admin-cms-seo"><span className="interactive-eyebrow">Search appearance</span><div className="interactive-admin-form-grid"><div><Label htmlFor={`${kind}-seo-title`}>SEO title</Label><Input id={`${kind}-seo-title`} value={form.seoTitle} onChange={event=>patch('seoTitle',event.currentTarget.value)} placeholder="Defaults to title"/></div><div className="interactive-admin-form-span"><Label htmlFor={`${kind}-seo-description`}>Meta description</Label><Input id={`${kind}-seo-description`} value={form.seoDescription} onChange={event=>patch('seoDescription',event.currentTarget.value)} placeholder={kind==='blog'?'Defaults to excerpt':'Optional search description'}/></div></div><label className="interactive-admin-check"><input type="checkbox" checked={form.noindex} onChange={event=>patch('noindex',event.currentTarget.checked)}/> Noindex this page</label></section></section><section className="interactive-admin-template-editor interactive-admin-cms-block-editor"><div className="interactive-admin-template-editor-head"><div><span className="interactive-eyebrow">Interactive body</span><h2>Content builder</h2><p>Use / to add media, runnable code, terminals, exercises, checkpoints, solutions, tabs and more.</p></div></div><InteractiveContentEditor value={form.document} onChange={document=>patch('document',document)} tools={tools} instanceKey={`${kind}-${id||'new'}`}/></section><div className="interactive-admin-publish-row"><div><strong className={`interactive-admin-save-state is-${saveState}`}>{editing?saveCopy:'New structured content'}</strong>{editing?<span className="interactive-muted"> · Edit version {form.editVersion}</span>:null}</div><div className="interactive-inline-form"><Button type="button" variant="outline" onClick={()=>void openPreview()}>Preview</Button><Button type="submit" disabled={saveState==='saving'||!form.title.trim()}>{editing?'Save now':'Create'}</Button></div></div></form><Dialog open={previewOpen} onOpenChange={setPreviewOpen}><DialogContent className="max-h-[92vh] overflow-y-auto sm:max-w-5xl"><DialogHeader><DialogTitle>{form.title||'Untitled'} — Draft preview</DialogTitle><DialogDescription>This uses the real interactive renderer and admin-only draft runtime. Publishing is not required.</DialogDescription></DialogHeader>{kind==='blog'&&form.coverImageUrl?<img className="interactive-blog-article-cover" src={form.coverImageUrl} alt={form.coverImageAlt}/>:null}{preview?<CmsInteractiveContent document={preview} kind={kind} slug={form.slug||'preview'} authenticated onAuthRequired={()=>{}} runtime={previewRuntime} headingIds={kind==='docs'}/>:null}</DialogContent></Dialog><Dialog open={historyOpen} onOpenChange={setHistoryOpen}><DialogContent className="sm:max-w-2xl"><DialogHeader><DialogTitle>Revision history</DialogTitle><DialogDescription>Restore a manual checkpoint. Autosave protects current edits between checkpoints.</DialogDescription></DialogHeader>{revisions.length?<div className="interactive-admin-cms-history">{revisions.map(revision=><div key={revision.id}><div><strong>{revision.title}</strong><span className="interactive-muted">{safeDate(revision.created_at)} · version {revision.editVersion||revision.edit_version||'—'}</span></div><Button type="button" size="sm" variant="outline" onClick={()=>void restoreRevision(revision)}>Restore</Button></div>)}</div>:<p className="interactive-muted">No checkpoints yet.</p>}</DialogContent></Dialog><Dialog open={mediaOpen} onOpenChange={setMediaOpen}><DialogContent className="sm:max-w-4xl"><DialogHeader><DialogTitle>CMS media library</DialogTitle><DialogDescription>Reuse uploaded assets from Editor.js. Delete is allowed only when an asset is not referenced by content.</DialogDescription></DialogHeader>{media.length?<div className="interactive-admin-cms-media-library">{media.map(asset=><div key={asset.id} className="interactive-admin-cms-media-asset">{(asset.mediaKind||asset.media_kind)==='image'?<img src={asset.url} alt=""/>:<FileText className="size-6"/>}<div><strong>{asset.originalName||asset.original_name||asset.url}</strong><small>{asset.mediaKind||asset.media_kind||'file'}</small></div><Button type="button" size="sm" variant="outline" onClick={()=>void removeMedia(asset)}>Delete if unused</Button></div>)}</div>:<p className="interactive-muted">No CMS media uploaded yet.</p>}</DialogContent></Dialog></>;
}
