<?php
/**
 * API Authentication & Security Middleware
 * Token-based auth, rate limiting, IP/device logging
 */

class ApiAuth {
    
    /**
     * Authenticate API request via Bearer token or session
     * Returns user array or sends 401 error
     */
    public static function authenticate() {
        // Try Bearer token first
        $token = self::getBearerToken();
        if ($token) {
            return self::validateToken($token);
        }
        
        // Fall back to session auth
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }
        
        if (isset($_SESSION['user_id']) && $_SESSION['user_type'] === 'user') {
            $user = dbFetch("SELECT * FROM users WHERE id = ? AND status = 'active'", [$_SESSION['user_id']]);
            if ($user) {
                return $user;
            }
        }
        
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Authentication required']);
        exit;
    }
    
    /**
     * Generate API token for a user (login)
     */
    public static function generateApiToken($userId, $deviceName = null, $deviceId = null) {
        $token = bin2hex(random_bytes(48)); // 96-char hex token
        $expiresAt = date('Y-m-d H:i:s', strtotime('+30 days'));
        
        dbInsert('api_tokens', [
            'user_id' => $userId,
            'token' => hash('sha256', $token),
            'device_name' => $deviceName,
            'device_id' => $deviceId,
            'ip_address' => self::getClientIp(),
            'expires_at' => $expiresAt
        ]);
        
        return [
            'token' => $token,
            'expires_at' => $expiresAt,
            'token_type' => 'Bearer'
        ];
    }
    
    /**
     * Validate Bearer token
     */
    private static function validateToken($token) {
        $hashedToken = hash('sha256', $token);
        
        $tokenRecord = dbFetch(
            "SELECT t.*, u.* FROM api_tokens t 
             JOIN users u ON t.user_id = u.id 
             WHERE t.token = :token AND t.expires_at > NOW() AND u.status = 'active'",
            ['token' => $hashedToken]
        );
        
        if (!$tokenRecord) {
            http_response_code(401);
            echo json_encode(['success' => false, 'error' => 'Invalid or expired token']);
            exit;
        }
        
        // Update last used
        dbQuery(
            "UPDATE api_tokens SET last_used_at = NOW() WHERE token = ?",
            [$hashedToken]
        );
        
        return $tokenRecord;
    }
    
    /**
     * Extract Bearer token from Authorization header
     */
    private static function getBearerToken() {
        $headers = self::getAuthorizationHeader();
        if (!empty($headers) && preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
            return $matches[1];
        }
        return null;
    }
    
    /**
     * Get Authorization header
     */
    private static function getAuthorizationHeader() {
        $headers = null;
        if (isset($_SERVER['Authorization'])) {
            $headers = trim($_SERVER['Authorization']);
        } elseif (isset($_SERVER['HTTP_AUTHORIZATION'])) {
            $headers = trim($_SERVER['HTTP_AUTHORIZATION']);
        } elseif (function_exists('apache_request_headers')) {
            $requestHeaders = apache_request_headers();
            $requestHeaders = array_combine(
                array_map('ucwords', array_keys($requestHeaders)),
                array_values($requestHeaders)
            );
            if (isset($requestHeaders['Authorization'])) {
                $headers = trim($requestHeaders['Authorization']);
            }
        }
        return $headers;
    }
    
    /**
     * Rate limiter: check if action is rate-limited
     * Uses simple DB-based counting
     */
    public static function rateLimit($userId, $action, $maxAttempts = 5, $windowSeconds = 60) {
        $since = date('Y-m-d H:i:s', time() - $windowSeconds);
        
        $count = dbFetch(
            "SELECT COUNT(*) as cnt FROM activity_logs 
             WHERE user_id = :uid AND action = :action AND created_at > :since",
            ['uid' => $userId, 'action' => $action, 'since' => $since]
        );
        
        if ($count && (int)$count['cnt'] >= $maxAttempts) {
            return false; // Rate limited
        }
        return true; // OK
    }
    
    /**
     * Log API activity
     */
    public static function logApiActivity($userId, $action, $description = null) {
        logActivity('user', $userId, $action, $description);
    }
    
    /**
     * Get client IP
     */
    public static function getClientIp() {
        if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            return trim($ips[0]);
        }
        if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
            return $_SERVER['HTTP_X_REAL_IP'];
        }
        return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
    }
    
    /**
     * Get device info string
     */
    public static function getDeviceInfo() {
        $ua = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown';
        $ip = self::getClientIp();
        return json_encode([
            'ip' => $ip,
            'user_agent' => substr($ua, 0, 250),
            'timestamp' => date('Y-m-d H:i:s')
        ]);
    }
    
    /**
     * Set CORS and JSON headers for API
     */
    public static function setApiHeaders() {
        header('Content-Type: application/json');
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type, Authorization');
        
        if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
            http_response_code(200);
            exit;
        }
    }
}
