<?php
/**
 * Plugin Name: IIS MetaTrace
 * Description: Self-service document & photo metadata forensics. Upload an image or document,
 *              reveal hidden metadata (EXIF GPS, device, timestamps, author, edit history).
 *              Account-gated, 1 free report, then Stripe/MMG. Admin dashboard + evidence report.
 * Version:     1.0.0
 */

if (!defined('ABSPATH')) exit;

const IIS_META_VERSION = '1.0.0';
const IIS_META_FREE = 1;                 // free reports per account
const IIS_META_RATE_PER_HOUR = 30;       // uploads per client per hour
const IIS_META_FREE_MAX_MB = 2;          // free-tier upload cap; paid clients get the full server ceiling

/* ------------------------------------------------------------------ tables */
function iis_meta_tables(): array {
    global $wpdb;
    return [
        'reports' => $wpdb->prefix . 'iismeta_reports',
        'audit'   => $wpdb->prefix . 'iismeta_audit',
    ];
}

function iis_meta_storage_dir(): array {
    $up = wp_upload_dir();
    return [
        'dir' => $up['basedir'] . '/iis-metatrace',
        'url' => $up['baseurl'] . '/iis-metatrace',
    ];
}

function iis_meta_install(): void {
    global $wpdb;
    $t = iis_meta_tables();
    $charset = $wpdb->get_charset_collate();
    require_once ABSPATH . 'wp-admin/includes/upgrade.php';

    dbDelta("CREATE TABLE {$t['reports']} (
        id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        user_id BIGINT UNSIGNED NOT NULL,
        original_name VARCHAR(255) NOT NULL,
        stored_path VARCHAR(255) DEFAULT '',
        mime VARCHAR(100) DEFAULT '',
        size_bytes BIGINT UNSIGNED DEFAULT 0,
        sha256 CHAR(64) DEFAULT '',
        md5 CHAR(32) DEFAULT '',
        has_gps TINYINT(1) DEFAULT 0,
        findings_count INT DEFAULT 0,
        meta LONGTEXT,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id), KEY user_id (user_id)
    ) $charset;");

    dbDelta("CREATE TABLE {$t['audit']} (
        id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        admin_id BIGINT UNSIGNED NOT NULL,
        action VARCHAR(64) NOT NULL,
        target VARCHAR(191) DEFAULT '',
        detail TEXT,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id), KEY admin_id (admin_id)
    ) $charset;");

    // Protected storage for originals (not web-accessible, not executable).
    $s = iis_meta_storage_dir();
    if (!file_exists($s['dir'])) wp_mkdir_p($s['dir']);
    if (is_dir($s['dir'])) {
        @file_put_contents($s['dir'] . '/.htaccess', "Require all denied\n<IfModule !mod_authz_core.c>\nOrder deny,allow\nDeny from all\n</IfModule>\n");
        @file_put_contents($s['dir'] . '/index.php', "<?php // silence\n");
    }

    add_rewrite_rule('^metadata/?$', 'index.php?iis_meta_page=1', 'top');
    flush_rewrite_rules();
}
register_activation_hook(__FILE__, 'iis_meta_install');

add_action('init', function() {
    add_rewrite_rule('^metadata/?$', 'index.php?iis_meta_page=1', 'top');
    add_rewrite_tag('%iis_meta_page%', '([0-9]+)');
    if (get_option('iis_meta_db_version') !== IIS_META_VERSION) {
        iis_meta_install();
        update_option('iis_meta_db_version', IIS_META_VERSION);
    }
});

add_action('template_redirect', function() {
    global $wp_query;
    if (!empty($wp_query->query_vars['iis_meta_page'])) {
        iis_meta_render_page();
        exit;
    }
});

/* ================================================================== pricing / quota */
function iis_meta_skus(): array {
    return [
        'single'    => ['label' => '1 additional report',          'usd' => 10,  'gyd' => 2000,  'type' => 'single'],
        'unlimited' => ['label' => 'Unlimited reports for 1 year', 'usd' => 100, 'gyd' => 20000, 'type' => 'unlimited'],
    ];
}

function iis_meta_get_quota(int $user_id): array {
    global $wpdb; $t = iis_meta_tables();
    $used = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$t['reports']} WHERE user_id=%d", $user_id));
    $has_unlimited = (bool) get_user_meta($user_id, 'iis_meta_unlimited', true);
    $exp = get_user_meta($user_id, 'iis_meta_unlimited_expires', true);
    if ($has_unlimited && $exp && strtotime($exp) < time()) $has_unlimited = false;
    $paid = (int) get_user_meta($user_id, 'iis_meta_paid', true);
    $total = IIS_META_FREE + $paid;
    if ($has_unlimited) $total = PHP_INT_MAX;
    return [
        'used' => $used, 'total' => $total, 'has_unlimited' => $has_unlimited,
        'unlimited_expires' => $has_unlimited ? $exp : '',
        'can_create' => ($used < $total) || $has_unlimited,
    ];
}

function iis_meta_apply_purchase(int $user_id, string $sku, string $ref, string $method): bool {
    if (!$user_id) return false;
    $skus = iis_meta_skus();
    if (!isset($skus[$sku])) { error_log("IIS MetaTrace: unknown sku $sku"); return false; }
    $done = (array) get_user_meta($user_id, 'iis_meta_payments', true);
    $key = $method . ':' . $ref;
    if ($ref !== '' && in_array($key, $done, true)) return true;
    if ($skus[$sku]['type'] === 'unlimited') {
        update_user_meta($user_id, 'iis_meta_unlimited', 1);
        update_user_meta($user_id, 'iis_meta_unlimited_expires', date('Y-m-d H:i:s', strtotime('+1 year')));
    } else {
        $paid = (int) get_user_meta($user_id, 'iis_meta_paid', true);
        update_user_meta($user_id, 'iis_meta_paid', $paid + 1);
    }
    if ($ref !== '') { $done[] = $key; update_user_meta($user_id, 'iis_meta_payments', $done); }
    $log = (array) get_user_meta($user_id, 'iis_meta_payment_log', true);
    $log[] = ['sku'=>$sku,'type'=>$skus[$sku]['type'],'method'=>$method,'ref'=>$ref,'usd'=>$skus[$sku]['usd'],'gyd'=>$skus[$sku]['gyd'],'at'=>current_time('mysql')];
    update_user_meta($user_id, 'iis_meta_payment_log', $log);
    $u = get_userdata($user_id);
    if ($u) wp_mail($u->user_email, 'MetaTrace — Payment Received',
        "Hello {$u->display_name},\n\nYour MetaTrace payment has been received and your account credited.\n\nPurchase: {$skus[$sku]['label']}\nMethod: " . strtoupper($method) . "\nReference: {$ref}\n\nOpen MetaTrace: " . home_url('/metadata/') . "\n\n— Investigative Intelligence Solutions Inc.\n  iis.gy | contact@iis.gy",
        ['Content-Type: text/plain; charset=UTF-8', 'From: IIS <contact@iis.gy>']);
    return true;
}

function iis_meta_stripe_available(): bool {
    return function_exists('iis_stripe_create_checkout_session') && get_option('iis_stripe_sk');
}
function iis_meta_mmg_available(): bool {
    if (!class_exists('NT_Payment')) {
        $f = WP_PLUGIN_DIR . '/nationaltrace/includes/class-payment.php';
        if (file_exists($f)) require_once $f;
    }
    if (!class_exists('NT_Payment') || !method_exists('NT_Payment', 'create_mmg_checkout_raw')) return false;
    $kd = WP_PLUGIN_DIR . '/nationaltrace/keys/';
    return file_exists($kd . '6231721.public.pem') && file_exists($kd . 'mmg_encrypt.py');
}

/* Stripe */
add_action('wp_ajax_iis_meta_stripe_checkout', function() {
    check_ajax_referer('iis_meta_pay', 'nonce');
    $uid = get_current_user_id();
    if (!$uid) wp_send_json_error(['message' => 'Please sign in.'], 403);
    $sku = sanitize_key($_POST['sku'] ?? ''); $skus = iis_meta_skus();
    if (!isset($skus[$sku])) wp_send_json_error(['message' => 'Invalid option.']);
    if (!iis_meta_stripe_available()) wp_send_json_error(['message' => 'Card payment unavailable. Please use MMG.']);
    $res = iis_stripe_create_checkout_session([
        'product_name' => 'MetaTrace — ' . $skus[$sku]['label'],
        'amount_usd'   => $skus[$sku]['usd'],
        'success_url'  => home_url('/metadata/?purchase=success'),
        'cancel_url'   => home_url('/metadata/?purchase=cancelled'),
        'metadata'     => ['product' => 'iis_meta', 'user_id' => (string)$uid, 'sku' => $sku],
    ]);
    if (isset($res['url'])) wp_send_json_success(['redirect_url' => $res['url']]);
    wp_send_json_error(['message' => $res['error'] ?? 'Could not start payment.']);
});
add_action('iis_stripe_checkout_complete', function($session) {
    $md = (array)($session->metadata ?? []);
    if (($md['product'] ?? '') !== 'iis_meta') return;
    $uid = intval($md['user_id'] ?? 0); $sku = sanitize_key($md['sku'] ?? '');
    if ($uid && $sku) iis_meta_apply_purchase($uid, $sku, (string)$session->id, 'stripe');
});

/* MMG */
add_action('wp_ajax_iis_meta_mmg_checkout', function() {
    check_ajax_referer('iis_meta_pay', 'nonce');
    $uid = get_current_user_id();
    if (!$uid) wp_send_json_error(['message' => 'Please sign in.'], 403);
    $sku = sanitize_key($_POST['sku'] ?? ''); $skus = iis_meta_skus();
    if (!isset($skus[$sku])) wp_send_json_error(['message' => 'Invalid option.']);
    if (!iis_meta_mmg_available()) wp_send_json_error(['message' => 'MMG payment unavailable. Please use card.']);
    $amount = (int)$skus[$sku]['gyd'];
    $txn = (string)(time() * 1000 + ($uid % 1000) + 500);
    set_transient('iismeta_mmg_order_' . $txn, ['user_id'=>$uid,'sku'=>$sku,'amount'=>$amount,'created'=>time()], HOUR_IN_SECONDS);
    $res = NT_Payment::create_mmg_checkout_raw($txn, $amount, 'MetaTrace - ' . $skus[$sku]['label']);
    if (isset($res['url'])) wp_send_json_success(['redirect_url' => $res['url']]);
    wp_send_json_error(['message' => $res['error'] ?? 'Could not start MMG payment.']);
});
function iis_meta_process_mmg(string $txn_id, array $result): bool {
    $order = get_transient('iismeta_mmg_order_' . $txn_id);
    if (!$order) return false;
    $ref = $result['transactionId'] ?? $txn_id;
    $ok = iis_meta_apply_purchase((int)$order['user_id'], (string)$order['sku'], (string)$ref, 'mmg');
    if ($ok) delete_transient('iismeta_mmg_order_' . $txn_id);
    return $ok;
}

/* ================================================================== extraction engine */
function iis_meta_allowed(): array {
    return [
        'jpg'=>'image/jpeg','jpeg'=>'image/jpeg','jpe'=>'image/jpeg','tif'=>'image/tiff','tiff'=>'image/tiff',
        'png'=>'image/png','gif'=>'image/gif','webp'=>'image/webp','heic'=>'image/heic','heif'=>'image/heif',
        'pdf'=>'application/pdf',
        'docx'=>'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        'xlsx'=>'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        'pptx'=>'application/vnd.openxmlformats-officedocument.presentationml.presentation',
    ];
}

/** Convert an EXIF GPS coordinate (array of rationals) + hemisphere ref to decimal degrees. */
function iis_meta_gps_decimal($coord, $ref): ?float {
    if (!is_array($coord) || count($coord) < 3) return null;
    $deg = iis_meta_rational($coord[0]); $min = iis_meta_rational($coord[1]); $sec = iis_meta_rational($coord[2]);
    if ($deg === null) return null;
    $dec = $deg + ($min/60) + ($sec/3600);
    if (in_array(strtoupper((string)$ref), ['S','W'], true)) $dec = -$dec;
    return round($dec, 6);
}
function iis_meta_rational($v): ?float {
    if (is_numeric($v)) return (float)$v;
    if (is_string($v) && strpos($v, '/') !== false) { [$n,$d] = array_pad(explode('/', $v, 2), 2, 1); return $d ? ((float)$n/(float)$d) : 0.0; }
    return null;
}

/**
 * Extract metadata. Returns a structured array:
 *   ['sections' => [ 'Section' => [k=>v,...] ], 'gps' => [lat,lon]|null, 'findings' => [str], 'summary' => str]
 */
function iis_meta_extract(string $path, string $mime, string $origname): array {
    $sections = []; $gps = null; $findings = [];

    $size = @filesize($path) ?: 0;
    $sha = @hash_file('sha256', $path) ?: '';
    $md5 = @hash_file('md5', $path) ?: '';
    $sections['File'] = [
        'Original name' => $origname,
        'Detected type' => $mime,
        'Size'          => size_format($size, 1) . " ({$size} bytes)",
        'SHA-256'       => $sha,
        'MD5'           => $md5,
    ];

    $ext = strtolower(pathinfo($origname, PATHINFO_EXTENSION));

    if (strpos($mime, 'image/') === 0) {
        $gi = @getimagesize($path);
        if ($gi) $sections['Image']['Dimensions'] = $gi[0] . ' × ' . $gi[1] . ' px';

        if (in_array($mime, ['image/jpeg','image/tiff'], true) && function_exists('exif_read_data')) {
            $exif = @exif_read_data($path, null, true);
            if ($exif) {
                $ifd0 = $exif['IFD0'] ?? []; $ex = $exif['EXIF'] ?? [];
                $cam = [];
                if (!empty($ifd0['Make']))  $cam['Camera make']  = trim($ifd0['Make']);
                if (!empty($ifd0['Model'])) $cam['Camera model'] = trim($ifd0['Model']);
                if (!empty($ex['LensModel'])) $cam['Lens'] = trim($ex['LensModel']);
                if (!empty($ifd0['Software'])) $cam['Software'] = trim($ifd0['Software']);
                if (!empty($ifd0['Artist'])) $cam['Author / Artist'] = trim($ifd0['Artist']);
                if (!empty($ifd0['Copyright'])) $cam['Copyright'] = trim($ifd0['Copyright']);
                if ($cam) $sections['Camera & device'] = $cam;

                $times = [];
                foreach (['DateTimeOriginal'=>'Taken','DateTimeDigitized'=>'Digitised'] as $k=>$lbl) if (!empty($ex[$k])) $times[$lbl] = $ex[$k];
                if (!empty($ifd0['DateTime'])) $times['Modified'] = $ifd0['DateTime'];
                if ($times) $sections['Timestamps'] = $times;

                $shot = [];
                foreach (['ExposureTime'=>'Exposure','FNumber'=>'Aperture','ISOSpeedRatings'=>'ISO','FocalLength'=>'Focal length','Flash'=>'Flash'] as $k=>$lbl)
                    if (isset($ex[$k])) $shot[$lbl] = is_array($ex[$k]) ? implode(',', $ex[$k]) : (string)$ex[$k];
                if ($shot) $sections['Capture settings'] = $shot;

                if (!empty($exif['GPS'])) {
                    $g = $exif['GPS'];
                    $lat = isset($g['GPSLatitude']) ? iis_meta_gps_decimal($g['GPSLatitude'], $g['GPSLatitudeRef'] ?? 'N') : null;
                    $lon = isset($g['GPSLongitude']) ? iis_meta_gps_decimal($g['GPSLongitude'], $g['GPSLongitudeRef'] ?? 'E') : null;
                    if ($lat !== null && $lon !== null) {
                        $gps = [$lat, $lon];
                        $gline = ['Coordinates' => "{$lat}, {$lon}", 'Map' => "https://www.google.com/maps?q={$lat},{$lon}"];
                        if (isset($g['GPSAltitude'])) $gline['Altitude'] = round(iis_meta_rational($g['GPSAltitude']) ?? 0, 1) . ' m';
                        if (!empty($g['GPSDateStamp'])) $gline['GPS date'] = $g['GPSDateStamp'];
                        $sections['GPS location'] = $gline;
                    }
                }
            }
        } elseif ($mime === 'image/png') {
            $sections['PNG text'] = iis_meta_png_text($path);
        }
    } elseif ($mime === 'application/pdf') {
        $sections['PDF document'] = iis_meta_pdf_info($path);
    } elseif (in_array($ext, ['docx','xlsx','pptx'], true) && class_exists('ZipArchive')) {
        $sections['Office document'] = iis_meta_office_info($path);
    }

    // Findings — the plain-language "what this reveals".
    if ($gps) $findings[] = 'This file embeds the exact GPS coordinates where it was created. The location is exposed.';
    foreach (['Camera & device','Office document','PDF document'] as $sc) {
        foreach (['Author / Artist','Author','Creator','Last modified by'] as $k) {
            if (!empty($sections[$sc][$k])) { $findings[] = 'A person or author name is embedded: "' . $sections[$sc][$k] . '".'; break 2; }
        }
    }
    if (!empty($sections['Camera & device']['Camera model'])) $findings[] = 'The capturing device is identifiable: ' . $sections['Camera & device']['Camera model'] . '.';
    if (!empty($sections['Camera & device']['Software']) || !empty($sections['PDF document']['Producer']) || !empty($sections['Office document']['Application']))
        $findings[] = 'Editing/producing software is recorded, which can indicate the file was processed or altered.';
    $hasTimes = !empty($sections['Timestamps']) || !empty($sections['PDF document']['Created']) || !empty($sections['Office document']['Created']);
    if ($hasTimes) $findings[] = 'Creation and/or modification timestamps are present and can establish a timeline.';
    if (!$findings) $findings[] = 'No sensitive metadata (location, author, device) was found. The file appears clean or stripped.';

    $summary = $gps ? 'Location data present' : (count($findings) > 1 ? 'Identifying metadata present' : 'Minimal metadata');

    return ['sections' => $sections, 'gps' => $gps, 'findings' => $findings, 'summary' => $summary,
            'sha256' => $sha, 'md5' => $md5, 'size' => $size];
}

function iis_meta_png_text(string $path): array {
    $out = [];
    $fp = @fopen($path, 'rb'); if (!$fp) return $out;
    $sig = fread($fp, 8);
    if ($sig !== "\x89PNG\r\n\x1a\n") { fclose($fp); return $out; }
    $guard = 0;
    while (!feof($fp) && $guard++ < 200) {
        $lenData = fread($fp, 4); if (strlen($lenData) < 4) break;
        $len = unpack('N', $lenData)[1];
        $type = fread($fp, 4);
        if (in_array($type, ['tEXt','iTXt'], true)) {
            $data = $len > 0 ? fread($fp, $len) : '';
            $parts = explode("\0", $data, 2);
            $key = $parts[0] ?? ''; $val = $parts[1] ?? '';
            if ($type === 'iTXt') { $val = preg_replace('~^[\x00-\x1f]+~', '', $val); }
            if ($key !== '') $out[substr($key,0,40)] = substr(trim((string)$val), 0, 300);
            fread($fp, 4); // CRC
        } else {
            if ($type === 'IEND') break;
            fseek($fp, $len + 4, SEEK_CUR);
        }
    }
    fclose($fp);
    return $out ?: ['(none)' => 'No text metadata found.'];
}

function iis_meta_pdf_info(string $path): array {
    $raw = @file_get_contents($path, false, null, 0, 2 * 1024 * 1024); // first 2MB
    if ($raw === false) return [];
    $out = [];
    $map = ['Title'=>'Title','Author'=>'Author','Subject'=>'Subject','Creator'=>'Creator','Producer'=>'Producer','CreationDate'=>'Created','ModDate'=>'Modified','Keywords'=>'Keywords'];
    foreach ($map as $k=>$lbl) {
        if (preg_match('~/' . $k . '\s*\(((?:[^()\\\\]|\\\\.)*)\)~s', $raw, $m)) {
            $v = stripcslashes($m[1]);
            if ($k === 'CreationDate' || $k === 'ModDate') $v = iis_meta_pdf_date($v);
            if (trim($v) !== '') $out[$lbl] = substr(trim($v), 0, 300);
        }
    }
    if (preg_match('~/Type\s*/Catalog~', $raw)) {
        if (preg_match_all('~/Type\s*/Page[^s]~', $raw, $mm)) $out['Pages (approx.)'] = (string)count($mm[0]);
    }
    // XMP packet (creator tool, dates)
    if (preg_match('~<x:xmpmeta.*?</x:xmpmeta>~s', $raw, $xm)) {
        if (preg_match('~<xmp:CreatorTool>([^<]+)</xmp:CreatorTool>~', $xm[0], $c)) $out['XMP creator tool'] = trim($c[1]);
    }
    return $out ?: ['(none)' => 'No document metadata found.'];
}
function iis_meta_pdf_date(string $d): string {
    if (preg_match('~D:(\d{4})(\d{2})?(\d{2})?(\d{2})?(\d{2})?(\d{2})?~', $d, $m)) {
        return sprintf('%s-%s-%s %s:%s:%s', $m[1], $m[2]?:'01', $m[3]?:'01', $m[4]?:'00', $m[5]?:'00', $m[6]?:'00');
    }
    return $d;
}

function iis_meta_office_info(string $path): array {
    $out = []; $zip = new ZipArchive();
    if ($zip->open($path) !== true) return ['(error)' => 'Could not read the document.'];
    $core = $zip->getFromName('docProps/core.xml');
    $app  = $zip->getFromName('docProps/app.xml');
    $zip->close();
    $grab = function($xml, $tag) { return (preg_match('~<' . preg_quote($tag,'~') . '[^>]*>([^<]*)</' . preg_quote($tag,'~') . '>~', (string)$xml, $m)) ? trim($m[1]) : ''; };
    if ($core) {
        foreach ([['dc:creator','Author'],['cp:lastModifiedBy','Last modified by'],['dcterms:created','Created'],['dcterms:modified','Modified'],['dc:title','Title'],['cp:revision','Revision']] as $p) {
            $v = $grab($core, $p[0]); if ($v !== '') $out[$p[1]] = substr($v, 0, 300);
        }
    }
    if ($app) {
        foreach ([['Application','Application'],['Company','Company'],['TotalTime','Editing time (min)']] as $p) {
            $v = $grab($app, $p[0]); if ($v !== '') $out[$p[1]] = substr($v, 0, 300);
        }
    }
    return $out ?: ['(none)' => 'No document metadata found.'];
}

/* ================================================================== upload handling */
/** Has this client paid in any way (bought a report or holds an active unlimited plan)? */
function iis_meta_is_paid(int $user_id): bool {
    if (!$user_id) return false;
    $q = iis_meta_get_quota($user_id);
    if ($q['has_unlimited']) return true;
    return (int) get_user_meta($user_id, 'iis_meta_paid', true) > 0;
}

/** Upload cap for this client: paid clients get the full server ceiling; free clients are capped. */
function iis_meta_max_bytes(int $user_id = 0): int {
    $server = wp_max_upload_size();
    if ($user_id && iis_meta_is_paid($user_id)) return $server;
    $free = IIS_META_FREE_MAX_MB * 1024 * 1024;
    return min($server, $free);
}

function iis_meta_handle_upload(int $user_id): void {
    // Rate limit
    $rk = 'iis_meta_rate_' . $user_id;
    $recent = (int) get_transient($rk);
    if ($recent >= IIS_META_RATE_PER_HOUR) {
        wp_redirect('/metadata/?error=' . urlencode('Too many uploads in a short time. Please wait and try again.')); exit;
    }
    $quota = iis_meta_get_quota($user_id);
    if (!$quota['can_create']) { wp_redirect('/metadata/?error=' . urlencode('No reports left. Please purchase to continue.')); exit; }

    if (empty($_FILES['file']) || !is_uploaded_file($_FILES['file']['tmp_name'] ?? '')) {
        wp_redirect('/metadata/?error=' . urlencode('Please choose a file to analyse.')); exit;
    }
    $f = $_FILES['file'];
    $cap = iis_meta_max_bytes($user_id);
    $paid = iis_meta_is_paid($user_id);
    $over_msg = 'That file is larger than your ' . size_format($cap) . ' limit.' . ($paid ? '' : ' Paid plans allow up to ' . size_format(wp_max_upload_size()) . ' — upgrade to analyse larger files.');
    if (!empty($f['error'])) {
        $msg = ($f['error'] == UPLOAD_ERR_INI_SIZE || $f['error'] == UPLOAD_ERR_FORM_SIZE) ? $over_msg : 'Upload failed. Please try again.';
        wp_redirect('/metadata/?error=' . urlencode($msg)); exit;
    }
    if ((int)$f['size'] > $cap) {
        wp_redirect('/metadata/?error=' . urlencode($over_msg)); exit;
    }

    $origname = sanitize_file_name($f['name']);
    $ext = strtolower(pathinfo($origname, PATHINFO_EXTENSION));
    $allowed = iis_meta_allowed();
    if (!isset($allowed[$ext])) {
        wp_redirect('/metadata/?error=' . urlencode('Unsupported file type. Allowed: JPG, PNG, GIF, TIFF, WEBP, HEIC, PDF, DOCX, XLSX, PPTX.')); exit;
    }
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $mime = $finfo->file($f['tmp_name']) ?: ($allowed[$ext]);
    // Reject anything that detects as a script/html regardless of extension.
    if (preg_match('~(php|html|xml|script|octet-stream)~i', $mime) && strpos($mime, 'officedocument') === false) {
        wp_redirect('/metadata/?error=' . urlencode('That file could not be accepted.')); exit;
    }

    $s = iis_meta_storage_dir();
    if (!file_exists($s['dir'])) iis_meta_install();
    $stored = wp_generate_password(24, false, false) . '.' . $ext;
    $dest = $s['dir'] . '/' . $stored;
    if (!@move_uploaded_file($f['tmp_name'], $dest)) {
        wp_redirect('/metadata/?error=' . urlencode('Could not store the file. Please try again.')); exit;
    }
    @chmod($dest, 0640);

    $data = iis_meta_extract($dest, $mime, $origname);

    global $wpdb; $t = iis_meta_tables();
    $wpdb->insert($t['reports'], [
        'user_id' => $user_id,
        'original_name' => $origname,
        'stored_path' => $stored,
        'mime' => $mime,
        'size_bytes' => (int)$data['size'],
        'sha256' => $data['sha256'],
        'md5' => $data['md5'],
        'has_gps' => $data['gps'] ? 1 : 0,
        'findings_count' => count($data['findings']),
        'meta' => wp_json_encode($data),
    ]);
    $rid = (int)$wpdb->insert_id;
    set_transient($rk, $recent + 1, HOUR_IN_SECONDS);
    wp_redirect('/metadata/?report=' . $rid); exit;
}

function iis_meta_client_delete(int $user_id): void {
    global $wpdb; $t = iis_meta_tables();
    $rid = intval($_POST['report_id'] ?? 0);
    if ($rid) {
        $r = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$t['reports']} WHERE id=%d AND user_id=%d", $rid, $user_id));
        if ($r) {
            $s = iis_meta_storage_dir();
            if ($r->stored_path) @unlink($s['dir'] . '/' . $r->stored_path);
            $wpdb->delete($t['reports'], ['id' => $rid], ['%d']);
        }
    }
    wp_redirect('/metadata/?deleted=1'); exit;
}

/* ================================================================== client page */
function iis_meta_bar(): void {
    $u = wp_get_current_user();
    ?>
    <div style="background:#111;border-bottom:1px solid #2a2a2a;">
      <div style="max-width:1000px;margin:0 auto;padding:10px 20px;display:flex;align-items:center;gap:18px;flex-wrap:wrap;font-size:0.9rem;">
        <a style="color:#e0e0e0;font-weight:700;letter-spacing:.04em;text-decoration:none;" href="<?php echo esc_url(home_url('/')); ?>">IIS Inc.</a>
        <a style="color:#c9a227;text-decoration:none;" href="<?php echo esc_url(home_url('/my-account/')); ?>">← My Account</a>
        <a style="color:#c9a227;text-decoration:none;" href="<?php echo esc_url(home_url('/national-trace/')); ?>">NationalTrace</a>
        <a style="color:#c9a227;text-decoration:none;" href="<?php echo esc_url(home_url('/ip-trace/')); ?>">IP Trace</a>
        <a style="color:#c9a227;text-decoration:none;" href="<?php echo esc_url(home_url('/metadata/')); ?>">Metadata</a>
        <span style="margin-left:auto;color:#888;"><?php echo esc_html($u->display_name ?: $u->user_login); ?> · <a style="color:#888;" href="<?php echo esc_url(wp_logout_url(home_url('/'))); ?>">Sign out</a></span>
      </div>
    </div>
    <?php
}

function iis_meta_head(string $title): void {
    ?><!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title><?php echo esc_html($title); ?></title>
    <style>
        *{box-sizing:border-box;margin:0;padding:0;}
        body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#0a0a0a;color:#e0e0e0;line-height:1.6;}
        .container{max-width:900px;margin:0 auto;padding:40px 20px;}
        h1{color:#c9a227;font-size:2rem;margin-bottom:6px;}
        .subtitle{color:#888;margin-bottom:26px;}
        .card{background:#1a1a1a;border:1px solid #333;border-radius:8px;padding:24px;margin-bottom:20px;}
        .card h2{color:#c9a227;font-size:1.15rem;margin-bottom:14px;}
        input[type=file]{width:100%;padding:12px;background:#222;border:1px dashed #555;color:#e0e0e0;border-radius:6px;}
        button{background:#c9a227;color:#0a0a0a;border:none;padding:12px 24px;border-radius:5px;font-weight:600;cursor:pointer;font-size:1rem;}
        button:hover{background:#d4b43a;} button.secondary{background:#333;color:#c9a227;border:1px solid #c9a227;}
        .quota-bar{background:#333;height:8px;border-radius:4px;overflow:hidden;margin:10px 0;}
        .quota-fill{background:#c9a227;height:100%;}
        .quota-text{font-size:.9rem;color:#888;}
        .help-text{color:#777;font-size:.85rem;margin-top:8px;}
        .error{background:#3a1a1a;border:1px solid #5a2a2a;color:#ff6b6b;padding:12px;border-radius:5px;margin-bottom:16px;}
        .success{background:#1a3a1a;border:1px solid #2a5a2a;color:#4caf50;padding:12px;border-radius:5px;margin-bottom:16px;}
        .price-cards{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-top:20px;}
        .price-card{background:#222;border:2px solid #333;border-radius:8px;padding:24px;text-align:center;}
        .price{font-size:2rem;color:#c9a227;font-weight:700;}
        .price-note{color:#888;font-size:.9rem;margin-top:8px;}
        table{width:100%;border-collapse:collapse;}
        th,td{padding:9px 10px;text-align:left;border-bottom:1px solid #2c2c2c;font-size:.9rem;vertical-align:top;}
        th{color:#c9a227;font-weight:600;width:38%;}
        .finding{background:#21180a;border:1px solid #3a2c10;color:#e8c766;padding:10px 12px;border-radius:6px;margin-bottom:8px;font-size:.9rem;}
        .finding.clean{background:#0f2a12;border-color:#1f4322;color:#8fdf9b;}
        .back-link{color:#c9a227;text-decoration:none;display:inline-block;margin-bottom:16px;}
        .rep-item{background:#222;border:1px solid #333;border-radius:6px;padding:14px;margin-bottom:10px;display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap;}
        .gps-tag{background:#4caf50;color:#0a0a0a;padding:2px 7px;border-radius:4px;font-size:.72rem;font-weight:700;}
        a{color:#c9a227;}
    </style></head><body><?php iis_meta_bar(); ?><div class="container"><?php
}

/**
 * Public, indexable landing shown to logged-out visitors at /metadata/, so search
 * engines and AI answer engines can discover the service. Members get the tool.
 */
function iis_meta_render_public_landing(): void {
    status_header(200);
    $url = home_url('/metadata/');
    $title = 'Metadata Forensics — Reveal Hidden Data in Photos & Documents | IIS Inc.';
    $desc  = 'Metadata Forensics by IIS Inc. reveals the hidden data inside a photo or document: GPS location, capturing device, author, software and timestamps. Upload a file and get a downloadable evidence report in seconds. Available worldwide; first report free.';
    // Carry the return destination so sign-up returns the client to this tool.
    $register = add_query_arg('redirect_to', rawurlencode($url), wp_registration_url());
    $login = wp_login_url($url);
    $faq = [
        ['What is file metadata?', 'Metadata is data stored inside a file that you do not normally see: where a photo was taken (GPS), the camera or phone that took it, the software used, who authored a document, and when it was created or last edited.'],
        ['Can it show where a photo was taken?', 'Yes, if the photo still carries its EXIF GPS tags. Many phones embed exact coordinates. Screenshots and images stripped by social networks usually do not, and we say so clearly.'],
        ['Can it tell if a photo or document was edited?', 'It surfaces the editing software, author and modification timestamps embedded in the file, which often indicate that it was processed or altered. It reports what is embedded; it does not by itself prove origin.'],
        ['What files are supported?', 'Photos (JPG, PNG, GIF, TIFF, WEBP, HEIC) and documents (PDF, Word, Excel, PowerPoint).'],
        ['How much does it cost?', 'Your first report is free. After that it is US$10 per report, or US$100 for unlimited reports for a year.'],
    ];
    $faq_schema = ['@context'=>'https://schema.org','@type'=>'FAQPage','mainEntity'=>array_map(fn($f)=>['@type'=>'Question','name'=>$f[0],'acceptedAnswer'=>['@type'=>'Answer','text'=>$f[1]]], $faq)];
    $svc_schema = ['@context'=>'https://schema.org','@type'=>'Service','name'=>'Metadata Forensics','serviceType'=>'Document and photo metadata extraction and forensic analysis','provider'=>['@type'=>'Organization','name'=>'Investigative Intelligence Solutions Inc.','url'=>'https://iis.gy','telephone'=>'+592-623-1721','email'=>'contact@iis.gy'],'areaServed'=>['@type'=>'Place','name'=>'Worldwide'],'url'=>$url,'description'=>$desc,'offers'=>[['@type'=>'Offer','name'=>'Single report','price'=>'10','priceCurrency'=>'USD'],['@type'=>'Offer','name'=>'Unlimited reports (1 year)','price'=>'100','priceCurrency'=>'USD']]];
    ?><!DOCTYPE html>
    <html <?php language_attributes(); ?>>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title><?php echo esc_html($title); ?></title>
        <meta name="description" content="<?php echo esc_attr($desc); ?>">
        <meta name="robots" content="index,follow,max-image-preview:large">
        <link rel="canonical" href="<?php echo esc_url($url); ?>">
        <meta property="og:type" content="website">
        <meta property="og:title" content="<?php echo esc_attr($title); ?>">
        <meta property="og:description" content="<?php echo esc_attr($desc); ?>">
        <meta property="og:url" content="<?php echo esc_url($url); ?>">
        <meta property="og:site_name" content="Investigative Intelligence Solutions Inc.">
        <script type="application/ld+json"><?php echo wp_json_encode($svc_schema, JSON_UNESCAPED_SLASHES); ?></script>
        <script type="application/ld+json"><?php echo wp_json_encode($faq_schema, JSON_UNESCAPED_SLASHES); ?></script>
        <style>
            *{box-sizing:border-box;margin:0;padding:0;}
            body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#0a0a0a;color:#e0e0e0;line-height:1.65;}
            .container{max-width:860px;margin:0 auto;padding:0 20px;}
            .hero{padding:64px 0 40px;text-align:center;}
            .hero h1{color:#c9a227;font-size:2.2rem;line-height:1.25;margin-bottom:16px;}
            .hero p{color:#cfcfcf;font-size:1.1rem;max-width:680px;margin:0 auto 26px;}
            .cta{display:inline-block;background:#c9a227;color:#0a0a0a;padding:14px 30px;border-radius:7px;font-weight:700;text-decoration:none;font-size:1rem;}
            .cta.sec{background:transparent;color:#c9a227;border:1px solid #c9a227;margin-left:10px;}
            h2{color:#c9a227;font-size:1.4rem;margin:40px 0 16px;}
            .steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:18px;margin:10px 0;}
            .step{background:#161616;border:1px solid #2a2a2a;border-radius:10px;padding:20px;}
            .step b{color:#e8c766;display:block;margin-bottom:6px;}
            .uses{list-style:none;display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:10px;}
            .uses li{background:#161616;border:1px solid #2a2a2a;border-radius:8px;padding:12px 14px;color:#ccc;}
            .faq dt{color:#e8c766;font-weight:700;margin-top:18px;}
            .faq dd{color:#bdbdbd;margin:4px 0 0;}
            .pricebar{background:#161616;border:1px solid #2a2a2a;border-radius:10px;padding:20px;text-align:center;margin-top:16px;}
            .disclaimer{color:#777;font-size:.85rem;border-top:1px solid #222;margin-top:40px;padding:20px 0 60px;}
            a{color:#c9a227;}
            .top{background:#111;border-bottom:1px solid #222;}
            .top .container{display:flex;gap:18px;align-items:center;padding:12px 20px;flex-wrap:wrap;}
            .top a{text-decoration:none;}
        </style>
    </head>
    <body>
        <div class="top"><div class="container">
            <a href="<?php echo esc_url(home_url('/')); ?>" style="color:#e0e0e0;font-weight:700;">IIS Inc.</a>
            <a href="<?php echo esc_url(home_url('/ip-trace/')); ?>">IP Trace</a>
            <a href="<?php echo esc_url(home_url('/national-trace/')); ?>">NationalTrace</a>
            <a href="<?php echo esc_url($login); ?>" style="margin-left:auto;">Sign in</a>
        </div></div>
        <div class="container">
            <div class="hero">
                <h1>Reveal the hidden data inside any photo or document</h1>
                <p>Metadata Forensics from Investigative Intelligence Solutions Inc. extracts the information most people never see — GPS location, the device that captured a photo, the author of a document, editing software and timestamps — and turns it into a clear evidence report. Upload a file and see what it reveals in seconds.</p>
                <a class="cta" href="<?php echo esc_url($register); ?>">Create an account — first report free</a>
                <a class="cta sec" href="<?php echo esc_url($login); ?>">Sign in</a>
            </div>

            <h2>How it works</h2>
            <div class="steps">
                <div class="step"><b>1. Upload a file</b>A photo or a document — JPG, PNG, HEIC, TIFF, PDF, Word, Excel or PowerPoint.</div>
                <div class="step"><b>2. We extract the metadata</b>EXIF and GPS from images, author and timestamps from documents, plus file hashes for chain of custody.</div>
                <div class="step"><b>3. Read the report</b>A plain-language summary of what the file reveals, with a downloadable evidence report you can keep or share.</div>
            </div>

            <h2>What it helps with</h2>
            <ul class="uses">
                <li>Finding where and when a photo was really taken</li>
                <li>Checking whether an image or document was edited</li>
                <li>Verifying the authenticity of evidence or a submission</li>
                <li>Identifying the device or author behind a file</li>
                <li>Spotting metadata you want to strip before sharing</li>
                <li>Building a forensic report for a case</li>
            </ul>

            <div class="pricebar">
                <p style="color:#e0e0e0;">Your <strong>first report is free.</strong> After that: US$10 per report, or US$100 for unlimited reports for a year.</p>
                <p style="margin-top:14px;"><a class="cta" href="<?php echo esc_url($register); ?>">Start free</a></p>
            </div>

            <h2>Frequently asked questions</h2>
            <dl class="faq">
                <?php foreach ($faq as $f): ?><dt><?php echo esc_html($f[0]); ?></dt><dd><?php echo esc_html($f[1]); ?></dd><?php endforeach; ?>
            </dl>

            <p class="disclaimer">
                Metadata is reported exactly as embedded in the file; its absence does not prove a file's origin, and precise GPS appears only when the file carries it. Files you upload are analysed privately and are never shared. Account required. &nbsp;·&nbsp; Investigative Intelligence Solutions Inc., Reg. No. 14926, Georgetown, Guyana &nbsp;·&nbsp; <a href="mailto:contact@iis.gy">contact@iis.gy</a> &nbsp;·&nbsp; +592-623-1721
            </p>
        </div>
    </body>
    </html>
    <?php
}

function iis_meta_render_page(): void {
    if (!is_user_logged_in()) { iis_meta_render_public_landing(); return; }
    $user_id = get_current_user_id();

    // If the POST body exceeded post_max_size, PHP silently drops $_POST (incl. the
    // nonce) AND $_FILES, so the normal handler never runs. Detect that and tell the
    // user clearly instead of letting it fail silently.
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && empty($_POST) && empty($_FILES) && !empty($_SERVER['CONTENT_LENGTH'])) {
        $cap = iis_meta_max_bytes($user_id);
        $msg = 'That file is too large to upload. Your limit is ' . size_format($cap) . '.'
             . (iis_meta_is_paid($user_id) ? '' : ' Paid plans allow up to ' . size_format(wp_max_upload_size()) . ' — upgrade to analyse larger files.');
        wp_redirect('/metadata/?error=' . urlencode($msg)); exit;
    }

    if ($_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce($_POST['_wpnonce'] ?? '', 'iis_meta_upload')) {
        iis_meta_handle_upload($user_id);
    }
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce($_POST['_wpnonce'] ?? '', 'iis_meta_delete')) {
        iis_meta_client_delete($user_id);
    }

    $view = isset($_GET['report']) ? intval($_GET['report']) : 0;
    if ($view > 0) { iis_meta_render_report($user_id, $view); return; }

    $quota = iis_meta_get_quota($user_id);
    $stripe_on = iis_meta_stripe_available();
    $mmg_on = iis_meta_mmg_available();
    $pay_nonce = wp_create_nonce('iis_meta_pay');
    global $wpdb; $t = iis_meta_tables();
    $reports = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$t['reports']} WHERE user_id=%d ORDER BY created_at DESC", $user_id));

    iis_meta_head('Metadata Forensics | IIS Inc.');
    ?>
        <h1>Metadata Forensics</h1>
        <p class="subtitle">Upload a photo or document to reveal its hidden data — location, device, author and timestamps.</p>

        <?php if (isset($_GET['error'])): ?><div class="error"><?php echo esc_html(wp_unslash($_GET['error'])); ?></div><?php endif; ?>
        <?php if (isset($_GET['deleted'])): ?><div class="success">Report deleted.</div><?php endif; ?>
        <?php if (isset($_GET['purchase']) && $_GET['purchase']==='success'): ?><div class="success">Payment received. Your account is being credited — refresh in a moment if your quota has not updated.</div>
        <?php elseif (isset($_GET['purchase']) && $_GET['purchase']==='cancelled'): ?><div class="error">Payment cancelled. You have not been charged.</div><?php endif; ?>

        <div class="card">
            <h2>Your Quota</h2>
            <div class="quota-bar"><div class="quota-fill" style="width:<?php echo $quota['has_unlimited']?100:min(100,($quota['used']/max($quota['total'],1))*100); ?>%"></div></div>
            <p class="quota-text"><?php echo $quota['has_unlimited'] ? ('Unlimited plan active' . ($quota['unlimited_expires']?' — expires '.esc_html(date('M j, Y', strtotime($quota['unlimited_expires']))):'')) : ($quota['used'].' of '.$quota['total'].' reports used'); ?></p>
            <?php if (!$quota['has_unlimited'] && ($stripe_on || $mmg_on)): ?>
                <p class="help-text">Go Unlimited — $100 / GYD 20,000 for a year of unlimited reports.</p>
                <?php iis_meta_pay_buttons('unlimited', $stripe_on, $mmg_on); ?>
            <?php endif; ?>
        </div>

        <?php if ($quota['can_create']): ?>
            <div class="card">
                <h2>Analyse a file</h2>
                <?php $my_cap = iis_meta_max_bytes($user_id); ?>
                <form method="POST" enctype="multipart/form-data" id="iis-meta-form" data-cap="<?php echo (int)$my_cap; ?>" data-paid="<?php echo iis_meta_is_paid($user_id) ? 1 : 0; ?>" data-ceiling="<?php echo (int)wp_max_upload_size(); ?>">
                    <?php wp_nonce_field('iis_meta_upload'); ?>
                    <input type="file" name="file" id="iis-meta-file" required accept=".jpg,.jpeg,.png,.gif,.tif,.tiff,.webp,.heic,.pdf,.docx,.xlsx,.pptx">
                    <div id="iis-meta-filemsg" class="error" style="display:none;margin-top:12px;"></div>
                    <p class="help-text">Images (JPG, PNG, GIF, TIFF, WEBP, HEIC) and documents (PDF, DOCX, XLSX, PPTX). Your limit: <strong><?php echo esc_html(size_format($my_cap)); ?></strong><?php if (!iis_meta_is_paid($user_id)): ?> on the free tier — paid plans allow up to <?php echo esc_html(size_format(wp_max_upload_size())); ?><?php endif; ?>. Your file is analysed privately and never shared.</p>
                    <p style="margin-top:14px;"><button type="submit">Reveal metadata</button></p>
                </form>
            </div>
        <?php else: ?>
            <div class="card" style="text-align:center;padding:36px;">
                <h2>You've used your free report</h2>
                <p>Purchase more to continue analysing files.</p>
                <?php if (!$stripe_on && !$mmg_on): ?>
                    <p class="help-text" style="margin-top:18px;">Online payment is being configured. Contact IIS at contact@iis.gy or +592-623-1721.</p>
                <?php else: ?>
                    <div class="price-cards">
                        <div class="price-card"><div class="price">$10</div><p class="price-note">1 additional report<br><span style="color:#666;">GYD 2,000</span></p><?php iis_meta_pay_buttons('single',$stripe_on,$mmg_on); ?></div>
                        <div class="price-card"><div class="price">$100</div><p class="price-note">Unlimited reports for 1 year<br><span style="color:#666;">GYD 20,000</span></p><?php iis_meta_pay_buttons('unlimited',$stripe_on,$mmg_on); ?></div>
                    </div>
                <?php endif; ?>
            </div>
        <?php endif; ?>

        <div class="card">
            <h2>Your Reports</h2>
            <?php if (!$reports): ?>
                <p style="color:#888;">No reports yet. Upload a file above to begin.</p>
            <?php else: foreach ($reports as $r): ?>
                <div class="rep-item">
                    <div>
                        <div style="color:#e0e0e0;font-weight:600;"><?php echo esc_html($r->original_name); ?> <?php if ($r->has_gps): ?><span class="gps-tag">GPS</span><?php endif; ?></div>
                        <div style="color:#777;font-size:.82rem;">#<?php echo (int)$r->id; ?> · <?php echo esc_html($r->mime); ?> · <?php echo esc_html($r->created_at); ?> UTC</div>
                    </div>
                    <div style="display:flex;gap:8px;align-items:center;">
                        <a href="/metadata/?report=<?php echo (int)$r->id; ?>"><button type="button" class="secondary">View</button></a>
                        <form method="POST" onsubmit="return confirm('Delete this report and its stored file?');"><?php wp_nonce_field('iis_meta_delete'); ?>
                            <input type="hidden" name="report_id" value="<?php echo (int)$r->id; ?>">
                            <button type="submit" class="secondary" style="border-color:#e74c3c;color:#e74c3c;">Delete</button></form>
                    </div>
                </div>
            <?php endforeach; endif; ?>
        </div>
    </div>
    <script>
        (function(){
            var form = document.getElementById('iis-meta-form');
            if (!form) return;
            var input = document.getElementById('iis-meta-file');
            var msg = document.getElementById('iis-meta-filemsg');
            var cap = parseInt(form.getAttribute('data-cap'), 10) || 0;
            var paid = form.getAttribute('data-paid') === '1';
            var ceiling = parseInt(form.getAttribute('data-ceiling'), 10) || cap;
            function fmt(b){ if(b>=1048576) return (b/1048576).toFixed(b%1048576?1:0)+' MB'; if(b>=1024) return Math.round(b/1024)+' KB'; return b+' B'; }
            function tooBig(){
                if (!input.files || !input.files.length) return false;
                return input.files[0].size > cap;
            }
            function show(){
                if (tooBig()) {
                    msg.textContent = 'That file is ' + fmt(input.files[0].size) + ', above your ' + fmt(cap) + ' limit.' + (paid ? '' : ' Paid plans allow up to ' + fmt(ceiling) + ' — upgrade to analyse larger files.');
                    msg.style.display = 'block';
                } else { msg.style.display = 'none'; }
            }
            input.addEventListener('change', show);
            form.addEventListener('submit', function(e){ if (tooBig()) { e.preventDefault(); show(); } });
        })();
        window.IIS_META_AJAX = <?php echo wp_json_encode(admin_url('admin-ajax.php')); ?>;
        window.IIS_META_NONCE = <?php echo wp_json_encode($pay_nonce); ?>;
        function iisMetaPay(method, sku, btn){
            var orig=btn.textContent; btn.disabled=true; btn.textContent='Starting…';
            var body=new URLSearchParams({action:'iis_meta_'+method+'_checkout',nonce:window.IIS_META_NONCE,sku:sku});
            fetch(window.IIS_META_AJAX,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body})
              .then(function(r){return r.json();}).then(function(d){
                if(d&&d.success&&d.data&&d.data.redirect_url){window.location.href=d.data.redirect_url;return;}
                alert((d&&d.data&&d.data.message)?d.data.message:'Payment could not be started.'); btn.disabled=false; btn.textContent=orig;
              }).catch(function(){alert('Connection error. Please try again.'); btn.disabled=false; btn.textContent=orig;});
        }
    </script>
    </body></html>
    <?php
}

function iis_meta_pay_buttons(string $sku, bool $stripe_on, bool $mmg_on): void {
    echo '<div style="display:flex;flex-direction:column;gap:8px;margin-top:14px;">';
    if ($stripe_on) echo '<button type="button" onclick="iisMetaPay(\'stripe\',\'' . esc_js($sku) . '\',this)">Pay by Card</button>';
    if ($mmg_on) echo '<button type="button" class="secondary" onclick="iisMetaPay(\'mmg\',\'' . esc_js($sku) . '\',this)">Pay with MMG</button>';
    echo '</div>';
}

function iis_meta_render_report(int $user_id, int $rid): void {
    global $wpdb; $t = iis_meta_tables();
    $r = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$t['reports']} WHERE id=%d AND user_id=%d", $rid, $user_id));
    if (!$r) { wp_die('Report not found.', 'MetaTrace', ['response' => 404]); }
    $data = json_decode($r->meta ?: '{}', true); if (!is_array($data)) $data = [];
    iis_meta_head('Report — ' . $r->original_name);
    ?>
        <a href="/metadata/" class="back-link">← Back to Metadata Forensics</a>
        <h1>Metadata Report</h1>
        <p class="subtitle"><?php echo esc_html($r->original_name); ?> · <?php echo esc_html($r->created_at); ?> UTC</p>

        <div class="card">
            <h2>What this file reveals</h2>
            <?php foreach (($data['findings'] ?? []) as $f):
                $clean = stripos($f, 'no sensitive') !== false; ?>
                <div class="finding <?php echo $clean?'clean':''; ?>"><?php echo esc_html($f); ?></div>
            <?php endforeach; ?>
            <?php if (!empty($data['gps'])): ?>
                <p style="margin-top:10px;"><a href="https://www.google.com/maps?q=<?php echo esc_attr($data['gps'][0].','.$data['gps'][1]); ?>" target="_blank">📍 Open location on Google Maps</a></p>
            <?php endif; ?>
        </div>

        <?php foreach (($data['sections'] ?? []) as $sec => $rows): ?>
            <div class="card">
                <h2><?php echo esc_html($sec); ?></h2>
                <table><tbody>
                <?php foreach ((array)$rows as $k => $v): ?>
                    <tr><th><?php echo esc_html($k); ?></th><td><?php
                        if (filter_var($v, FILTER_VALIDATE_URL)) echo '<a href="'.esc_url($v).'" target="_blank">'.esc_html($v).'</a>';
                        else echo esc_html((string)$v);
                    ?></td></tr>
                <?php endforeach; ?>
                </tbody></table>
            </div>
        <?php endforeach; ?>

        <p style="color:#555;font-size:.8rem;">SHA-256 <?php echo esc_html($r->sha256); ?></p>
    </div></body></html>
    <?php
}

/* ================================================================== admin */
add_action('admin_menu', function() {
    add_menu_page('MetaTrace', 'MetaTrace', 'manage_options', 'iis-meta', 'iis_meta_admin_router', 'dashicons-media-document', 58);
});

function iis_meta_admin_guard(string $a): void { if (!current_user_can('manage_options')) wp_die('Not allowed.'); check_admin_referer($a); }
function iis_meta_audit(string $action, string $target='', string $detail=''): void {
    global $wpdb; $t = iis_meta_tables();
    $wpdb->insert($t['audit'], ['admin_id'=>get_current_user_id(),'action'=>$action,'target'=>substr($target,0,191),'detail'=>$detail,'created_at'=>current_time('mysql')]);
}
function iis_meta_admin_back(string $msg='', string $tab='reports'): void {
    $url = admin_url('admin.php?page=iis-meta&tab=' . $tab);
    if ($msg) $url = add_query_arg('iis_notice', rawurlencode($msg), $url);
    wp_safe_redirect($url); exit;
}

add_action('admin_post_iis_meta_grant', function() {
    iis_meta_admin_guard('iis_meta_grant');
    $uid = intval($_POST['user_id'] ?? 0); $n = max(1, min(1000, intval($_POST['count'] ?? 1)));
    if ($uid) { update_user_meta($uid, 'iis_meta_paid', (int)get_user_meta($uid,'iis_meta_paid',true) + $n); iis_meta_audit('grant_reports', "user:$uid", "+{$n}"); }
    iis_meta_admin_back("Granted {$n} report(s).", 'clients');
});
add_action('admin_post_iis_meta_grant_unlimited', function() {
    iis_meta_admin_guard('iis_meta_grant_unlimited');
    $uid = intval($_POST['user_id'] ?? 0); $mo = max(1, min(60, intval($_POST['months'] ?? 12)));
    if ($uid) { update_user_meta($uid,'iis_meta_unlimited',1); update_user_meta($uid,'iis_meta_unlimited_expires',date('Y-m-d H:i:s',strtotime("+{$mo} months"))); iis_meta_audit('grant_unlimited',"user:$uid","{$mo}mo"); }
    iis_meta_admin_back('Unlimited granted.', 'clients');
});
add_action('admin_post_iis_meta_revoke_unlimited', function() {
    iis_meta_admin_guard('iis_meta_revoke_unlimited');
    $uid = intval($_POST['user_id'] ?? 0);
    if ($uid) { delete_user_meta($uid,'iis_meta_unlimited'); delete_user_meta($uid,'iis_meta_unlimited_expires'); iis_meta_audit('revoke_unlimited',"user:$uid"); }
    iis_meta_admin_back('Unlimited revoked.', 'clients');
});
add_action('admin_post_iis_meta_delete_report', function() {
    iis_meta_admin_guard('iis_meta_delete_report');
    global $wpdb; $t = iis_meta_tables();
    $rid = intval($_POST['report_id'] ?? 0);
    if ($rid) {
        $r = $wpdb->get_row($wpdb->prepare("SELECT stored_path FROM {$t['reports']} WHERE id=%d", $rid));
        if ($r && $r->stored_path) { $s = iis_meta_storage_dir(); @unlink($s['dir'] . '/' . $r->stored_path); }
        $wpdb->delete($t['reports'], ['id'=>$rid], ['%d']);
        iis_meta_audit('delete_report', "report:$rid");
    }
    iis_meta_admin_back('Report deleted.', 'reports');
});

function iis_meta_admin_router(): void {
    if (!current_user_can('manage_options')) wp_die('Not allowed.');
    $tab = sanitize_key($_GET['tab'] ?? 'overview');
    $view = intval($_GET['view'] ?? 0);
    echo '<div class="wrap"><h1 style="display:flex;align-items:center;gap:8px;"><span class="dashicons dashicons-media-document" style="font-size:26px;width:26px;height:26px;"></span> MetaTrace</h1>';
    if (isset($_GET['iis_notice'])) echo '<div class="notice notice-success is-dismissible"><p>'.esc_html(wp_unslash($_GET['iis_notice'])).'</p></div>';
    $tabs = ['overview'=>'Dashboard','reports'=>'Reports','clients'=>'Clients','payments'=>'Payments','audit'=>'Audit Log'];
    echo '<h2 class="nav-tab-wrapper">';
    foreach ($tabs as $k=>$l) printf('<a class="nav-tab%s" href="%s">%s</a>', ($tab===$k&&!$view)?' nav-tab-active':'', esc_url(admin_url('admin.php?page=iis-meta&tab='.$k)), esc_html($l));
    echo '</h2><div style="margin-top:16px;">';
    if ($view) iis_meta_admin_view_report($view);
    elseif ($tab==='reports') iis_meta_admin_reports();
    elseif ($tab==='clients') iis_meta_admin_clients();
    elseif ($tab==='payments') iis_meta_admin_payments();
    elseif ($tab==='audit') iis_meta_admin_audit();
    else iis_meta_admin_overview();
    echo '</div></div>';
}

function iis_meta_admin_cards(array $cards): void {
    echo '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:14px;margin:4px 0 24px;">';
    foreach ($cards as $c) echo '<div style="background:#fff;border:1px solid #dcdcde;border-radius:8px;padding:16px 18px;"><div style="font-size:12px;text-transform:uppercase;letter-spacing:.05em;color:#646970;">'.esc_html($c[0]).'</div><div style="font-size:26px;font-weight:700;color:#1d2327;margin-top:4px;">'.esc_html($c[1]).'</div>'.(isset($c[2])?'<div style="font-size:12px;color:#787c82;margin-top:2px;">'.esc_html($c[2]).'</div>':'').'</div>';
    echo '</div>';
}

function iis_meta_admin_overview(): void {
    global $wpdb; $t = iis_meta_tables();
    $reports = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$t['reports']}");
    $gps = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$t['reports']} WHERE has_gps=1");
    $clients = (int)$wpdb->get_var("SELECT COUNT(DISTINCT user_id) FROM {$t['reports']}");
    $r7 = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$t['reports']} WHERE created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 7 DAY)");
    $logs = $wpdb->get_col("SELECT meta_value FROM {$wpdb->usermeta} WHERE meta_key='iis_meta_payment_log'");
    $usd=0; foreach($logs as $raw){ foreach((array)maybe_unserialize($raw) as $p) $usd += (int)($p['usd']??0); }
    $unl = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->usermeta} WHERE meta_key='iis_meta_unlimited' AND meta_value='1'");
    iis_meta_admin_cards([
        ['Reports', number_format($reports), $r7.' in last 7 days'],
        ['With GPS', number_format($gps), $reports?round($gps/$reports*100).'% of files':''],
        ['Clients', number_format($clients)],
        ['Unlimited plans', number_format($unl)],
        ['Revenue', '$'.number_format($usd)],
    ]);
    echo '<p style="color:#646970;">Recent reports:</p>';
    iis_meta_admin_reports(10);
}

function iis_meta_admin_reports(int $limit = 0): void {
    global $wpdb; $t = iis_meta_tables();
    $f_gps = !empty($_GET['f_gps']); $s = sanitize_text_field($_GET['s'] ?? '');
    if (!$limit) {
        echo '<form method="get" style="margin-bottom:12px;"><input type="hidden" name="page" value="iis-meta"><input type="hidden" name="tab" value="reports">';
        echo '<input type="search" name="s" value="'.esc_attr($s).'" placeholder="Search filename"> <label><input type="checkbox" name="f_gps" value="1" '.checked($f_gps,true,false).'> GPS only</label> <button class="button">Filter</button>';
        if ($s!=='' || $f_gps) echo ' <a class="button" href="'.esc_url(admin_url('admin.php?page=iis-meta&tab=reports')).'">Reset</a>';
        echo '</form>';
    }
    $where = ['1=1']; $args=[];
    if ($f_gps) $where[] = 'r.has_gps=1';
    if ($s !== '') { $where[]='r.original_name LIKE %s'; $args[]='%'.$wpdb->esc_like($s).'%'; }
    $wsql = implode(' AND ', $where);
    $lim = $limit ? ('LIMIT '.(int)$limit) : 'LIMIT 500';
    $sql = "SELECT r.*, u.user_login, u.display_name FROM {$t['reports']} r LEFT JOIN {$wpdb->users} u ON r.user_id=u.ID WHERE {$wsql} ORDER BY r.created_at DESC {$lim}";
    $rows = $args ? $wpdb->get_results($wpdb->prepare($sql,$args)) : $wpdb->get_results($sql);
    echo '<table class="wp-list-table widefat fixed striped"><thead><tr><th>When (UTC)</th><th>Client</th><th>File</th><th>Type</th><th>GPS</th><th>Findings</th><th></th></tr></thead><tbody>';
    if (!$rows) echo '<tr><td colspan="7">No reports.</td></tr>';
    foreach ($rows as $r) {
        echo '<tr><td>'.esc_html($r->created_at).'</td><td>'.esc_html($r->display_name ?: $r->user_login).'</td>'
           .'<td>'.esc_html($r->original_name).'</td><td>'.esc_html($r->mime).'</td>'
           .'<td>'.($r->has_gps?'<span style="color:#00844a;font-weight:700;">YES</span>':'—').'</td><td>'.(int)$r->findings_count.'</td>'
           .'<td><a class="button button-small button-primary" href="'.esc_url(admin_url('admin.php?page=iis-meta&view='.(int)$r->id)).'">View</a></td></tr>';
    }
    echo '</tbody></table>';
}

function iis_meta_admin_view_report(int $rid): void {
    global $wpdb; $t = iis_meta_tables();
    $r = $wpdb->get_row($wpdb->prepare("SELECT r.*, u.user_login, u.display_name, u.user_email FROM {$t['reports']} r LEFT JOIN {$wpdb->users} u ON r.user_id=u.ID WHERE r.id=%d", $rid));
    if (!$r) { echo '<p>Report not found.</p>'; return; }
    $data = json_decode($r->meta ?: '{}', true); if (!is_array($data)) $data=[];
    echo '<p><a href="'.esc_url(admin_url('admin.php?page=iis-meta&tab=reports')).'">&larr; All reports</a></p>';
    echo '<h2>'.esc_html($r->original_name).'</h2>';
    echo '<p>Client: '.esc_html(($r->display_name ?: $r->user_login).' ('.$r->user_email.')').' · '.esc_html($r->created_at).' UTC · '.esc_html($r->mime).'</p>';
    echo '<p><a class="button button-primary" target="_blank" href="'.esc_url(wp_nonce_url(admin_url('admin-post.php?action=iis_meta_report_pdf&report_id='.$rid), 'iis_meta_report_pdf')).'">Evidence report (print → PDF)</a> ';
    echo '<form method="post" action="'.esc_url(admin_url('admin-post.php')).'" style="display:inline;" onsubmit="return confirm(\'Delete this report and its stored file?\');">';
    wp_nonce_field('iis_meta_delete_report');
    echo '<input type="hidden" name="action" value="iis_meta_delete_report"><input type="hidden" name="report_id" value="'.$rid.'"><button class="button button-link-delete">Delete report</button></form></p>';
    if (!empty($data['findings'])) { echo '<h3>Findings</h3><ul style="list-style:disc;margin-left:20px;">'; foreach ($data['findings'] as $f) echo '<li>'.esc_html($f).'</li>'; echo '</ul>'; }
    foreach (($data['sections'] ?? []) as $sec=>$rowsx) {
        echo '<h3>'.esc_html($sec).'</h3><table class="wp-list-table widefat striped" style="max-width:720px;"><tbody>';
        foreach ((array)$rowsx as $k=>$v) echo '<tr><th style="width:38%;">'.esc_html($k).'</th><td>'.(filter_var($v,FILTER_VALIDATE_URL)?'<a href="'.esc_url($v).'" target="_blank">'.esc_html($v).'</a>':esc_html((string)$v)).'</td></tr>';
        echo '</tbody></table>';
    }
    echo '<p style="color:#787c82;margin-top:12px;">SHA-256 '.esc_html($r->sha256).' · MD5 '.esc_html($r->md5).'</p>';
}

function iis_meta_admin_clients(): void {
    global $wpdb; $t = iis_meta_tables();
    $ids = $wpdb->get_col("SELECT DISTINCT user_id FROM {$t['reports']} ORDER BY user_id DESC");
    // also include anyone with a paid/unlimited meta
    $extra = $wpdb->get_col("SELECT DISTINCT user_id FROM {$wpdb->usermeta} WHERE meta_key IN ('iis_meta_paid','iis_meta_unlimited')");
    $ids = array_unique(array_merge(array_map('intval',$ids), array_map('intval',$extra)));
    echo '<table class="wp-list-table widefat fixed striped"><thead><tr><th>Client</th><th>Quota</th><th>Reports</th><th>Manage</th></tr></thead><tbody>';
    if (!$ids) echo '<tr><td colspan="4">No clients yet.</td></tr>';
    foreach ($ids as $uid) {
        $u = get_userdata($uid); if (!$u) continue;
        $q = iis_meta_get_quota($uid);
        $n = (int)$wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$t['reports']} WHERE user_id=%d", $uid));
        $quota = $q['has_unlimited'] ? ('Unlimited'.($q['unlimited_expires']?' → '.date('M j, Y',strtotime($q['unlimited_expires'])):'')) : ($q['used'].' / '.$q['total']);
        echo '<tr><td>'.esc_html($u->display_name ?: $u->user_login).'<br><span style="color:#787c82;">'.esc_html($u->user_email).'</span></td><td>'.esc_html($quota).'</td><td>'.$n.'</td><td style="white-space:nowrap;">';
        echo '<form method="post" action="'.esc_url(admin_url('admin-post.php')).'" style="display:inline;">';
        wp_nonce_field('iis_meta_grant');
        echo '<input type="hidden" name="action" value="iis_meta_grant"><input type="hidden" name="user_id" value="'.$uid.'"><input type="hidden" name="count" value="1"><button class="button button-small">+1 report</button></form> ';
        if ($q['has_unlimited']) {
            echo '<form method="post" action="'.esc_url(admin_url('admin-post.php')).'" style="display:inline;" onsubmit="return confirm(\'Revoke unlimited?\');">';
            wp_nonce_field('iis_meta_revoke_unlimited');
            echo '<input type="hidden" name="action" value="iis_meta_revoke_unlimited"><input type="hidden" name="user_id" value="'.$uid.'"><button class="button button-small">Revoke ∞</button></form>';
        } else {
            echo '<form method="post" action="'.esc_url(admin_url('admin-post.php')).'" style="display:inline;">';
            wp_nonce_field('iis_meta_grant_unlimited');
            echo '<input type="hidden" name="action" value="iis_meta_grant_unlimited"><input type="hidden" name="user_id" value="'.$uid.'"><input type="hidden" name="months" value="12"><button class="button button-small">Grant ∞ 1yr</button></form>';
        }
        echo '</td></tr>';
    }
    echo '</tbody></table>';
}

function iis_meta_admin_payments(): void {
    global $wpdb;
    $rows = $wpdb->get_results("SELECT user_id, meta_value FROM {$wpdb->usermeta} WHERE meta_key='iis_meta_payment_log'");
    $pays=[]; foreach($rows as $r){ foreach((array)maybe_unserialize($r->meta_value) as $p){ $p['user_id']=(int)$r->user_id; $pays[]=$p; } }
    usort($pays, fn($a,$b)=>strcmp($b['at']??'',$a['at']??''));
    $total = array_sum(array_map(fn($p)=>(int)($p['usd']??0), $pays));
    iis_meta_admin_cards([['Payments', number_format(count($pays))],['Revenue','$'.number_format($total)]]);
    echo '<table class="wp-list-table widefat fixed striped"><thead><tr><th>When</th><th>Client</th><th>Item</th><th>Method</th><th>Amount</th><th>Reference</th></tr></thead><tbody>';
    if (!$pays) echo '<tr><td colspan="6">No payments yet.</td></tr>';
    foreach ($pays as $p) { $u=get_userdata($p['user_id']);
        echo '<tr><td>'.esc_html($p['at']??'').'</td><td>'.esc_html($u?($u->display_name?:$u->user_login):('#'.$p['user_id'])).'</td>'
           .'<td>'.esc_html(($p['type']??'')==='unlimited'?'Unlimited (1yr)':'Single report').'</td><td>'.esc_html(strtoupper($p['method']??'')).'</td>'
           .'<td>$'.esc_html((string)($p['usd']??0)).' / GYD '.esc_html(number_format((int)($p['gyd']??0))).'</td><td style="color:#787c82;word-break:break-all;">'.esc_html($p['ref']??'').'</td></tr>';
    }
    echo '</tbody></table>';
}

function iis_meta_admin_audit(): void {
    global $wpdb; $t = iis_meta_tables();
    $rows = $wpdb->get_results("SELECT a.*, u.user_login FROM {$t['audit']} a LEFT JOIN {$wpdb->users} u ON a.admin_id=u.ID ORDER BY a.id DESC LIMIT 500");
    echo '<table class="wp-list-table widefat fixed striped"><thead><tr><th>When (UTC)</th><th>Admin</th><th>Action</th><th>Target</th><th>Detail</th></tr></thead><tbody>';
    if (!$rows) echo '<tr><td colspan="5">No admin actions logged yet.</td></tr>';
    foreach ($rows as $r) echo '<tr><td>'.esc_html($r->created_at).'</td><td>'.esc_html($r->user_login ?: ('#'.$r->admin_id)).'</td><td><code>'.esc_html($r->action).'</code></td><td>'.esc_html($r->target).'</td><td style="color:#787c82;">'.esc_html($r->detail).'</td></tr>';
    echo '</tbody></table>';
}

/* Branded print-to-PDF evidence report */
add_action('admin_post_iis_meta_report_pdf', function() {
    if (!current_user_can('manage_options')) wp_die('Not allowed.');
    check_admin_referer('iis_meta_report_pdf');
    global $wpdb; $t = iis_meta_tables();
    $rid = intval($_GET['report_id'] ?? 0);
    $r = $wpdb->get_row($wpdb->prepare("SELECT r.*, u.display_name, u.user_email FROM {$t['reports']} r LEFT JOIN {$wpdb->users} u ON r.user_id=u.ID WHERE r.id=%d", $rid));
    if (!$r) wp_die('Report not found.');
    $data = json_decode($r->meta ?: '{}', true); if (!is_array($data)) $data=[];
    $admin = wp_get_current_user();
    ?><!DOCTYPE html><html><head><meta charset="utf-8"><title>MetaTrace Evidence Report #<?php echo (int)$rid; ?></title>
    <style>@page{margin:18mm;}body{font-family:'Segoe UI',Arial,sans-serif;color:#111;font-size:12px;line-height:1.5;}
    .head{display:flex;justify-content:space-between;border-bottom:3px solid #c9a227;padding-bottom:10px;margin-bottom:16px;}
    .brand{font-size:20px;font-weight:800;}.brand span{color:#c9a227;}.muted{color:#555;font-size:11px;}
    h1{font-size:16px;margin:16px 0 6px;}h2{font-size:13px;margin:16px 0 6px;border-bottom:1px solid #ccc;padding-bottom:3px;}
    table{width:100%;border-collapse:collapse;margin:8px 0 14px;}th,td{border:1px solid #ccc;padding:6px 8px;text-align:left;font-size:11px;vertical-align:top;}th{background:#f3f1ea;width:34%;}
    .btns{margin:12px 0 18px;}.btns button{background:#c9a227;border:0;color:#0a0a0a;padding:8px 16px;font-weight:700;border-radius:4px;cursor:pointer;}@media print{.btns{display:none;}}
    .foot{margin-top:22px;border-top:1px solid #ccc;padding-top:8px;color:#555;font-size:10px;}</style></head><body>
    <div class="btns"><button onclick="window.print()">Print / Save as PDF</button></div>
    <div class="head"><div><div class="brand">IIS<span>.</span> Investigative Intelligence Solutions Inc.</div>
    <div class="muted">Licensed Private Investigation Firm · Reg. No. 14926 · Georgetown, Guyana · iis.gy · contact@iis.gy · +592-623-1721</div></div>
    <div class="muted" style="text-align:right;">Generated<br><?php echo esc_html(current_time('mysql')); ?> UTC<br>by <?php echo esc_html($admin->display_name); ?></div></div>
    <h1>Document Metadata Evidence Report</h1>
    <table><tr><th>Report ID</th><td>#<?php echo (int)$rid; ?></td></tr>
    <tr><th>Client</th><td><?php echo esc_html(($r->display_name ?? '').' ('.($r->user_email ?? '').')'); ?></td></tr>
    <tr><th>File</th><td><?php echo esc_html($r->original_name); ?></td></tr>
    <tr><th>Type</th><td><?php echo esc_html($r->mime); ?></td></tr>
    <tr><th>Analysed</th><td><?php echo esc_html($r->created_at); ?> UTC</td></tr>
    <tr><th>SHA-256</th><td style="font-family:monospace;font-size:10px;"><?php echo esc_html($r->sha256); ?></td></tr>
    <tr><th>MD5</th><td style="font-family:monospace;font-size:10px;"><?php echo esc_html($r->md5); ?></td></tr></table>
    <h2>Findings</h2><ul><?php foreach (($data['findings'] ?? []) as $f) echo '<li>'.esc_html($f).'</li>'; ?></ul>
    <?php foreach (($data['sections'] ?? []) as $sec=>$rowsx): ?>
    <h2><?php echo esc_html($sec); ?></h2><table><?php foreach ((array)$rowsx as $k=>$v) echo '<tr><th>'.esc_html($k).'</th><td>'.esc_html((string)$v).'</td></tr>'; ?></table>
    <?php endforeach; ?>
    <div class="foot">This report was generated by the IIS MetaTrace system directly from the submitted file. The SHA-256 and MD5 hashes identify the exact file analysed. Metadata is reported as embedded in the file; absence of metadata does not prove a file's origin. Prepared for investigative and law-enforcement purposes.</div>
    </body></html><?php
    exit;
});
