<?php
/* admin_noticias_lite.php — Editor visual compacto (PROD + VIDEO local/URL)
   - Página auto-extensible (altura ajusta al contenido).
   - “Nuevo” crea al toque e inserta en la lista.
   - Bloques: h1, p, img, link, quote, embed, video (NUEVO).
*/
@session_start();
if (file_exists(__DIR__.'/db.php')) { require __DIR__.'/db.php'; $pdo = pdo();
} else {
  // Fallback solo si no hay db.php
  define('DB_HOST','localhost'); define('DB_NAME','tu_base'); define('DB_USER','tu_usuario'); define('DB_PASS','tu_pass');
  try{
    $pdo = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=utf8mb4", DB_USER, DB_PASS, [
      PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC
    ]);
  }catch(Exception $e){ http_response_code(500); die("Error DB: ".$e->getMessage()); }
}
$pdo->exec("CREATE TABLE IF NOT EXISTS noticias(
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  titulo VARCHAR(255) NOT NULL,
  estado ENUM('borrador','publicado') NOT NULL DEFAULT 'borrador',
  portada_url VARCHAR(500) NULL,
  contenido_json LONGTEXT NOT NULL,
  created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL,
  KEY(estado), KEY(created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");

function out($d,$c=200){ http_response_code($c); header('Content-Type:application/json;charset=utf-8'); echo json_encode($d,JSON_UNESCAPED_UNICODE); exit; }
function updir(){ $d=__DIR__.'/uploads'; if(!is_dir($d)) @mkdir($d,0775,true); if(!is_dir($d)) throw new Exception('uploads'); return $d; }
$act=$_GET['action']??null;

/* === LIST === */
if($act==='list'){
  $rows = $pdo->query("SELECT id,titulo,estado,portada_url,updated_at,contenido_json
                       FROM noticias
                       ORDER BY updated_at DESC
                       LIMIT 200")->fetchAll();
  foreach ($rows as &$r) {
    $t = trim($r['titulo'] ?? '');
    if ($t === '' || $t === 'Sin título') {
      $arr = [];
      try { $arr = json_decode($r['contenido_json'] ?? '[]', true) ?: []; } catch(Throwable $e){ $arr=[]; }
      if (is_array($arr)) {
        foreach ($arr as $b) {
          if (($b['type']??'')==='h1') {
            $txt = strip_tags((string)($b['content']??''));
            $txt = preg_replace('/\xC2\xA0|&nbsp;/', ' ', $txt);
            $txt = trim(preg_replace('/\s+/u',' ', $txt));
            if ($txt!==''){ $t=$txt; break; }
          }
        }
      }
      if ($t==='') $t='Sin título';
      $r['titulo'] = $t;
    }
    unset($r['contenido_json']);
  }
  out(['ok'=>1,'items'=>$rows]);
}

if($act==='get'){ $id=(int)($_GET['id']??0); $s=$pdo->prepare("SELECT * FROM noticias WHERE id=?"); $s->execute([$id]); $r=$s->fetch(); if(!$r) out(['ok'=>0,'msg'=>'404'],404); out(['ok'=>1,'item'=>$r]); }
if($act==='delete'){ $id=(int)($_POST['id']??0); $s=$pdo->prepare("DELETE FROM noticias WHERE id=? LIMIT 1"); $s->execute([$id]); out(['ok'=>1]); }

if($act==='save'){
  $p=json_decode(file_get_contents('php://input'),true); if(!$p) out(['ok'=>0,'msg'=>'json'],400);
  $id=(int)($p['id']??0);
  $t=trim((string)($p['titulo']??'')); if($t==='') $t='Sin título';
  $e=in_array(($p['estado']??'borrador'),['borrador','publicado'])?$p['estado']:'borrador';
  $port=trim((string)($p['portada_url']??''));
  $contenido = is_array($p['contenido']??null) ? $p['contenido'] : [];

  // Portada desde 1ª IMG si falta
  if($port===''){
    foreach($contenido as $b){
      if(($b['type']??'')==='img' && !empty($b['src'])){ $port=trim($b['src']); break; }
    }
  }

  $json=json_encode($contenido,JSON_UNESCAPED_UNICODE);
  if($id){
    $s=$pdo->prepare("UPDATE noticias SET titulo=?,estado=?,portada_url=?,contenido_json=?,updated_at=NOW() WHERE id=?");
    $s->execute([$t,$e,$port?:null,$json,$id]);
  } else {
    $s=$pdo->prepare("INSERT INTO noticias(titulo,estado,portada_url,contenido_json,created_at,updated_at)VALUES(?,?,?,?,NOW(),NOW())");
    $s->execute([$t,$e,$port?:null,$json]); $id=$pdo->lastInsertId();
  }
  out(['ok'=>1,'id'=>(int)$id,'titulo'=>$t,'portada_url'=>$port]);
}

/* === Upload imágenes y videos === */
if($act==='upload'){
  try{
    updir(); if(empty($_FILES['file'])) out(['ok'=>0,'msg'=>'file'],400);
    $f=$_FILES['file']; if($f['error']!==UPLOAD_ERR_OK) out(['ok'=>0,'msg'=>'err'],400);

    // Tamaño orientativo (previene locuras si php.ini está bien)
    if(!empty($f['size']) && $f['size'] > 1024*1024*500){ // 500 MB
      out(['ok'=>0,'msg'=>'too_large'],400);
    }

    $fi=finfo_open(FILEINFO_MIME_TYPE); $m=finfo_file($fi,$f['tmp_name']); finfo_close($fi);

    $imgMap=['image/jpeg'=>'jpg','image/png'=>'png','image/webp'=>'webp','image/gif'=>'gif'];
    $vidMap=['video/mp4'=>'mp4','video/webm'=>'webm','video/ogg'=>'ogg'];
    $ext = null; $prefix = null;

    if(isset($imgMap[$m])){ $ext=$imgMap[$m]; $prefix='img_'; }
    elseif(isset($vidMap[$m])){ $ext=$vidMap[$m]; $prefix='vid_'; }
    else out(['ok'=>0,'msg'=>'type'],400);

    $name=$prefix.date('Ymd_His').'_'.bin2hex(random_bytes(3)).'.'.$ext;
    $dest=__DIR__.'/uploads/'.$name; if(!move_uploaded_file($f['tmp_name'],$dest)) out(['ok'=>0,'msg'=>'move'],500);

    $base = rtrim(dirname($_SERVER['SCRIPT_NAME']),'/');
    $url = $base.'/uploads/'.$name;
    out(['ok'=>1,'url'=>$url,'mime'=>$m]);
  }catch(Exception $e){ out(['ok'=>0,'msg'=>$e->getMessage()],500); }
}

/* La galería de portada lista solo IMÁGENES (intencional) */
if($act==='list_uploads'){
  $d=updir(); $fs=@scandir($d)?:[]; $arr=[];
  foreach($fs as $fn){
    if($fn[0]==='.') continue;
    if(!preg_match('/\.(jpe?g|png|gif|webp)$/i',$fn)) continue;
    $arr[]=rtrim(dirname($_SERVER['SCRIPT_NAME']),'/').'/uploads/'.$fn;
  }
  rsort($arr); out(['ok'=>1,'items'=>$arr]);
}
?>
<!doctype html><html lang="es"><head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
  <link rel="icon" href="img/favicon.png" type="image/png">

<title>Admin Noticias — Lite</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<style>
:root{--bg:#0b1218;--panel:#0f1821;--line:#1b2a3a;--txt:#eaf2fb;--muted:#a8bed6;--accent:#2d8cff;--radius:14px}
*{box-sizing:border-box}body{margin:0;background:#0b1218;color:var(--txt);font-family:Poppins,system-ui}
.top{position:sticky;top:0;display:flex;gap:8px;align-items:center;padding:10px 12px;background:#0b1218d0;border-bottom:1px solid var(--line);backdrop-filter:blur(6px);z-index:10}
.input,select,textarea{background:#101a25;border:1px solid var(--line);color:var(--txt);padding:8px 10px;border-radius:10px}
.btn{background:#162536;border:1px solid #25415f;color:var(--txt);padding:8px 10px;border-radius:10px;cursor:pointer}
.btn.primary{background:linear-gradient(135deg,#2d8cff,#1aa3ff)}
.grid{display:grid;grid-template-columns:280px 1fr 320px;gap:12px;padding:12px}
.panel{background:linear-gradient(180deg,#0f1821,#0e1922);border:1px solid var(--line);border-radius:var(--radius);padding:10px;overflow:auto}
h3{font-size:13px;margin:4px 0 8px;letter-spacing:.1em;color:#9db7d4;text-transform:uppercase}
.block-btn{width:100%;text-align:left;margin-bottom:8px}
.list{display:flex;flex-direction:column;gap:8px}.item{background:#101a25;border:1px solid var(--line);border-radius:10px;padding:8px;display:flex;align-items:center;gap:8px}
.badge{font-size:11px;padding:2px 6px;border-radius:999px;background:#162536;border:1px solid #25415f}
.canvasbar{display:flex;gap:8px;align-items:center;border-bottom:1px dashed var(--line);padding:8px}
.canvas{display:flex;align-items:flex-start;justify-content:center;overflow:auto;height:calc(100vh - 116px);padding:10px}
.page{width:900px;min-height:800px;background:#fff;color:#111;border-radius:12px;border:1px solid #e8eef6;position:relative;overflow:hidden;padding:36px}
.page.mobile{width:420px;padding:24px}
.block{position:absolute;border:1px dashed transparent;border-radius:8px}
.block.selected{border-color:#2d8cff;box-shadow:0 0 0 3px #2d8cff33}
.block .inner[contenteditable=true]:focus{outline:2px solid #2d8cff55;border-radius:6px}
.block[data-type="h1"] .inner{font-size:46px;font-weight:800;line-height:1.1}
.block[data-type="p"] .inner{font-size:18px;line-height:1.6}
.block img, .block video, .block iframe{width:100%;height:100%;border-radius:8px;display:block}
.handle{position:absolute;right:-7px;bottom:-7px;width:14px;height:14px;background:#2d8cff;border:2px solid #fff;border-radius:4px}
.drop{border:2px dashed #2d8cff;border-radius:10px;padding:8px;text-align:center;color:#9ec9ff;cursor:pointer}
.small{font-size:12px;color:var(--muted)}
.thumb{width:36px;height:28px;object-fit:cover;border-radius:6px;border:1px solid #28405a}
.modal{position:fixed;inset:0;background:#00000088;backdrop-filter:blur(4px);display:none;align-items:center;justify-content:center;z-index:50}
.modal .box{width:min(700px,96vw);background:#0f1821;border:1px solid var(--line);border-radius:14px;padding:12px}
.gal{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;max-height:280px;overflow:auto}
.gal img{width:100%;aspect-ratio:16/10;object-fit:cover;border-radius:8px;border:1px solid #24374f;cursor:pointer}
.guides{position:absolute;inset:0;pointer-events:none}
.guide-v,.guide-h{position:absolute;background:#2d8cff33}
.guide-v{width:2px;top:0;bottom:0}
.guide-h{height:2px;left:0;right:0}
.guides.hidden{display:none}
.btn.active{outline:2px solid #2d8cff66}
</style>
</head><body>
<div class="top">
  <strong>📰 Admin Noticias — Lite</strong>
  <input id="titulo" class="input" placeholder="Título (opcional)" style="min-width:200px">
  <select id="estado" class="input"><option value="borrador">Borrador</option><option value="publicado">Publicado</option></select>
  <button class="btn" id="undo" title="Ctrl+Z"><i class="bi bi-arrow-counterclockwise"></i></button>
  <button class="btn" id="redo" title="Ctrl+Y"><i class="bi bi-arrow-clockwise"></i></button>
  <button class="btn" id="nuevo" title="Nueva nota"><i class="bi bi-file-earmark-plus"></i></button>
  <button class="btn" id="refresh" title="Actualizar lista"><i class="bi bi-arrow-repeat"></i></button>
  <button class="btn primary" id="guardar"><i class="bi bi-cloud-check"></i> Guardar</button>
  <span class="small" id="status">—</span>
  <div style="margin-left:auto;display:flex;gap:6px">
    <button class="btn active" id="toggleGuides" title="Mostrar/ocultar guías"><i class="bi bi-bounding-box"></i></button>
    <button class="btn active" id="toggleSnap" title="Snap on/off"><i class="bi bi-magnet"></i></button>
    <button class="btn" id="desk"><i class="bi bi-display"></i></button>
    <button class="btn" id="mob"><i class="bi bi-phone"></i></button>
    <button class="btn" id="portadaBtn"><i class="bi bi-image"></i> Portada</button>
  </div>
</div>

<div class="grid">
  <div class="panel">
    <h3>Bloques</h3>
    <button class="btn block-btn" data-add="h1"><i class="bi bi-type-h1"></i> Título</button>
    <button class="btn block-btn" data-add="p"><i class="bi bi-card-text"></i> Párrafo</button>
    <button class="btn block-btn" data-add="img"><i class="bi bi-image"></i> Imagen</button>
    <button class="btn block-btn" data-add="link"><i class="bi bi-link-45deg"></i> Enlace</button>
    <button class="btn block-btn" data-add="quote"><i class="bi bi-chat-right-quote"></i> Cita</button>
    <button class="btn block-btn" data-add="embed"><i class="bi bi-code-slash"></i> Embed</button>
    <button class="btn block-btn" data-add="video"><i class="bi bi-camera-reels"></i> Video</button>
    <div class="small" style="margin-top:8px">Supr=Eliminar, Ctrl+Z/Y=Undo/Redo</div>
    <h3 style="margin-top:12px">Capas</h3><div id="layers" class="list"></div>
    <h3 style="margin-top:12px">Notas</h3><div id="notas" class="list"></div>
  </div>

  <div class="panel">
    <div class="canvasbar">
      <div class="drop" id="drop">Arrastrá una <u>imagen o video</u>, o click para subir</div>
      <input type="file" id="file" accept="image/*,video/mp4,video/webm,video/ogg" style="display:none">
      <span class="small" id="doc">ID: —</span>
    </div>
    <div class="canvas">
      <div id="page" class="page">
        <div id="guides" class="guides"></div>
      </div>
    </div>
  </div>

  <div class="panel">
    <h3>Portada (solo listados)</h3>
    <div style="display:flex;align-items:center;gap:6px">
      <img id="portadaPrev" class="thumb" style="display:none">
      <input id="portada" class="input" placeholder="URL de portada (opcional)">
    </div>
    <h3 style="margin-top:12px">Bloque</h3>
    <div style="display:grid;grid-template-columns:1fr 1fr;gap:6px">
      <input id="btype" class="input" disabled placeholder="—" style="grid-column:1/3">
      <input id="bx" class="input" type="number" placeholder="Left (x)">
      <input id="by" class="input" type="number" placeholder="Top (y)">
      <input id="bw" class="input" type="number" placeholder="Width">
      <input id="bh" class="input" type="number" placeholder="Height">
      <input id="bfont" class="input" type="number" placeholder="Tamaño">
      <input id="bcolor" class="input" placeholder="Color #222">
      <select id="balign" class="input"><option value="">—</option><option>left</option><option>center</option><option>right</option><option>justify</option></select>
      <input id="bhref" class="input" placeholder="Link URL" style="grid-column:1/3">
      <input id="bsrc" class="input" placeholder="SRC (URL YouTube/Vimeo/.mp4)" style="grid-column:1/3">
      <!-- Subida específica de VIDEO (cuando el bloque seleccionado es video) -->
      <div style="grid-column:1/3;display:flex;gap:6px;align-items:center">
        <button class="btn" id="bUploadVideo"><i class="bi bi-upload"></i> Subir video</button>
        <input type="file" id="bFileVideo" accept="video/mp4,video/webm,video/ogg" style="display:none">
        <span class="small">o pegá la URL en SRC</span>
      </div>
      <input id="bz" class="input" type="number" placeholder="z-index" style="grid-column:1/3">
      <textarea id="bembed" class="input" placeholder="HTML embed" style="grid-column:1/3;min-height:90px"></textarea>
      <button class="btn" id="del" style="grid-column:1/3;background:#2a1212;border-color:#6b2a2a;color:#ffbaba"><i class="bi bi-trash3"></i> Eliminar bloque</button>
    </div>
  </div>
</div>

<!-- Modal Portada -->
<div id="portadaModal" class="modal"><div class="box">
  <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px"><strong>Portada (para listados, opcional)</strong><button class="btn" id="closePort"><i class="bi bi-x-lg"></i></button></div>
  <div style="display:flex;gap:8px;align-items:center;margin-bottom:8px">
    <button class="btn" id="subirPort"><i class="bi bi-upload"></i> Subir</button><input type="file" id="filePort" accept="image/*" style="display:none">
    <input id="urlPort" class="input" placeholder="o pegá URL" style="flex:1">
    <button class="btn primary" id="usarPort"><i class="bi bi-check2-circle"></i> Usar</button>
  </div>
  <div class="gal" id="gal"></div>
</div></div>

<script src="https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.2/Sortable.min.js"></script>
<script>
let S={id:0,titulo:'',estado:'borrador',portada_url:'',blocks:[]}, sel=null, timer=null;
let hist=[], redo=[]; let lastKey='';
const $=q=>document.querySelector(q), P=$('#page'), L=$('#layers'), N=$('#notas'), ST=$('#status'), DOC=$('#doc');

/* ====== Layout / Snap / Guías ====== */
let SNAP_ON = true; let GUIDES_ON = true;
const GRID = 8, PAGE_PAD = 36, COL_W = 720, GAP_Y = 16;

function clamp(n,min,max){ return Math.max(min, Math.min(max, n)); }
function pageRect(){ return { w: P.clientWidth, h: P.clientHeight, left: PAGE_PAD, top: PAGE_PAD, right: P.clientWidth-PAGE_PAD }; }

/* --- altura auto --- */
function pageContentBottom(){
  let max = PAGE_PAD;
  (S.blocks||[]).forEach(b=>{
    const baseH = (b.h || (b.type==='h1'?80 : b.type==='p'?120 : (b.type==='video'?300:240)));
    const y = (b.y||0) + baseH;
    if (y > max) max = y;
  });
  return max + PAGE_PAD;
}
function updatePageHeight(){
  const minH = 800;
  const h = Math.max(minH, pageContentBottom());
  P.style.height = h + 'px';
}

function drawGuides(){
  const G = $('#guides'); if(!G) return;
  G.classList.toggle('hidden', !GUIDES_ON);
  if(!GUIDES_ON){ G.innerHTML=''; return; }
  const r = pageRect();
  G.innerHTML = '';
  const gvL = document.createElement('div'); gvL.className='guide-v'; gvL.style.left = r.left+'px';
  const gvR = document.createElement('div'); gvR.className='guide-v'; gvR.style.left = r.right+'px';
  G.appendChild(gvL); G.appendChild(gvR);
  const colLeft = Math.round((P.clientWidth - COL_W)/2);
  const gvC1 = document.createElement('div'); gvC1.className='guide-v'; gvC1.style.left = colLeft+'px'; gvC1.style.background='#5fb3ff33';
  const gvC2 = document.createElement('div'); gvC2.className='guide-v'; gvC2.style.left = (colLeft+COL_W)+'px'; gvC2.style.background='#5fb3ff33';
  G.appendChild(gvC1); G.appendChild(gvC2);
}
function snapX(x,w){
  if(!SNAP_ON) return Math.round(x/GRID)*GRID;
  const r = pageRect();
  const colLeft = Math.round((P.clientWidth - COL_W)/2);
  const targets = [r.left, r.right - w, colLeft, colLeft + (COL_W - w)];
  let best=x, bestd=1e9;
  targets.forEach(t=>{ const d=Math.abs(x-t); if(d<bestd && d<=6) {best=t; bestd=d;} });
  if(bestd===1e9) best = Math.round(x/GRID)*GRID;
  return clamp(best, r.left, r.right - w);
}
function snapY(y){
  if(!SNAP_ON) return Math.round(y/GRID)*GRID;
  const gridY = Math.round((y-PAGE_PAD)/GAP_Y)*GAP_Y + PAGE_PAD;
  if(Math.abs(gridY - y) <= 6) y = gridY; else y = Math.round(y/GRID)*GRID;
  return y;
}

/* ========== Helpers ========= */
function clone(o){return JSON.parse(JSON.stringify(o))}
function keyOf(s){return JSON.stringify({t:s.titulo,e:s.estado,p:s.portada_url,b:s.blocks})}
function pushHist(){const k=keyOf(S); if(k===lastKey) return; hist.push(clone(S)); if(hist.length>120) hist.shift(); redo=[]; lastKey=k}
function undo(){if(!hist.length) return; redo.push(clone(S)); S=clone(hist.pop()); sel=null; render(); fill(); ST.textContent='Deshecho'}
function redoDo(){if(!redo.length) return; hist.push(clone(S)); S=clone(redo.pop()); sel=null; render(); fill(); ST.textContent='Rehecho'}
document.addEventListener('keydown',e=>{const a=document.activeElement,typ=a&&(a.isContentEditable||['INPUT','TEXTAREA','SELECT'].includes(a.tagName));const c=e.ctrlKey||e.metaKey;
  if(c&&e.key.toLowerCase()==='z'){e.preventDefault(); e.shiftKey?redoDo():undo();}
  if(c&&e.key.toLowerCase()==='y'){e.preventDefault(); redoDo();}
  if(!typ&&(e.key==='Delete'||e.key==='Backspace')){if(sel){delSel(); e.preventDefault();}}
});
function dirty(){ST.textContent='Guardando…'; if(timer) clearTimeout(timer); timer=setTimeout(()=>save(1),900)}
function view(m){m==='mobile'?P.classList.add('mobile'):P.classList.remove('mobile'); drawGuides(); updatePageHeight(); }
function uid(){return 'b_'+Math.random().toString(36).slice(2,9)}

/* ====== Bloques ====== */
function addBlock(t,init={}){
  const isImg = t==='img';
  const b={id:uid(),type:t,x:40+S.blocks.length*6,y:40+S.blocks.length*6,w:isImg?420:COL_W,h:t==='h1'?80:(t==='p'?140:(t==='link'?60: (t==='video'?300:240))),z:S.blocks.length,styles:{}};
  if(t==='h1') b.styles.fontSize=46;
  if(t==='p') b.styles.fontSize=18;
  if(t==='link') b.href = init.href||'';
  if(t==='img') b.src = init.src||'';
  if(t==='embed') b.embedHtml = init.embedHtml||'';
  if(t==='video') b.src = init.src||'';

  Object.assign(b,init);
  const colLeft = Math.round((P.clientWidth - COL_W)/2);
  if(!isImg){ b.x = colLeft; b.w = COL_W; }
  S.blocks.push(b); sel=b.id; render(); pushHist(); dirty();
}

document.querySelectorAll('[data-add]').forEach(b=>b.onclick=()=>addBlock(b.dataset.add));

function fitTextHeight(B, n, inner){
  const pad = 12;
  inner.style.minHeight='0px';
  const h = Math.max(40, Math.ceil(inner.scrollHeight + pad));
  n.style.height = h+'px';
  B.h = h;
  updatePageHeight();
}

function isYouTube(u){ try{ const x=new URL(u); return x.hostname.includes('youtube.com')||x.hostname==='youtu.be'; }catch(e){return false;} }
function ytId(u){ try{ const x=new URL(u); if(x.hostname==='youtu.be') return x.pathname.slice(1); if(x.hostname.includes('youtube.com')) return x.searchParams.get('v')||''; }catch(e){} return ''; }
function isVimeo(u){ try{ const x=new URL(u); return x.hostname.includes('vimeo.com'); }catch(e){return false;} }
function vimeoId(u){ try{ const x=new URL(u); const p=x.pathname.split('/').filter(Boolean); return p[0]||''; }catch(e){return '';} }

function render(){
  P.innerHTML='';
  const G = document.createElement('div'); G.id='guides'; G.className='guides'; P.appendChild(G);
  drawGuides();

  [...S.blocks].sort((a,b)=>(a.z||0)-(b.z||0)).forEach(B=>{
    const n=document.createElement('div'); n.className='block'; n.dataset.id=B.id; n.dataset.type=B.type;
    const baseH = (B.h||(B.type==='h1'?80:B.type==='p'?120:(B.type==='video'?300:200)));
    Object.assign(n.style,{left:(B.x||40)+'px',top:(B.y||40)+'px',width:(B.w||300)+'px',height:baseH+'px',zIndex:(B.z||0)+10});
    let inner;

    if(B.type==='img'){
      inner=document.createElement('img'); inner.src=B.src||''; n.appendChild(inner);

    } else if(B.type==='video'){
      const u = (B.src||'').trim();
      if(u && isYouTube(u)){
        const id = ytId(u);
        inner=document.createElement('iframe');
        inner.src='https://www.youtube.com/embed/'+encodeURIComponent(id);
        inner.allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share';
        inner.allowFullscreen=true; inner.frameBorder='0';
        n.appendChild(inner);
      } else if(u && isVimeo(u)){
        const id = vimeoId(u);
        inner=document.createElement('iframe');
        inner.src='https://player.vimeo.com/video/'+encodeURIComponent(id);
        inner.allow='autoplay; fullscreen; picture-in-picture'; inner.allowFullscreen=true; inner.frameBorder='0';
        n.appendChild(inner);
      } else {
        inner=document.createElement('video'); inner.controls=true; if(u) inner.src=u;
        n.appendChild(inner);
      }

    } else if(B.type==='embed'){
      inner=document.createElement('div'); inner.className='inner'; inner.style.cssText='width:100%;height:100%'; inner.innerHTML=B.embedHtml||'<div style="width:100%;height:100%;display:grid;place-items:center;border:1px dashed #ccc;color:#666">Pegar HTML</div>'; n.appendChild(inner);

    } else {
      inner=document.createElement('div'); inner.className='inner'; inner.contentEditable='true';
      inner.innerHTML=B.content|| (B.type==='h1'?'Título': B.type==='p'?'Escribí tu párrafo…': B.type==='quote'?'“Tu cita…”':'Texto del enlace');
      if(B.styles){ if(B.styles.fontSize) inner.style.fontSize=B.styles.fontSize+'px'; if(B.styles.color) inner.style.color=B.styles.color; if(B.styles.textAlign) inner.style.textAlign=B.styles.textAlign; }
      if(B.type==='link'){ inner.style.textDecoration='underline'; inner.style.color=B.styles?.color||'#1f5bbf'; }
      inner.oninput=()=>{ B.content=inner.innerHTML; pushHist(); dirty(); refreshLayers(); fitTextHeight(B,n,inner); };
      n.appendChild(inner);
      fitTextHeight(B,n,inner);
    }

    n.onmousedown=()=>select(B.id);
    const h=document.createElement('div'); h.className='handle'; n.appendChild(h);
    P.appendChild(n);

    interact(n).draggable({
      listeners:{ move(ev){
        let x=(parseFloat(n.style.left)||0)+ev.dx, y=(parseFloat(n.style.top)||0)+ev.dy;
        x = snapX(x, B.w||300);
        y = snapY(y);
        n.style.left=x+'px'; n.style.top=y+'px';
        B.x=Math.round(x); B.y=Math.round(y); updateIfSel(B); pushHist(); dirty(); updatePageHeight();
      }}
    }).resizable({ edges:{left:false,right:true,bottom:true,top:false} })
    .on('resizemove',ev=>{
      let w=Math.max(80,Math.round(ev.rect.width)), h=Math.max(40,Math.round(ev.rect.height));
      if(SNAP_ON){
        const x = parseFloat(n.style.left)||0;
        let best = Math.abs(w - COL_W) <= 10 ? COL_W : Math.round(w/GRID)*GRID;
        w = clamp(best, 80, P.clientWidth - x - PAGE_PAD);
      } else { w = Math.round(w/GRID)*GRID; }
      n.style.width=w+'px'; B.w=w;
      if(B.type!=='img' && B.type!=='video' && inner){ fitTextHeight(B,n,inner); } else { n.style.height=h+'px'; B.h=h; updatePageHeight(); }
      updateIfSel(B); pushHist(); dirty();
    });
  });

  updateSelFx(); refreshLayers();
  updatePageHeight();
}

function refreshLayers(){
  L.innerHTML=''; [...S.blocks].sort((a,b)=>(a.z||0)-(b.z||0)).forEach(B=>{
    const icon = B.type==='img'?'bi-image': B.type==='h1'?'bi-type-h1': B.type==='p'?'bi-card-text': B.type==='quote'?'bi-chat-right-quote': B.type==='link'?'bi-link-45deg': B.type==='video'?'bi-camera-reels':'bi-play-btn';
    const it=document.createElement('div'); it.className='item'; it.dataset.id=B.id;
    it.innerHTML=`<i class="bi ${icon}"></i>
      <span>${B.type.toUpperCase()}</span><span class="badge" style="margin-left:auto">z:${B.z||0}</span>`;
    it.onclick=()=>select(B.id); L.appendChild(it);
  });
  Sortable.create(L,{animation:120,onEnd:()=>{Array.from(L.children).forEach((n,i)=>{const id=n.dataset.id;const b=S.blocks.find(k=>k.id===id); if(b) b.z=i;}); pushHist(); dirty(); render();}});
}

function select(id){ sel=id; updateSelFx(); fill(); }
function getSel(){ return S.blocks.find(b=>b.id===sel)||null }
function updateSelFx(){ P.querySelectorAll('.block').forEach(n=>n.classList.toggle('selected', n.dataset.id===sel)); }
function fill(){
  const b=getSel(); $('#btype').value=b?b.type.toUpperCase():'—';
  $('#bx').value=b?b.x||0:''; $('#by').value=b?b.y||0:''; $('#bw').value=b?b.w||300:''; $('#bh').value=b?b.h||160:'';
  $('#bfont').value=b&&b.styles?.fontSize?b.styles.fontSize:''; $('#bcolor').value=b&&b.styles?.color?b.styles.color:''; $('#balign').value=b&&b.styles?.textAlign?b.styles.textAlign:'';
  $('#bhref').value=b&&b.href?b.href:''; $('#bz').value=b&&b.z!==undefined?b.z:''; $('#bembed').value=b&&b.embedHtml?b.embedHtml:''; 
  $('#bsrc').value=b&&b.type==='video'&&b.src?b.src:''; // SRC solo para video
}

['bx','by','bw','bh','bfont','bcolor','balign','bhref','bz','bembed','bsrc'].forEach(id=>{
  const el = document.getElementById(id);
  el && (el.oninput = ()=>{
    const b=getSel(); if(!b) return;
    if(id==='bx') b.x=parseInt(el.value||'0',10)||0;
    else if(id==='by') b.y=parseInt(el.value||'0',10)||0;
    else if(id==='bw') b.w=parseInt(el.value||'0',10)||300;
    else if(id==='bh') b.h=parseInt(el.value||'0',10)||160;
    else if(id==='bfont'){ b.styles=b.styles||{}; b.styles.fontSize=parseInt(el.value||'0',10)||undefined; }
    else if(id==='bcolor'){ b.styles=b.styles||{}; b.styles.color=el.value||undefined; }
    else if(id==='balign'){ b.styles=b.styles||{}; b.styles.textAlign=el.value||undefined; }
    else if(id==='bhref'){ b.href=el.value||''; }
    else if(id==='bz'){ b.z=parseInt(el.value||'0',10)||0; }
    else if(id==='bembed'){ b.embedHtml=el.value||''; }
    else if(id==='bsrc' && b.type==='video'){ b.src=el.value||''; }
    pushHist(); dirty(); render(); fill();
  });
});

function updateIfSel(b){ if(!b||b.id!==sel) return; $('#bx').value=b.x||0; $('#by').value=b.y||0; $('#bw').value=b.w||300; $('#bh').value=b.h||160; }

/* eliminar bloque */
function delSel(){ const b=getSel(); if(!b) return; S.blocks=S.blocks.filter(k=>k.id!==b.id); sel=null; render(); pushHist(); dirty(); }
$('#del').onclick=delSel;

/* portada (panel lateral) */
const prev=$('#portadaPrev'), port=$('#portada');
port.oninput=()=>{const v=port.value.trim(); S.portada_url=v; prev.src=v; prev.style.display=v?'block':'none'; pushHist(); dirty(); }
$('#portadaBtn').onclick=()=>{$('#portadaModal').style.display='flex'; loadGal();}
$('#closePort').onclick=()=>{$('#portadaModal').style.display='none'}
$('#subirPort').onclick=()=>$('#filePort').click();
$('#filePort').onchange=()=>{const f=$('#filePort').files?.[0]; if(f) up(f, url=>$('#urlPort').value=url);}
$('#usarPort').onclick=()=>{const u=$('#urlPort').value.trim(); if(!u){alert('Elegí una portada');return;} S.portada_url=u; port.value=u; prev.src=u; prev.style.display='block'; pushHist(); dirty(); $('#portadaModal').style.display='none';}
function loadGal(){ $('#gal').innerHTML='<div class="small">Cargando…</div>'; fetch('?action=list_uploads').then(r=>r.json()).then(j=>{
  if(!j.ok) throw new Error('uploads'); $('#gal').innerHTML=''; j.items.slice(0,16).forEach(u=>{const im=document.createElement('img'); im.src=u; im.onclick=()=>$('#urlPort').value=u; $('#gal').appendChild(im);});
}).catch(e=>$('#gal').innerHTML='<div class="small">Error</div>');}

/* uploads bloque (imagen o video) desde toolbar/drop */
$('#drop').onclick=()=>$('#file').click();
$('#file').onchange=()=>{const f=$('#file').files?.[0]; if(f) up(f, url=>addFromMime(f.type,url));}
$('#drop').ondragover=e=>{e.preventDefault(); $('#drop').style.background='#2d8cff22'}
$('#drop').ondragleave=e=>{e.preventDefault(); $('#drop').style.background='transparent'}
$('#drop').ondrop=e=>{e.preventDefault(); $('#drop').style.background='transparent'; const f=e.dataTransfer.files?.[0]; if(f) up(f, url=>addFromMime(f.type,url));}

function addFromMime(m,url){
  if(/^video\//i.test(m)) addBlock('video',{src:url,w:COL_W,h:300});
  else addBlock('img',{src:url});
}
function up(file,cb){
  const fd=new FormData(); fd.append('file',file);
  fetch('?action=upload',{method:'POST',body:fd}).then(r=>r.json()).then(j=>{
    if(!j.ok) { alert('Error de subida'); return; }
    cb(j.url);
  }).catch(()=>alert('Upload error'));
}

/* Subida específica desde panel del BLOQUE VIDEO */
$('#bUploadVideo').onclick=()=>{
  const b=getSel(); if(!b || b.type!=='video'){ alert('Seleccioná un bloque de video'); return; }
  $('#bFileVideo').click();
};
$('#bFileVideo').onchange=()=>{
  const b=getSel(); if(!b || b.type!=='video') return;
  const f=$('#bFileVideo').files?.[0]; if(!f) return;
  up(f, url=>{ b.src=url; $('#bsrc').value=url; pushHist(); dirty(); render(); fill(); });
};

/* Pegado de URL: si es video, crea bloque Video al vuelo */
document.addEventListener('paste', (e)=>{
  const txt = (e.clipboardData || window.clipboardData)?.getData('text')?.trim();
  if(!txt) return;
  try{
    const u = new URL(txt);
    if(isYouTube(u.href) || isVimeo(u.href) || /\.(mp4|webm|ogg)(\?|#|$)/i.test(u.pathname)){
      addBlock('video',{src:u.href,w:COL_W,h:300});
      e.preventDefault();
    }
  }catch(_){}
});

/* === Autocompletar antes de guardar === */
function textFromHtml(html){ const d=document.createElement('div'); d.innerHTML=html||''; return d.textContent.replace(/\u00a0/g,' ').replace(/\s+/g,' ').trim(); }
function firstBlock(type){ return (S.blocks||[]).find(b=>b.type===type); }
function inferTitleAndCover(){
  if(!S.titulo || !S.titulo.trim()){
    const h=firstBlock('h1'); const t=h?textFromHtml(h.content||''):'';
    if(t){ S.titulo=t; $('#titulo').value=t; }
  }
  if(!S.portada_url || !S.portada_url.trim()){
    const im=firstBlock('img'); if(im && im.src){ S.portada_url=im.src.trim(); port.value=S.portada_url; prev.src=S.portada_url; prev.style.display='block'; }
  }
}

/* guardar/cargar */
function save(silent){
  inferTitleAndCover();
  const body = {id:S.id||0,titulo:(S.titulo||'').trim()||'Sin título',estado:S.estado||'borrador',portada_url:S.portada_url||'',contenido:S.blocks};
  fetch('?action=save',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)})
    .then(r=>r.json()).then(j=>{ if(!j.ok) throw 0; S.id=j.id; if(j.titulo) S.titulo=j.titulo; if(j.portada_url!==undefined) S.portada_url=j.portada_url||''; DOC.textContent='ID: '+S.id; ST.textContent=(silent?'Auto-guardado':'Guardado')+' ✓'; loadList();})
    .catch(()=>ST.textContent='Error');
}
$('#guardar').onclick=()=>save(0);

function openDoc(id){
  fetch('?action=get&id='+id).then(r=>r.json()).then(j=>{
    if(!j.ok) throw 0; const it=j.item;
    S={id:it.id,titulo:it.titulo,estado:it.estado,portada_url:it.portada_url||'',blocks:[]};
    try{S.blocks=JSON.parse(it.contenido_json)||[]}catch(e){S.blocks=[]}
    if(!S.titulo || S.titulo==='Sin título'){
      const h1 = (S.blocks||[]).find(b=>b.type==='h1');
      if(h1){
        const d=document.createElement('div'); d.innerHTML=h1.content||'';
        const t=(d.textContent||'').replace(/\u00a0/g,' ').replace(/\s+/g,' ').trim();
        if(t) S.titulo=t;
      }
    }
    $('#titulo').value=S.titulo||''; $('#estado').value=S.estado; port.value=S.portada_url||'';
    if(S.portada_url){prev.src=S.portada_url; prev.style.display='block';} else prev.style.display='none';
    sel=null; render(); ST.textContent='—'; hist=[]; redo=[]; lastKey=''; pushHist();
  }).catch(()=>alert('Error'));
}

/* nuevo — crea y guarda al toque */
function nuevo(){
  S={id:0,titulo:'',estado:'borrador',portada_url:'',blocks:[]};
  $('#titulo').value=''; $('#estado').value='borrador'; port.value=''; prev.style.display='none';
  sel=null; render(); DOC.textContent='ID: —'; ST.textContent='—'; hist=[]; redo=[]; lastKey=''; pushHist();
}
$('#nuevo').onclick = () => { nuevo(); save(0); };

/* refresh manual del listado */
$('#refresh').onclick = () => { loadList(); ST.textContent='Actualizado'; };

/* head inputs */
$('#titulo').oninput=()=>{S.titulo=$('#titulo').value; pushHist(); dirty();}
$('#estado').onchange=()=>{S.estado=$('#estado').value; pushHist(); dirty();}

/* toggle guías / snap / vista */
$('#desk').onclick=()=>view('desktop'); $('#mob').onclick=()=>view('mobile');
$('#undo').onclick=undo; $('#redo').onclick=redoDo;
$('#toggleGuides').onclick=()=>{ GUIDES_ON = !GUIDES_ON; $('#toggleGuides').classList.toggle('active', GUIDES_ON); drawGuides(); }
$('#toggleSnap').onclick=()=>{ SNAP_ON = !SNAP_ON; $('#toggleSnap').classList.toggle('active', SNAP_ON); }

/* listado */
function loadList(){
  fetch('?action=list').then(r=>r.json()).then(j=>{
    if(!j.ok) throw 0; N.innerHTML=''; j.items.forEach(it=>{
      const d=document.createElement('div'); d.className='item';
      d.innerHTML=`<img class="thumb" src="${it.portada_url?it.portada_url:''}" onerror="this.style.display='none'"><div style="display:flex;flex-direction:column"><strong>${it.titulo||'Sin título'}</strong><span class="small">${it.estado} — ${it.updated_at}</span></div><button class="btn" data-open="${it.id}"><i class="bi bi-pencil"></i></button><button class="btn" data-del="${it.id}" style="background:#2a1212;border-color:#6b2a2a;color:#ffbaba"><i class="bi bi-trash3"></i></button>`;
      N.appendChild(d);
    });
    N.querySelectorAll('[data-open]').forEach(b=>b.onclick=()=>openDoc(parseInt(b.dataset.open)));
    N.querySelectorAll('[data-del]').forEach(b=>b.onclick=()=>{ if(!confirm('Eliminar?')) return; const fd=new FormData(); fd.append('id',b.dataset.del); fetch('?action=delete',{method:'POST',body:fd}).then(r=>r.json()).then(j=>{ if(!j.ok) throw 0; if(S.id==b.dataset.del) nuevo(); loadList();}).catch(()=>alert('Error')); });
  }).catch(()=>N.innerHTML='<div class="small">Error</div>');
}

/* init */
loadList(); nuevo(); drawGuides(); updatePageHeight();
window.addEventListener('resize', ()=>{ drawGuides(); updatePageHeight(); });
</script>
</body></html>
