<?php
/**
 * Bootstrap — Global license verification orchestrator
 * 
 * Include this file in every entry point (index.php, cronjobs, etc.)
 * It coordinates all verification layers and blocks execution if anything fails.
 * 
 * Verification chain:
 *   1. shield.php  → file integrity checks
 *   2. cipher.php  → decrypt license.dat
 *   3. sentinel.php → validate binding hash + domain + IP
 *   4. DB check    → verify system_integrity table
 *   5. Remote API  → periodic re-validation (6h cache)
 * 
 * If ANY step fails → _shield_lock() → "This installation is not authorized."
 */

if (!defined('BASEPATH')) { http_response_code(403); exit; }

// ─── Configuration ──────────────────────────────────────────────────
define('_BOOTSTRAP_CACHE_TTL', 6 * 3600);  // 6 hours
define('_BOOTSTRAP_CURL_CONNECT', 5);       // 5s connect timeout
define('_BOOTSTRAP_CURL_TIMEOUT', 10);      // 10s total timeout

// ─── Paths ──────────────────────────────────────────────────────────
$_bsRoot = defined('PATH') ? PATH : dirname(__DIR__);
$_bsLicenseDat  = $_bsRoot . '/config/license.dat';
$_bsSystemLock  = $_bsRoot . '/config/system.lock';
$_bsCachePath   = $_bsRoot . '/storage/license_check_cache.json';
$_bsLogPath     = $_bsRoot . '/storage/logs/license_verify.log';

// ─── Step 0: Load core components ───────────────────────────────────
// These files contain the split validation logic.
// If any are missing, shield will catch it below.
$_bsCoreDir = __DIR__;
@include_once $_bsCoreDir . '/cipher.php';
@include_once $_bsCoreDir . '/sentinel.php';
@include_once $_bsCoreDir . '/shield.php';

// Verify core functions loaded (prevents bypass by emptying files)
if (!function_exists('_shield_lock') 
    || !function_exists('_cipher_read_license') 
    || !function_exists('_sentinel_verify')
) {
    http_response_code(403);
    echo 'This installation is not authorized.';
    exit;
}

// ─── Step 1: File integrity checks ─────────────────────────────────
if (!_shield_check_files()) {
    _shield_lock();
}

// ─── Step 2: Verify system.lock ────────────────────────────────────
$_bsLockData = _shield_verify_lock($_bsSystemLock);
if ($_bsLockData === false) {
    _shield_lock();
}

// ─── Step 3: Decrypt license.dat ───────────────────────────────────
$_bsLicense = _cipher_read_license($_bsLicenseDat);
if ($_bsLicense === false) {
    _shield_lock();
}

// Validate required license fields exist
if (empty($_bsLicense['license_key']) || empty($_bsLicense['bind_hash']) || empty($_bsLicense['api_url'])) {
    _shield_lock();
}

// ─── Step 4: Validate binding hash ─────────────────────────────────
$_bsDomain = _sentinel_current_domain();
$_bsIp = _sentinel_current_ip();

if (!_sentinel_verify(
    $_bsLicense['bind_hash'],
    $_bsDomain,
    $_bsIp,
    $_bsLicense['license_key']
)) {
    // Domain or IP changed — log and lock
    _bootstrap_log($_bsLogPath, "BIND MISMATCH: domain=$_bsDomain ip=$_bsIp");
    _shield_lock();
}

// ─── Step 5: DB integrity check ────────────────────────────────────
if (isset($conn) && $conn instanceof PDO) {
    if (!_shield_check_db_lock($conn)) {
        _bootstrap_log($_bsLogPath, "DB LOCK: system_integrity flagged");
        _shield_lock();
    }
}

// ─── Step 6: Remote API re-validation (cached) ─────────────────────
if (!_bootstrap_cache_valid($_bsCachePath)) {
    $apiResult = _bootstrap_remote_check(
        $_bsLicense['api_url'],
        $_bsLicense['license_key'],
        $_bsDomain,
        $_bsIp,
        $_bsLicense['product_slug'] ?? '',
        $_bsLicense['bind_hash'] ?? ''
    );
    
    if ($apiResult === true) {
        _bootstrap_update_cache($_bsCachePath, $_bsDomain);
    } else {
        _bootstrap_log($_bsLogPath, "API REJECTED: domain=$_bsDomain");
        _shield_lock();
    }
}

// ─── ALL CHECKS PASSED — Allow execution ────────────────────────────
// Clean up variables to avoid polluting global scope
unset($_bsRoot, $_bsLicenseDat, $_bsSystemLock, $_bsCachePath, $_bsLogPath);
unset($_bsCoreDir, $_bsLockData, $_bsLicense, $_bsDomain, $_bsIp);

return; // Proceed to normal application


// ═══════════════════════════════════════════════════════════════════
// ─── Helper functions (scoped to bootstrap) ─────────────────────────
// ═══════════════════════════════════════════════════════════════════

/**
 * Log a verification event.
 */
function _bootstrap_log($logPath, $message) {
    $dir = dirname($logPath);
    if (!is_dir($dir)) {
        @mkdir($dir, 0755, true);
    }
    $ts = date('Y-m-d H:i:s');
    @file_put_contents($logPath, "[$ts] $message\n", FILE_APPEND | LOCK_EX);
}

/**
 * Check if the local cache is still valid (within TTL).
 */
function _bootstrap_cache_valid($cachePath) {
    if (!file_exists($cachePath)) {
        return false;
    }
    
    $raw = @file_get_contents($cachePath);
    $cache = @json_decode($raw, true);
    
    if (!is_array($cache) || !isset($cache['status'], $cache['timestamp'])) {
        return false;
    }
    
    if ($cache['status'] !== 'valid') {
        return false;
    }
    
    return (time() - $cache['timestamp']) < _BOOTSTRAP_CACHE_TTL;
}

/**
 * Update the license verification cache.
 */
function _bootstrap_update_cache($cachePath, $domain) {
    $data = [
        'status'     => 'valid',
        'timestamp'  => time(),
        'domain'     => $domain,
        'checked_at' => date('Y-m-d H:i:s'),
    ];
    
    $dir = dirname($cachePath);
    if (!is_dir($dir)) {
        @mkdir($dir, 0755, true);
    }
    
    @file_put_contents($cachePath, json_encode($data, JSON_PRETTY_PRINT), LOCK_EX);
}

/**
 * Perform a remote API check against the license server.
 * 
 * @return bool True if valid, false if invalid or unreachable
 */
function _bootstrap_remote_check($apiUrl, $licenseKey, $domain, $ip, $productSlug, $localHash = '') {
    if (empty($apiUrl) || !function_exists('curl_init')) {
        return false; // Fail closed
    }
    
    // Convert stored activation URL to verify endpoint
    // e.g. .../api/license/activate.php → .../api/license/verify.php
    $verifyUrl = str_replace('activate.php', 'verify.php', $apiUrl);
    // Fallback: if stored URL is old-style license_check.php
    if ($verifyUrl === $apiUrl) {
        $verifyUrl = str_replace('license_check.php', 'license/verify.php', $apiUrl);
    }
    
    $postData = [
        'license_key'  => $licenseKey,
        'domain'       => $domain,
        'server_ip'    => $ip,
        'local_hash'   => $localHash,
        'product_slug' => $productSlug,
    ];
    
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL            => $verifyUrl,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => http_build_query($postData),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT => _BOOTSTRAP_CURL_CONNECT,
        CURLOPT_TIMEOUT        => _BOOTSTRAP_CURL_TIMEOUT,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_HTTPHEADER     => [
            'User-Agent: SMMPanel/2.0',
            'Accept: application/json',
        ],
    ]);
    
    $response = curl_exec($ch);
    curl_close($ch);
    
    if ($response === false || empty($response)) {
        return false; // Fail closed
    }
    
    $data = @json_decode($response, true);
    
    if (!is_array($data) || !isset($data['status'])) {
        return false;
    }
    
    return ($data['status'] === 'valid');
}
