<?php
declare(strict_types=1);

function ef_blog_index(?string $categorySlug=null):void
{
    $page=max(1,(int)($_GET['page']??1));
    $per=max(6,min(30,(int)setting('blog_posts_per_page','18')));
    $where="c.type='blog_post' AND c.status='published' AND c.noindex=0";$params=[];$category=null;
    if($categorySlug!==null){$category=one("SELECT * FROM categories WHERE slug=? AND type='blog_category'",[$categorySlug]);if(!$category){ef_error(404);return;}$where.=' AND c.category_id=?';$params[]=(int)$category['id'];}
    $total=(int)(one("SELECT COUNT(*) n FROM content c WHERE $where",$params)['n']??0);
    $rows=all("SELECT c.*,cat.name category_name,cat.slug category_slug FROM content c LEFT JOIN categories cat ON cat.id=c.category_id WHERE $where ORDER BY c.featured DESC,c.published_at DESC,c.id DESC LIMIT $per OFFSET ".(($page-1)*$per),$params);
    $cats=all("SELECT cat.name,cat.slug,COUNT(c.id) total FROM categories cat JOIN content c ON c.category_id=cat.id AND c.type='blog_post' AND c.status='published' AND c.noindex=0 WHERE cat.type='blog_category' GROUP BY cat.id,cat.name,cat.slug ORDER BY cat.name");
    $title=$category?$category['name'].' Articles':'Exams.fit Study Blog';
    $description=$category?($category['description']?:'Helpful USA education articles about '.$category['name'].'.'):'Practical, reviewed study strategies, exam guidance and education explainers for learners in the United States.';
    $base=$category?'blog/category/'.$category['slug']:'blog';
    $body='<section class="blog-hero"><p class="eyebrow">EXAMS.FIT EDITORIAL</p><h1>'.e($title).'</h1><p class="lead">'.e($description).'</p><p><a href="'.url('blog/feed.xml').'">Subscribe to the blog RSS feed</a></p></section>'.ef_blog_category_nav($cats,$categorySlug).'<section aria-label="Blog articles">'.($rows?ef_blog_cards($rows):'<p class="empty">Approved blog articles will appear here after import and editorial review.</p>').ef_pagination($page,(int)ceil($total/$per),$base).'</section>';
    $items=[];foreach($rows as$i=>$r)$items[]=['@type'=>'ListItem','position'=>(($page-1)*$per)+$i+1,'url'=>url($r['slug']),'name'=>$r['title']];
    $schema=['@context'=>'https://schema.org','@graph'=>[['@type'=>'Blog','name'=>'Exams.fit Study Blog','url'=>url('blog'),'description'=>$description,'publisher'=>['@type'=>'Organization','name'=>setting('site_name','Exams.fit'),'url'=>url()]],['@type'=>'CollectionPage','name'=>$title,'url'=>url($base.($page>1?'?page='.$page:'')),'mainEntity'=>['@type'=>'ItemList','numberOfItems'=>$total,'itemListElement'=>$items]]]];
    $body.='<script type="application/ld+json">'.json_encode($schema,JSON_UNESCAPED_SLASHES|JSON_HEX_TAG).'</script>';
    ef_layout($body,['title'=>$title,'description'=>$description,'canonical'=>url($base.($page>1?'?page='.$page:''))]);
}

function ef_blog_archive(int $year,int $month):void
{
    if($year<2000||$year>(int)date('Y')+1||$month<1||$month>12){ef_error(404);return;}
    $from=sprintf('%04d-%02d-01 00:00:00',$year,$month);$to=date('Y-m-d H:i:s',strtotime($from.' +1 month'));
    $rows=all("SELECT c.*,cat.name category_name,cat.slug category_slug FROM content c LEFT JOIN categories cat ON cat.id=c.category_id WHERE c.type='blog_post' AND c.status='published' AND c.noindex=0 AND c.published_at>=? AND c.published_at<? ORDER BY c.published_at DESC",[$from,$to]);
    $title='Blog archive: '.date('F Y',strtotime($from));$body='<section><h1>'.e($title).'</h1>'.($rows?ef_blog_cards($rows):'<p class="empty">No published articles in this archive.</p>').'</section>';
    ef_layout($body,['title'=>$title,'description'=>'Exams.fit study blog archive for '.date('F Y',strtotime($from)).'.','canonical'=>url(sprintf('blog/archive/%04d/%02d',$year,$month)),'noindex'=>true]);
}

function ef_blog_category_nav(array $cats,?string $active):string
{
    if(!$cats)return'';$h='<nav class="blog-categories" aria-label="Blog topics"><a '.($active===null?'aria-current="page"':'').' href="'.url('blog').'">All topics</a>';
    foreach($cats as$cat)$h.='<a '.($active===$cat['slug']?'aria-current="page"':'').' href="'.url('blog/category/'.$cat['slug']).'">'.e($cat['name']).' <span>'.(int)$cat['total'].'</span></a>';
    return$h.'</nav>';
}

function ef_blog_cards(array $rows):string
{
    $h='<div class="blog-grid">';foreach($rows as$r){$d=json_decode($r['data']??'{}',true)?:[];$image=$d['featured_image']??'';$h.='<article class="blog-card">';if($image&&preg_match('~^uploads/media/[a-zA-Z0-9_.-]+\.(?:webp|avif|jpe?g|png)$~i',$image))$h.='<a class="blog-image" href="'.url($r['slug']).'"><img loading="lazy" decoding="async" src="'.url($image).'" alt="'.e($d['featured_image_alt']??$r['title']).'" width="720" height="405"></a>';$h.='<div class="blog-card-body"><p class="type">'.e($d['primary_topic']??$r['category_name']??'Study guidance').'</p><h2><a href="'.url($r['slug']).'">'.e($r['title']).'</a></h2><p>'.e($r['excerpt']).'</p><p class="blog-meta">'.e($r['author']?:'Exams.fit Editorial Team').' · '.e(substr($r['published_at']?:$r['updated_at'],0,10)).'</p></div></article>';}
    return$h.'</div>';
}

function ef_blog_extras(array $d):string
{
    $h='';if(!empty($d['key_takeaways'])&&is_array($d['key_takeaways'])){$h.='<aside class="takeaways"><h2>Key takeaways</h2><ul>';foreach(array_slice($d['key_takeaways'],0,12) as$x)if(trim((string)$x)!=='')$h.='<li>'.e((string)$x).'</li>';$h.='</ul></aside>';}
    if(!empty($d['faqs'])&&is_array($d['faqs'])){$h.='<section class="blog-faq"><h2>Frequently asked questions</h2>';foreach(array_slice($d['faqs'],0,12) as$faq){$q=trim((string)($faq['question']??''));$a=trim((string)($faq['answer']??''));if($q!==''&&$a!=='')$h.='<details><summary>'.e($q).'</summary><p>'.e($a).'</p></details>';}$h.='</section>';}
    if(!empty($d['references'])&&is_array($d['references'])){$list='';foreach(array_slice($d['references'],0,20) as$ref){$label=is_array($ref)?trim((string)($ref['label']??'')):'';$link=is_array($ref)?trim((string)($ref['url']??'')):'';if($label!==''&&filter_var($link,FILTER_VALIDATE_URL)&&parse_url($link,PHP_URL_SCHEME)==='https')$list.='<li><a rel="nofollow noopener" href="'.e($link).'">'.e($label).'</a></li>';}if($list)$h.='<section class="references"><h2>Sources and further reading</h2><ul>'.$list.'</ul></section>';}
    return$h;
}

function ef_blog_rss():void
{
    $rows=all("SELECT slug,title,excerpt,published_at,updated_at,author FROM content WHERE type='blog_post' AND status='published' AND noindex=0 ORDER BY published_at DESC,id DESC LIMIT 50");header('Content-Type:application/rss+xml; charset=utf-8');echo'<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>Exams.fit Study Blog</title><link>'.ef_xml(url('blog')).'</link><description>Reviewed USA study strategies and education guidance.</description><language>en-us</language><lastBuildDate>'.date(DATE_RSS).'</lastBuildDate>';foreach($rows as$r)echo'<item><title>'.ef_xml($r['title']).'</title><link>'.ef_xml(url($r['slug'])).'</link><guid isPermaLink="true">'.ef_xml(url($r['slug'])).'</guid><description>'.ef_xml($r['excerpt']).'</description><author>'.ef_xml($r['author']?:'Exams.fit Editorial Team').'</author><pubDate>'.date(DATE_RSS,strtotime($r['published_at']?:$r['updated_at'])).'</pubDate></item>';echo'</channel></rss>';
}

function ef_xml(?string $value):string{return htmlspecialchars($value??'',ENT_QUOTES|ENT_XML1,'UTF-8');}

function ef_sitemap_v31(?string $type):void
{
    header('Content-Type:application/xml; charset=utf-8');echo'<?xml version="1.0" encoding="UTF-8"?>';
    $groups=['static','blog','exams','practice','school','college','notes','courses','articles','news','videos','pdfs','tools','categories','images'];
    if($type===null){echo'<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';foreach($groups as$group){$count=ef_sitemap_count($group);if($count<1)continue;for($page=1;$page<=max(1,(int)ceil($count/50000));$page++){echo'<sitemap><loc>'.ef_xml(url('sitemap-'.$group.($page>1?'-'.$page:'').'.xml')).'</loc></sitemap>';}}echo'</sitemapindex>';return;}
    $page=1;if(preg_match('/^(.+)-(\d+)$/',$type,$m)){$type=$m[1];$page=max(1,(int)$m[2]);}if(!in_array($type,$groups,true)){http_response_code(404);echo'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"/>';return;}$offset=($page-1)*50000;
    if($type==='static'){$paths=['','school','college','exams','practice-tests','tools'];foreach(['about','editorial-policy','privacy','contact'] as$slug)if(one("SELECT id FROM content WHERE slug=? AND status='published' AND noindex=0",[$slug]))$paths[]=$slug;echo'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';foreach(array_slice($paths,$offset,50000) as$path)echo'<url><loc>'.ef_xml(url($path)).'</loc></url>';echo'</urlset>';return;}
    if($type==='blog'){ef_blog_sitemap_page($offset);return;}
    if($type==='tools'){ef_tools_sitemap_page($offset);return;}
    if($type==='categories'){$rows=all("SELECT cat.slug,cat.updated_at FROM categories cat WHERE cat.type NOT IN('blog_category','tool_category') AND EXISTS(SELECT 1 FROM content c WHERE c.category_id=cat.id AND c.status='published' AND c.noindex=0) ORDER BY cat.id LIMIT 50000 OFFSET $offset");echo'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';foreach($rows as$r)echo'<url><loc>'.ef_xml(url('category/'.$r['slug'])).'</loc><lastmod>'.ef_xml(substr($r['updated_at'],0,10)).'</lastmod></url>';echo'</urlset>';return;}
    if($type==='images'){$rows=all("SELECT DISTINCT c.slug,m.path,m.title FROM media m JOIN content c ON c.body LIKE CONCAT('%',m.path,'%') WHERE c.status='published' AND c.noindex=0 AND m.mime LIKE 'image/%' ORDER BY c.id LIMIT 50000 OFFSET $offset");echo'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">';foreach($rows as$r)echo'<url><loc>'.ef_xml(url($r['slug'])).'</loc><image:image><image:loc>'.ef_xml(url($r['path'])).'</image:loc><image:title>'.ef_xml($r['title']).'</image:title></image:image></url>';echo'</urlset>';return;}
    $types=ef_sitemap_group($type);$ph=implode(',',array_fill(0,count($types),'?'));$rows=all("SELECT slug,updated_at FROM content WHERE status='published' AND noindex=0 AND type IN($ph) ORDER BY id LIMIT 50000 OFFSET $offset",$types);echo'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';foreach($rows as$r)echo'<url><loc>'.ef_xml(url($r['slug'])).'</loc><lastmod>'.ef_xml(substr($r['updated_at'],0,10)).'</lastmod></url>';echo'</urlset>';
}

function ef_sitemap_count(string $group):int
{
    if($group==='static'){$count=6;foreach(['about','editorial-policy','privacy','contact'] as$slug)if(one("SELECT id FROM content WHERE slug=? AND status='published' AND noindex=0",[$slug]))$count++;return$count;}if($group==='blog')return 1+(int)(one("SELECT COUNT(*) n FROM categories cat WHERE cat.type='blog_category' AND EXISTS(SELECT 1 FROM content c WHERE c.category_id=cat.id AND c.type='blog_post' AND c.status='published' AND c.noindex=0)")['n']??0)+(int)(one("SELECT COUNT(*) n FROM content WHERE type='blog_post' AND status='published' AND noindex=0")['n']??0);if($group==='tools')return(int)(one("SELECT COUNT(*) n FROM categories cat WHERE cat.type='tool_category' AND EXISTS(SELECT 1 FROM content c WHERE c.category_id=cat.id AND c.type='tool' AND c.status='published' AND c.noindex=0)")['n']??0)+(int)(one("SELECT COUNT(*) n FROM content WHERE type='tool' AND status='published' AND noindex=0")['n']??0);if($group==='categories')return(int)(one("SELECT COUNT(*) n FROM categories cat WHERE cat.type NOT IN('blog_category','tool_category') AND EXISTS(SELECT 1 FROM content c WHERE c.category_id=cat.id AND c.status='published' AND c.noindex=0)")['n']??0);if($group==='images')return(int)(one("SELECT COUNT(DISTINCT CONCAT(c.id,':',m.id)) n FROM media m JOIN content c ON c.body LIKE CONCAT('%',m.path,'%') WHERE c.status='published' AND c.noindex=0 AND m.mime LIKE 'image/%'")['n']??0);$types=ef_sitemap_group($group);if(!$types)return 0;$ph=implode(',',array_fill(0,count($types),'?'));return(int)(one("SELECT COUNT(*) n FROM content WHERE status='published' AND noindex=0 AND type IN($ph)",$types)['n']??0);
}

function ef_blog_sitemap_page(int $offset):void
{
    $cats=all("SELECT cat.slug,cat.updated_at FROM categories cat WHERE cat.type='blog_category' AND EXISTS(SELECT 1 FROM content c WHERE c.category_id=cat.id AND c.type='blog_post' AND c.status='published' AND c.noindex=0) ORDER BY cat.id");$prefix=[['loc'=>url('blog'),'lastmod'=>null]];foreach($cats as$cat)$prefix[]=['loc'=>url('blog/category/'.$cat['slug']),'lastmod'=>substr($cat['updated_at'],0,10)];$entries=array_slice($prefix,$offset,50000);$remaining=50000-count($entries);$postOffset=max(0,$offset-count($prefix));if($remaining>0){$posts=all("SELECT slug,updated_at FROM content WHERE type='blog_post' AND status='published' AND noindex=0 ORDER BY id LIMIT $remaining OFFSET $postOffset");foreach($posts as$post)$entries[]=['loc'=>url($post['slug']),'lastmod'=>substr($post['updated_at'],0,10)];}echo'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';foreach($entries as$x)echo'<url><loc>'.ef_xml($x['loc']).'</loc>'.($x['lastmod']?'<lastmod>'.ef_xml($x['lastmod']).'</lastmod>':'').'</url>';echo'</urlset>';
}

function ef_tools_sitemap_page(int $offset):void
{
    $cats=all("SELECT cat.slug,cat.updated_at FROM categories cat WHERE cat.type='tool_category' AND EXISTS(SELECT 1 FROM content c WHERE c.category_id=cat.id AND c.type='tool' AND c.status='published' AND c.noindex=0) ORDER BY cat.id");$prefix=[];foreach($cats as$cat)$prefix[]=['loc'=>url('tools/category/'.$cat['slug']),'lastmod'=>substr($cat['updated_at'],0,10)];$entries=array_slice($prefix,$offset,50000);$remaining=50000-count($entries);$postOffset=max(0,$offset-count($prefix));if($remaining>0){$posts=all("SELECT slug,updated_at FROM content WHERE type='tool' AND status='published' AND noindex=0 ORDER BY id LIMIT $remaining OFFSET $postOffset");foreach($posts as$post)$entries[]=['loc'=>url($post['slug']),'lastmod'=>substr($post['updated_at'],0,10)];}echo'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';foreach($entries as$x)echo'<url><loc>'.ef_xml($x['loc']).'</loc><lastmod>'.ef_xml($x['lastmod']).'</lastmod></url>';echo'</urlset>';
}
