<?php
/**
 * API Key Management System for WHP
 * Handles API key generation, validation, and rate limiting
 */

class ApiKeyManager {
    private $db;
    private $logger;
    
    public function __construct($database = null) {
        if ($database) {
            $this->db = $database;
        } else {
            require_once __DIR__ . '/mysqlmgmt.php';
            $MySQLMgmt = new mysqlmgmt();
            $this->db = $MySQLMgmt->getMySQLConnection();
            
            // Select the WHP database
            if ($this->db) {
                $this->db->exec("USE whp");
            }
        }
        
        // Initialize logger if available
        if (class_exists('Logger')) {
            $this->logger = new Logger('api');
        }
    }
    
    /**
     * Generate a new API key pair
     */
    public function generateApiKey($keyName, $createdBy, $permissions = [], $options = []) {
        try {
            // Generate secure API key and secret
            $apiKey = 'whp_' . bin2hex(random_bytes(24));
            $apiSecret = bin2hex(random_bytes(32));
            $hashedSecret = password_hash($apiSecret, PASSWORD_ARGON2ID);
            
            // Default options
            $defaults = [
                'rate_limit_per_hour' => 1000,
                'ip_whitelist' => null,
                'notes' => null
            ];
            $options = array_merge($defaults, $options);
            
            $stmt = $this->db->prepare("
                INSERT INTO api_keys (key_name, api_key, api_secret, created_by, permissions, 
                                    rate_limit_per_hour, ip_whitelist, notes) 
                VALUES (?, ?, ?, ?, ?, ?, ?, ?)
            ");
            
            $result = $stmt->execute([
                $keyName,
                $apiKey,
                $hashedSecret,
                $createdBy,
                json_encode($permissions),
                $options['rate_limit_per_hour'],
                $options['ip_whitelist'],
                $options['notes']
            ]);
            
            if ($result) {
                $this->logAction('api_key_created', $apiKey, [
                    'key_name' => $keyName,
                    'created_by' => $createdBy
                ]);
                
                // Return the plain secret only once
                return [
                    'success' => true,
                    'api_key' => $apiKey,
                    'api_secret' => $apiSecret, // Only returned once!
                    'key_id' => $this->db->lastInsertId()
                ];
            }
            
            return ['success' => false, 'error' => 'Failed to create API key'];
            
        } catch (Exception $e) {
            $this->logAction('api_key_create_error', null, ['error' => $e->getMessage()]);
            return ['success' => false, 'error' => 'API key generation failed'];
        }
    }
    
    /**
     * Validate API key and secret
     */
    public function validateApiKey($apiKey, $apiSecret, $ipAddress = null) {
        try {
            $stmt = $this->db->prepare("
                SELECT id, api_secret, permissions, rate_limit_per_hour, ip_whitelist, 
                       is_active, usage_count, created_by
                FROM api_keys 
                WHERE api_key = ? AND is_active = 1
            ");
            
            $stmt->execute([$apiKey]);
            $keyData = $stmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$keyData) {
                $this->logAction('api_key_not_found', $apiKey, ['ip' => $ipAddress]);
                return ['success' => false, 'error' => 'Invalid API key'];
            }
            
            // Verify secret
            if (!password_verify($apiSecret, $keyData['api_secret'])) {
                $this->logAction('api_secret_invalid', $apiKey, ['ip' => $ipAddress]);
                return ['success' => false, 'error' => 'Invalid API secret'];
            }
            
            // Check IP whitelist if configured
            if ($keyData['ip_whitelist'] && $ipAddress) {
                $allowedIPs = explode(',', $keyData['ip_whitelist']);
                $allowedIPs = array_map('trim', $allowedIPs);
                
                if (!in_array($ipAddress, $allowedIPs)) {
                    $this->logAction('api_ip_blocked', $apiKey, ['ip' => $ipAddress]);
                    return ['success' => false, 'error' => 'IP not whitelisted'];
                }
            }
            
            // Check rate limiting
            $rateLimitCheck = $this->checkRateLimit($keyData['id'], $keyData['rate_limit_per_hour']);
            if (!$rateLimitCheck['allowed']) {
                $this->logAction('api_rate_limited', $apiKey, ['ip' => $ipAddress]);
                return ['success' => false, 'error' => 'Rate limit exceeded'];
            }
            
            // Update last used timestamp and usage count
            $this->updateKeyUsage($keyData['id']);
            
            return [
                'success' => true,
                'key_id' => $keyData['id'],
                'permissions' => json_decode($keyData['permissions'], true) ?: [],
                'created_by' => $keyData['created_by']
            ];
            
        } catch (Exception $e) {
            $this->logAction('api_validation_error', $apiKey, [
                'error' => $e->getMessage(),
                'ip' => $ipAddress
            ]);
            return ['success' => false, 'error' => 'Validation failed'];
        }
    }
    
    /**
     * Check if API key has specific permission
     */
    public function hasPermission($keyId, $permission) {
        try {
            $stmt = $this->db->prepare("SELECT permissions FROM api_keys WHERE id = ?");
            $stmt->execute([$keyId]);
            $result = $stmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$result) {
                return false;
            }
            
            $permissions = json_decode($result['permissions'], true) ?: [];
            return isset($permissions[$permission]) && $permissions[$permission] === true;
            
        } catch (Exception $e) {
            return false;
        }
    }
    
    /**
     * Rate limiting check
     */
    private function checkRateLimit($keyId, $limitPerHour) {
        try {
            $stmt = $this->db->prepare("
                SELECT COUNT(*) as usage_count 
                FROM api_usage_logs 
                WHERE api_key_id = ? AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)
            ");
            
            $stmt->execute([$keyId]);
            $result = $stmt->fetch(PDO::FETCH_ASSOC);
            
            $currentUsage = $result['usage_count'] ?? 0;
            
            return [
                'allowed' => $currentUsage < $limitPerHour,
                'current_usage' => $currentUsage,
                'limit' => $limitPerHour,
                'reset_time' => time() + 3600
            ];
            
        } catch (Exception $e) {
            // If rate limit check fails, allow the request
            return ['allowed' => true, 'current_usage' => 0, 'limit' => $limitPerHour];
        }
    }
    
    /**
     * Update key usage statistics
     */
    private function updateKeyUsage($keyId) {
        try {
            $stmt = $this->db->prepare("
                UPDATE api_keys 
                SET last_used_at = NOW(), usage_count = usage_count + 1 
                WHERE id = ?
            ");
            $stmt->execute([$keyId]);
        } catch (Exception $e) {
            // Log but don't fail the request
            $this->logAction('usage_update_failed', null, ['key_id' => $keyId, 'error' => $e->getMessage()]);
        }
    }
    
    /**
     * Log API usage for monitoring and debugging
     */
    public function logApiUsage($keyId, $endpoint, $method, $ipAddress, $responseCode, $responseTime = null, $requestData = null, $userAgent = null) {
        try {
            $stmt = $this->db->prepare("
                INSERT INTO api_usage_logs (api_key_id, endpoint, method, ip_address, 
                                          user_agent, request_data, response_code, response_time_ms) 
                VALUES (?, ?, ?, ?, ?, ?, ?, ?)
            ");
            
            $stmt->execute([
                $keyId,
                $endpoint,
                $method,
                $ipAddress,
                $userAgent,
                json_encode($requestData),
                $responseCode,
                $responseTime
            ]);
            
        } catch (Exception $e) {
            // Log but don't fail the request
            error_log("API usage logging failed: " . $e->getMessage());
        }
    }
    
    /**
     * Get API key list (admin only)
     */
    public function getApiKeys($includeInactive = false) {
        try {
            $sql = "
                SELECT id, key_name, api_key, created_by, created_at, updated_at, 
                       last_used_at, is_active, permissions, rate_limit_per_hour, 
                       usage_count, ip_whitelist, notes
                FROM api_keys
            ";
            
            if (!$includeInactive) {
                $sql .= " WHERE is_active = 1";
            }
            
            $sql .= " ORDER BY created_at DESC";
            
            $stmt = $this->db->prepare($sql);
            $stmt->execute();
            
            $keys = $stmt->fetchAll(PDO::FETCH_ASSOC);
            
            // Decode permissions for each key
            foreach ($keys as &$key) {
                $key['permissions'] = json_decode($key['permissions'], true) ?: [];
                // Never return the actual secret
                unset($key['api_secret']);
            }
            
            return ['success' => true, 'keys' => $keys];
            
        } catch (Exception $e) {
            return ['success' => false, 'error' => 'Failed to retrieve API keys'];
        }
    }
    
    /**
     * Deactivate API key
     */
    public function deactivateApiKey($keyId, $deactivatedBy) {
        try {
            $stmt = $this->db->prepare("
                UPDATE api_keys 
                SET is_active = 0, updated_at = NOW() 
                WHERE id = ?
            ");
            
            $result = $stmt->execute([$keyId]);
            
            if ($result) {
                $this->logAction('api_key_deactivated', null, [
                    'key_id' => $keyId,
                    'deactivated_by' => $deactivatedBy
                ]);
                return ['success' => true];
            }
            
            return ['success' => false, 'error' => 'Failed to deactivate API key'];
            
        } catch (Exception $e) {
            return ['success' => false, 'error' => 'Deactivation failed'];
        }
    }
    
    /**
     * Update API key permissions
     */
    public function updatePermissions($keyId, $permissions, $updatedBy) {
        try {
            $stmt = $this->db->prepare("
                UPDATE api_keys 
                SET permissions = ?, updated_at = NOW() 
                WHERE id = ?
            ");
            
            $result = $stmt->execute([json_encode($permissions), $keyId]);
            
            if ($result) {
                $this->logAction('api_permissions_updated', null, [
                    'key_id' => $keyId,
                    'updated_by' => $updatedBy,
                    'new_permissions' => $permissions
                ]);
                return ['success' => true];
            }
            
            return ['success' => false, 'error' => 'Failed to update permissions'];
            
        } catch (Exception $e) {
            return ['success' => false, 'error' => 'Permission update failed'];
        }
    }
    
    /**
     * Generate SSO token for auto-login
     */
    public function generateSSOToken($username, $keyId, $ipAddress, $redirectUrl = null, $expiryMinutes = 10) {
        try {
            $token = bin2hex(random_bytes(32));
            $expiresAt = date('Y-m-d H:i:s', time() + ($expiryMinutes * 60));
            
            $stmt = $this->db->prepare("
                INSERT INTO sso_tokens (token, username, api_key_id, redirect_url, expires_at, ip_address) 
                VALUES (?, ?, ?, ?, ?, ?)
            ");
            
            $result = $stmt->execute([$token, $username, $keyId, $redirectUrl, $expiresAt, $ipAddress]);
            
            if ($result) {
                $this->logAction('sso_token_generated', null, [
                    'username' => $username,
                    'key_id' => $keyId,
                    'expires_at' => $expiresAt
                ]);
                
                return [
                    'success' => true,
                    'token' => $token,
                    'expires_at' => $expiresAt,
                    'login_url' => "/api/sso-login.php?token=" . $token
                ];
            }
            
            return ['success' => false, 'error' => 'Failed to generate SSO token'];
            
        } catch (Exception $e) {
            $this->logAction('sso_token_error', null, ['error' => $e->getMessage()]);
            return ['success' => false, 'error' => 'SSO token generation failed'];
        }
    }
    
    /**
     * Validate and consume SSO token
     */
    public function validateSSOToken($token, $ipAddress) {
        try {
            $stmt = $this->db->prepare("
                SELECT id, username, redirect_url, expires_at, used_at, ip_address
                FROM sso_tokens 
                WHERE token = ? AND used_at IS NULL AND expires_at > NOW()
            ");
            
            $stmt->execute([$token]);
            $tokenData = $stmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$tokenData) {
                $this->logAction('sso_token_invalid', null, ['token' => substr($token, 0, 8) . '...']);
                return ['success' => false, 'error' => 'Invalid or expired SSO token'];
            }
            
            // Optional: Verify IP address matches (for added security)
            // if ($tokenData['ip_address'] !== $ipAddress) {
            //     return ['success' => false, 'error' => 'IP address mismatch'];
            // }
            
            // Mark token as used
            $updateStmt = $this->db->prepare("UPDATE sso_tokens SET used_at = NOW() WHERE id = ?");
            $updateStmt->execute([$tokenData['id']]);
            
            $this->logAction('sso_token_used', null, [
                'username' => $tokenData['username'],
                'token_id' => $tokenData['id']
            ]);
            
            return [
                'success' => true,
                'username' => $tokenData['username'],
                'redirect_url' => $tokenData['redirect_url']
            ];
            
        } catch (Exception $e) {
            return ['success' => false, 'error' => 'SSO validation failed'];
        }
    }
    
    /**
     * Clean up expired SSO tokens
     */
    public function cleanupExpiredTokens() {
        try {
            $stmt = $this->db->prepare("DELETE FROM sso_tokens WHERE expires_at < NOW()");
            $stmt->execute();
            
            $deletedCount = $stmt->rowCount();
            
            if ($deletedCount > 0) {
                $this->logAction('sso_tokens_cleaned', null, ['deleted_count' => $deletedCount]);
            }
            
            return ['success' => true, 'deleted_count' => $deletedCount];
            
        } catch (Exception $e) {
            return ['success' => false, 'error' => 'Cleanup failed'];
        }
    }
    
    /**
     * Log actions for audit trail
     */
    private function logAction($action, $apiKey = null, $data = []) {
        if ($this->logger) {
            $this->logger->info($action, array_merge([
                'api_key' => $apiKey ? substr($apiKey, 0, 10) . '...' : null,
                'timestamp' => date('Y-m-d H:i:s')
            ], $data));
        } else {
            error_log("API Action: $action - " . json_encode($data));
        }
    }
}
?>