<?php
function qr_append_utm(string $url, array $qr): string {
    $fields = ['utm_source','utm_medium','utm_campaign','utm_term','utm_content'];
    $params = [];
    foreach ($fields as $field) {
        $value = trim((string)($qr[$field] ?? ''));
        if ($value !== '') { $params[$field] = $value; }
    }
    if (!$params) return $url;
    return $url . (str_contains($url, '?') ? '&' : '?') . http_build_query($params, '', '&', PHP_QUERY_RFC3986);
}
function qr_geo_empty(): array { return ['country_code'=>null,'country_name'=>null,'region_name'=>null,'city'=>null]; }
function qr_geo_lookup(array $config, string $ip): array {
    if (!empty($config['geo_tracking']) && ($config['geo_provider'] ?? 'none') === 'cloudflare') {
        $cc = $_SERVER['HTTP_CF_IPCOUNTRY'] ?? '';
        if (preg_match('/^[A-Z]{2}$/', $cc)) return ['country_code'=>$cc,'country_name'=>null,'region_name'=>null,'city'=>null];
        return qr_geo_empty();
    }
    if (empty($config['geo_tracking']) || ($config['geo_provider'] ?? 'none') !== 'ipapi') return qr_geo_empty();
    if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) return qr_geo_empty();
    $ctx = stream_context_create(['http'=>['timeout'=>2,'ignore_errors'=>true,'header'=>'User-Agent: KuhRator/13']]);
    $json = @file_get_contents('https://ipapi.co/'.rawurlencode($ip).'/json/', false, $ctx);
    $data = $json ? json_decode($json, true) : null;
    if (!is_array($data)) return qr_geo_empty();
    return [
        'country_code' => isset($data['country_code']) ? substr((string)$data['country_code'],0,2) : null,
        'country_name' => isset($data['country_name']) ? substr((string)$data['country_name'],0,120) : null,
        'region_name' => isset($data['region']) ? substr((string)$data['region'],0,120) : null,
        'city' => isset($data['city']) ? substr((string)$data['city'],0,120) : null,
    ];
}
function pdf_escape_text(string $s): string {
    $s = @iconv('UTF-8', 'ISO-8859-1//TRANSLIT//IGNORE', $s);
    return str_replace(['\\','(',')'], ['\\\\','\\(','\\)'], $s ?: '');
}
function simple_pdf_report(string $title, array $lines): string {
    $objects=[]; $pages=[]; $lineHeight=16; $top=800; $left=50; $maxLines=45; $chunks=array_chunk($lines,$maxLines) ?: [[]]; $objNo=3;
    foreach($chunks as $chunk){
        $content="BT /F1 20 Tf {$left} {$top} Td (".pdf_escape_text($title).") Tj 0 -28 Td /F1 11 Tf";
        foreach($chunk as $line){ $content .= " 0 -{$lineHeight} Td (".pdf_escape_text($line).") Tj"; }
        $content.=" ET"; $contentObj=$objNo++; $pageObj=$objNo++;
        $objects[$contentObj]="<< /Length ".strlen($content)." >>\nstream\n{$content}\nendstream";
        $objects[$pageObj]="<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 1 0 R >> >> /Contents {$contentObj} 0 R >>";
        $pages[]=$pageObj;
    }
    $objects[1]="<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>";
    $objects[2]="<< /Type /Pages /Kids [".implode(' ',array_map(fn($n)=>$n.' 0 R',$pages))."] /Count ".count($pages)." >>";
    $catalog=$objNo; $objects[$catalog]="<< /Type /Catalog /Pages 2 0 R >>"; ksort($objects); $pdf="%PDF-1.4\n"; $offsets=[];
    foreach($objects as $n=>$body){ $offsets[$n]=strlen($pdf); $pdf.="{$n} 0 obj\n{$body}\nendobj\n"; }
    $xref=strlen($pdf); $size=$catalog+1; $pdf.="xref\n0 {$size}\n0000000000 65535 f \n";
    for($i=1;$i<$size;$i++){ $pdf.=sprintf("%010d 00000 n \n", $offsets[$i] ?? 0); }
    return $pdf."trailer\n<< /Size {$size} /Root {$catalog} 0 R >>\nstartxref\n{$xref}\n%%EOF";
}
