<?php
/**
 * Heritage Voice — helpers.php
 * Hàm dùng chung: response, CORS, auth, input, geo, pagination
 */
require_once __DIR__ . '/config.php';

// ── Response ──────────────────────────────────────────────────

function json_response(array $data, int $status = 200): void {
    http_response_code($status);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

function json_ok(mixed $data, string $message = 'OK'): void {
    json_response(['success' => true, 'message' => $message, 'data' => $data]);
}

function json_error(string $message, int $status = 400): void {
    json_response(['success' => false, 'error' => $message], $status);
}

// ── CORS ──────────────────────────────────────────────────────

function cors(): void {
    $origin  = $_SERVER['HTTP_ORIGIN'] ?? '';
    $allowed = defined('ALLOWED_ORIGINS') ? ALLOWED_ORIGINS : [];

    // Thêm app và mobile origins
    $allowed[] = 'https://tads.ctv.hanoi.vn';
    $allowed[] = 'http://localhost';
    $allowed[] = 'http://localhost:3000';
    $allowed[] = 'capacitor://localhost'; // iOS PWA
    $allowed[] = 'ionic://localhost';     // Ionic

    if (!empty($origin) && in_array($origin, $allowed, true)) {
        header("Access-Control-Allow-Origin: $origin");
        header('Vary: Origin');
    } elseif (empty($origin)) {
        // Direct request (curl, mobile app) — cho phép
        header('Access-Control-Allow-Origin: *');
    }
    // Origin không hợp lệ → không set header → browser tự block

    header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
    header('Access-Control-Allow-Headers: Content-Type, Authorization, X-API-Key, X-Anonymous-ID, X-Requested-With');
    header('Access-Control-Max-Age: 86400');
    header('X-Content-Type-Options: nosniff');
    header('X-Frame-Options: SAMEORIGIN');
    header('X-XSS-Protection: 1; mode=block');
    header('Referrer-Policy: strict-origin-when-cross-origin');
    if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(204); exit; }
}

// ── Auth ──────────────────────────────────────────────────────

function require_admin(): void {
    // Cách 1: X-API-Key
    $key = isset($_SERVER['HTTP_X_API_KEY']) ? $_SERVER['HTTP_X_API_KEY'] : '';
    if (!empty($key) && $key === ADMIN_API_KEY) return;

    // Cách 2: Bearer token — decode payload thủ công
    $header = isset($_SERVER['HTTP_AUTHORIZATION']) ? $_SERVER['HTTP_AUTHORIZATION'] : '';
    if (strpos($header, 'Bearer ') === 0) {
        $token = substr($header, 7);
        $pos   = strrpos($token, '.');
        if ($pos !== false) {
            $data = substr($token, 0, $pos);
            $sig  = substr($token, $pos + 1);
            if (hash_equals(hash_hmac('sha256', $data, TOKEN_SECRET), $sig)) {
                $payload = json_decode(base64_decode($data), true);
                if ($payload && $payload['exp'] > time() && $payload['type'] === 'access') {
                    $stmt = db()->prepare("SELECT role FROM users WHERE id=:id AND status='active'");
                    $stmt->execute(array(':id' => $payload['uid']));
                    $u = $stmt->fetch();
                    if ($u && in_array($u['role'], array('editor','reviewer','admin','superadmin'))) return;
                }
            }
        }
    }
    json_error('Unauthorized', 401);
}

// Chỉ editor trở lên mới tạo/sửa được
function require_editor(): void {
    $uid = get_token_uid();
    if (!$uid) json_error('Unauthorized', 401);
    $stmt = db()->prepare("SELECT role FROM users WHERE id=:id AND status='active'");
    $stmt->execute(array(':id' => $uid));
    $u = $stmt->fetch();
    if (!$u || !in_array($u['role'], array('editor','admin','superadmin'))) json_error('Forbidden', 403);
}

// Chỉ reviewer trở lên mới duyệt được
function require_reviewer(): void {
    $uid = get_token_uid();
    if (!$uid) json_error('Unauthorized', 401);
    $stmt = db()->prepare("SELECT role FROM users WHERE id=:id AND status='active'");
    $stmt->execute(array(':id' => $uid));
    $u = $stmt->fetch();
    if (!$u || !in_array($u['role'], array('reviewer','editor','admin','superadmin'))) json_error('Forbidden', 403);
}

// Chỉ admin trở lên mới quản lý user
function require_admin_role(): void {
    $uid = get_token_uid();
    if (!$uid) json_error('Unauthorized', 401);
    $stmt = db()->prepare("SELECT role FROM users WHERE id=:id AND status='active'");
    $stmt->execute(array(':id' => $uid));
    $u = $stmt->fetch();
    if (!$u || !in_array($u['role'], array('admin','superadmin'))) json_error('Forbidden', 403);
}

// Chỉ superadmin mới xóa/export được
function require_superadmin(): void {
    $uid = get_token_uid();
    if (!$uid) json_error('Unauthorized', 401);
    $stmt = db()->prepare("SELECT role FROM users WHERE id=:id AND status='active'");
    $stmt->execute(array(':id' => $uid));
    $u = $stmt->fetch();
    if (!$u || $u['role'] !== 'superadmin') json_error('Forbidden', 403);
}

// Lấy user_id từ Bearer token
function get_token_uid(): ?int {
    $header = isset($_SERVER['HTTP_AUTHORIZATION']) ? $_SERVER['HTTP_AUTHORIZATION'] : '';
    if (strpos($header, 'Bearer ') !== 0) return null;
    $token = substr($header, 7);
    $pos   = strrpos($token, '.');
    if ($pos === false) return null;
    $data = substr($token, 0, $pos);
    $sig  = substr($token, $pos + 1);
    if (!hash_equals(hash_hmac('sha256', $data, TOKEN_SECRET), $sig)) return null;
    $payload = json_decode(base64_decode($data), true);
    if (!$payload || $payload['exp'] <= time()) return null;
    // Kiểm tra token blacklist
    if (!empty($payload['jti'])) {
        $ck = db()->prepare("SELECT id FROM hv_token_blacklist WHERE jti = :jti LIMIT 1");
        $ck->execute([':jti' => $payload['jti']]);
        if ($ck->fetch()) return null; // Token đã bị thu hồi
    }
    return (int)$payload['uid'];
}

// Lấy JTI từ token hiện tại
function get_token_payload(): ?array {
    $header = isset($_SERVER['HTTP_AUTHORIZATION']) ? $_SERVER['HTTP_AUTHORIZATION'] : '';
    if (strpos($header, 'Bearer ') !== 0) return null;
    $token = substr($header, 7);
    $pos   = strrpos($token, '.');
    if ($pos === false) return null;
    $data = substr($token, 0, $pos);
    $sig  = substr($token, $pos + 1);
    if (!hash_equals(hash_hmac('sha256', $data, TOKEN_SECRET), $sig)) return null;
    $payload = json_decode(base64_decode($data), true);
    if (!$payload || $payload['exp'] <= time()) return null;
    return $payload;
}

// Lấy role của user hiện tại (null nếu chưa đăng nhập)
function current_user_role(): ?string {
    $uid = get_token_uid();
    if (!$uid) return null;
    $stmt = db()->prepare("SELECT role FROM users WHERE id=:id AND status='active'");
    $stmt->execute(array(':id' => $uid));
    $u = $stmt->fetch();
    return $u ? $u['role'] : null;
}

// ── Request ───────────────────────────────────────────────────

function body(): array {
    static $parsed = null;
    if ($parsed === null) {
        $raw    = file_get_contents('php://input');
        $parsed = !empty($raw) ? (json_decode($raw, true) ?? []) : [];
    }
    return $parsed;
}

function anonymous_id(): string {
    $id = $_SERVER['HTTP_X_ANONYMOUS_ID'] ?? '';
    if (preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i', $id)) return $id;
    $d    = random_bytes(16);
    $d[6] = chr(ord($d[6]) & 0x0f | 0x40);
    $d[8] = chr(ord($d[8]) & 0x3f | 0x80);
    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($d), 4));
}

function required_body(array $fields): void {
    $b = body();
    foreach ($fields as $f) if (empty($b[$f])) json_error("Thiếu trường bắt buộc: $f");
}

// ── Input ─────────────────────────────────────────────────────

function float_param(string $key, ?float $default = null): ?float {
    if (!isset($_GET[$key]) || $_GET[$key] === '') return $default;
    $v = filter_var($_GET[$key], FILTER_VALIDATE_FLOAT);
    return $v === false ? $default : $v;
}

function int_param(string $key, ?int $default = null, int $min = 0, int $max = PHP_INT_MAX): ?int {
    if (!isset($_GET[$key]) || $_GET[$key] === '') return $default;
    $v = filter_var($_GET[$key], FILTER_VALIDATE_INT);
    if ($v === false) return $default;
    return max($min, min($max, $v));
}

function str_param(string $key, string $default = ''): string {
    return trim(htmlspecialchars($_GET[$key] ?? $default, ENT_QUOTES, 'UTF-8'));
}

// ── Geo ───────────────────────────────────────────────────────

function haversine(float $lat1, float $lng1, float $lat2, float $lng2): float {
    $R    = 6371000;
    $dLat = deg2rad($lat2 - $lat1);
    $dLng = deg2rad($lng2 - $lng1);
    $a    = sin($dLat / 2) ** 2 + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLng / 2) ** 2;
    return (float)($R * 2 * atan2(sqrt($a), sqrt(1 - $a)));
}



// ── Audit Log ──────────────────────────────────────────────────
function audit_log(string $action, string $target_type = '', string $target_id = '', string $detail = ''): void {
    try {
        $uid   = get_token_uid();
        $email = '';
        if ($uid) {
            $u = db()->prepare("SELECT email FROM users WHERE id=:id");
            $u->execute([':id' => $uid]);
            $row   = $u->fetch();
            $email = $row ? $row['email'] : '';
        }
        $ip = $_SERVER['HTTP_X_FORWARDED_FOR']
            ?? $_SERVER['HTTP_X_REAL_IP']
            ?? $_SERVER['REMOTE_ADDR']
            ?? '';
        // Chỉ lấy IP đầu tiên nếu có nhiều
        $ip = trim(explode(',', $ip)[0]);

        db()->prepare("
            INSERT INTO hv_audit_log
                (user_id, user_email, action, target_type, target_id, detail, ip_address)
            VALUES
                (:uid, :email, :action, :ttype, :tid, :detail, :ip)
        ")->execute([
            ':uid'    => $uid,
            ':email'  => $email,
            ':action' => $action,
            ':ttype'  => $target_type,
            ':tid'    => $target_id,
            ':detail' => mb_substr($detail, 0, 1000),
            ':ip'     => mb_substr($ip, 0, 45),
        ]);
    } catch (\Exception $e) {
        // Không để lỗi audit làm crash API
        error_log('[HV Audit Error] ' . $e->getMessage());
    }
}

// ── Rate Limiting ──────────────────────────────────────────────
function rate_limit(string $key, int $max, int $window = 60): void {
    $ip       = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
    $cacheKey = md5('hv_rl_' . $key . '_' . $ip);
    $cacheFile = sys_get_temp_dir() . '/hv_rl_' . $cacheKey . '.json';

    $data = ['count' => 0, 'reset' => time() + $window];
    if (file_exists($cacheFile)) {
        $saved = json_decode(file_get_contents($cacheFile), true);
        if ($saved && time() < $saved['reset']) {
            $data = $saved;
        }
    }

    $data['count']++;
    file_put_contents($cacheFile, json_encode($data), LOCK_EX);

    if ($data['count'] > $max) {
        $retry = $data['reset'] - time();
        header('Retry-After: ' . $retry);
        json_error('Quá nhiều yêu cầu. Thử lại sau ' . $retry . ' giây.', 429);
    }
}

// ── Pagination ────────────────────────────────────────────────

function paginate(int $total, int $page, int $limit): array {
    return [
        'total' => $total,
        'page'  => $page,
        'limit' => $limit,
        'pages' => (int)ceil($total / max(1, $limit)),
    ];
}
