<?php
/**
 * Security — CSRF, XSS, input sanitization, rate limiting
 */
class Security {

    // ── CSRF ────────────────────────────────────────────────
    public static function generateCsrfToken(): string {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }
        if (empty($_SESSION[CSRF_TOKEN_NAME])) {
            $_SESSION[CSRF_TOKEN_NAME] = bin2hex(random_bytes(32));
        }
        return $_SESSION[CSRF_TOKEN_NAME];
    }

    public static function validateCsrfToken(string $token): bool {
        return isset($_SESSION[CSRF_TOKEN_NAME])
            && hash_equals($_SESSION[CSRF_TOKEN_NAME], $token);
    }

    public static function csrfField(): string {
        $token = self::generateCsrfToken();
        return '<input type="hidden" name="'.CSRF_TOKEN_NAME.'" value="'.htmlspecialchars($token).'">';
    }

    public static function checkCsrf(): void {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }
        $token = $_POST[CSRF_TOKEN_NAME] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
        if (!self::validateCsrfToken($token)) {
            http_response_code(403);
            die(json_encode(['success' => false, 'message' => 'Invalid CSRF token.']));
        }
    }

    // ── XSS / Sanitization ──────────────────────────────────
    public static function sanitize(mixed $value): mixed {
        if (is_array($value)) {
            return array_map([self::class, 'sanitize'], $value);
        }
        return trim((string)$value);
    }

    public static function sanitizeInput(array $fields, array $source = []): array {
        $source = $source ?: $_POST;
        $out = [];
        foreach ($fields as $field) {
            $out[$field] = self::sanitize($source[$field] ?? '');
        }
        return $out;
    }

    // ── Rate Limiting ───────────────────────────────────────
    public static function checkLoginRateLimit(string $ip, string $email = ''): bool {
        $cutoff = date('Y-m-d H:i:s', strtotime("-".MAX_LOGIN_ATTEMPTS." minutes"));
        $stmt = db()->prepare(
            "SELECT COUNT(*) FROM login_attempts WHERE ip_address = ? AND attempted_at > ?"
        );
        $stmt->execute([$ip, $cutoff]);
        return $stmt->fetchColumn() < MAX_LOGIN_ATTEMPTS;
    }

    public static function recordLoginAttempt(string $ip, string $email = ''): void {
        db()->prepare("INSERT INTO login_attempts (ip_address, email) VALUES (?,?)")
             ->execute([$ip, $email]);
    }

    public static function clearLoginAttempts(string $ip): void {
        db()->prepare("DELETE FROM login_attempts WHERE ip_address = ?")
             ->execute([$ip]);
    }

    // ── Password ────────────────────────────────────────────
    public static function hashPassword(string $password): string {
        return password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
    }

    public static function verifyPassword(string $password, string $hash): bool {
        return password_verify($password, $hash);
    }

    // ── Slug ────────────────────────────────────────────────
    public static function makeSlug(string $text): string {
        $text = strtolower(trim($text));
        $text = preg_replace('/[^a-z0-9\-]/', '-', $text);
        return preg_replace('/-+/', '-', $text);
    }

    // ── IP ──────────────────────────────────────────────────
    public static function getIp(): string {
        return $_SERVER['HTTP_CLIENT_IP']
            ?? $_SERVER['HTTP_X_FORWARDED_FOR']
            ?? $_SERVER['REMOTE_ADDR']
            ?? '0.0.0.0';
    }
}
