<?php
declare(strict_types=1);
require __DIR__.'/bootstrap.php';
$id=preg_replace('/[^A-Za-z0-9_-]/','',(string)($_GET['book']??''));
$token=(string)($_GET['token']??'');
$secret=hash('sha256',(string)($config['db_pass']??'').':'.(string)($config['db_name']??'').':storybook-preview');
$valid=$id!=='' && strlen($token)===64 && hash_equals(hash_hmac('sha256',$id,$secret),$token);
$book=null;
if($valid){$st=$pdo->prepare('SELECT book_json FROM production_books WHERE id=?');$st->execute([$id]);$row=$st->fetch();if($row)$book=json_decode((string)$row['book_json'],true);}
if(!is_array($book)){http_response_code(404);$book=null;}
function asset_url(string $path): string {if($path===''||str_starts_with($path,'data:')||preg_match('~^https?://~i',$path))return $path;return ltrim($path,'/');}
$safeBook=$book?[
 'theme'=>(string)($book['theme']??'Personalized Story'),
 'name'=>(string)($book['name']??''),
 'age'=>(string)($book['age']??''),
 'language'=>(string)($book['language']??'en'),
 'coverImage'=>asset_url((string)($book['coverImage']??'')),
 'backImage'=>asset_url((string)($book['backImage']??'')),
 'pages'=>array_values(array_map(static function($p){return ['id'=>(int)($p['id']??0),'title'=>(string)($p['title']??''),'text'=>(string)($p['text']??''),'image'=>asset_url((string)($p['image']??'')),'layout'=>(string)($p['layout']??'bottom')];},array_filter($book['pages']??[],'is_array')))
]:null;
?><!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex,nofollow"><title><?=e($safeBook?($safeBook['name'].' — '.$safeBook['theme']):'Story not found')?></title><style>
*{box-sizing:border-box}html,body{margin:0;min-height:100%;background:#171512;color:#fff;font-family:Arial,sans-serif}button{font:inherit}.shell{min-height:100vh;display:grid;grid-template-rows:auto 1fr auto}.top{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 18px;background:#211e1a;border-bottom:1px solid #ffffff18}.brand{font:700 18px Georgia}.meta{color:#cabdaf;font-size:12px;text-align:right}.stage{display:grid;place-items:center;padding:18px;overflow:hidden}.page{position:relative;width:min(92vw,460px);aspect-ratio:148/210;overflow:hidden;border-radius:8px;background:radial-gradient(circle at 25% 18%,#e4b85b,#315f72 48%,#8b5269);box-shadow:0 24px 70px #0009}.page img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}.story{position:absolute;left:5%;right:5%;bottom:5%;padding:14px 16px;border-radius:8px;background:#fffdf7d9;color:#2e2925;box-shadow:0 8px 22px #19140f35;backdrop-filter:blur(2px)}.story.top{top:5%;bottom:auto;display:block;border:0}.story.left{top:50%;right:25%;bottom:auto;transform:translateY(-50%)}.story.right{top:50%;left:25%;bottom:auto;transform:translateY(-50%);text-align:right}.story.full{top:50%;bottom:auto;transform:translateY(-50%);text-align:center}.story h2{margin:0 0 7px;font:700 clamp(17px,3vw,23px) Georgia;color:#713e4d}.story p{margin:0;font-size:clamp(14px,2.4vw,18px);line-height:1.5}.cover-label{position:absolute;left:7%;right:7%;bottom:7%;padding:13px;border-radius:8px;background:#14110eb8;text-align:center}.cover-label h1{margin:0 0 5px;font:700 clamp(23px,5vw,36px) Georgia}.cover-label p{margin:0;color:#ead8c4}.empty{display:grid;place-items:center;height:100%;padding:30px;text-align:center;color:#eee}.controls{display:flex;align-items:center;justify-content:center;gap:14px;padding:14px;background:#211e1a;border-top:1px solid #ffffff18}.nav{border:1px solid #ffffff38;border-radius:9px;background:#fff;color:#211e1a;padding:10px 18px;font-weight:700;cursor:pointer}.nav:disabled{opacity:.35;cursor:not-allowed}.count{min-width:88px;text-align:center;color:#d7cabe;font-size:13px}@media(max-width:520px){.stage{padding:10px}.top{padding:10px 12px}.page{width:min(94vw,430px)}.story{padding:11px 13px}.controls{padding:10px;gap:9px}.nav{padding:9px 13px}}
</style></head><body><?php if(!$safeBook):?><main class="empty"><div><h1>Story preview not found</h1><p>This link may be incorrect or the book may no longer be available.</p></div></main><?php else:?><div class="shell"><header class="top"><div class="brand">Personalized StoryBook</div><div class="meta"><b><?=e($safeBook['name'])?></b><br><?=e($safeBook['theme'])?></div></header><main class="stage"><article class="page" id="page"></article></main><footer class="controls"><button class="nav" id="prev">← Previous</button><span class="count" id="count"></span><button class="nav" id="next">Next →</button></footer></div><script>
const book=<?=json_encode($safeBook,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOT)?>;
const slides=[{type:'cover',image:book.coverImage},...book.pages.map(p=>({type:'page',...p})),{type:'back',image:book.backImage}];let at=0;
const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
function render(){const s=slides[at],el=document.getElementById('page');let image=s.image?`<img src="${esc(s.image)}" alt="">`:'';if(s.type==='cover')el.innerHTML=image+`<div class="cover-label"><h1>${esc(book.theme)}</h1><p>${esc(book.name)}'s Adventure</p></div>`;else if(s.type==='back')el.innerHTML=image||'<div class="empty">The End</div>';else el.innerHTML=image+`<div class="story ${esc(s.layout)}"><h2>${esc(s.title)}</h2><p>${esc(s.text).replaceAll('{name}',esc(book.name)).replaceAll('{age}',esc(book.age))}</p></div>`;document.getElementById('count').textContent=at===0?'Cover':at===slides.length-1?'Back Cover':`Page ${at} of ${book.pages.length}`;document.getElementById('prev').disabled=at===0;document.getElementById('next').disabled=at===slides.length-1;}
document.getElementById('prev').onclick=()=>{if(at>0){at--;render()}};document.getElementById('next').onclick=()=>{if(at<slides.length-1){at++;render()}};document.addEventListener('keydown',e=>{if(e.key==='ArrowLeft')document.getElementById('prev').click();if(e.key==='ArrowRight')document.getElementById('next').click()});render();
</script><?php endif?></body></html>
