<?php
declare(strict_types=1);
session_start(['cookie_httponly'=>true,'cookie_samesite'=>'Lax','use_strict_mode'=>true]);
$config=require dirname(__DIR__).'/config.php';
$pdo=new PDO('sqlite:'.$config['database_path'],null,null,[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC,PDO::ATTR_EMULATE_PREPARES=>false]);
$pdo->exec('PRAGMA foreign_keys=ON; PRAGMA busy_timeout=5000; PRAGMA journal_mode=WAL;');
$_SESSION['user']??='admin';
function e(?string $v):string{return htmlspecialchars($v??'',ENT_QUOTES|ENT_SUBSTITUTE,'UTF-8');}
function u(string $p=''):string{global $config;return rtrim($config['base_url'],'/').'/'.ltrim($p,'/');}
function token():string{return $_SESSION['csrf']??=bin2hex(random_bytes(32));}
function csrf():void{if(!isset($_POST['csrf'])||!hash_equals(token(),(string)$_POST['csrf'])){http_response_code(419);exit('Invalid form token.');}}
function go(string $p):never{header('Location: '.u($p),true,303);exit;}
function flash(string $m,string $type='success'):void{$_SESSION['flash']=['message'=>$m,'type'=>$type];}
function norm_serial(string $v):string{$v=trim($v);if(strlen($v)!==12)return $v;return strtoupper(substr($v,0,11)).strtolower(substr($v,11,1));}
function valid_serial(string $v):bool{return preg_match('/^[A-Z0-9]{4}\/[A-Z0-9]{5}\/[a-z]$/',$v)===1;}
function norm_voucher(string $v):string{return strtoupper(preg_replace('/\s+/','',trim($v)));}
function valid_voucher(string $v):bool{return preg_match('/^[A-Z0-9]{3,4}(?:-[A-Z0-9]{3,4}){3}$/',$v)===1;}
function now_utc():string{return gmdate('Y-m-d\TH:i:s\Z');}
function annual_valid(?string $date):bool{if(!$date)return false;$d=DateTimeImmutable::createFromFormat('!Y-m-d',$date,new DateTimeZone('America/New_York'));$today=new DateTimeImmutable('today',new DateTimeZone('America/New_York'));return $d&&$d->format('Y-m-d')===$date&&$d>=$today;}
