<?php

namespace FleekDash\Integrations\Core;

defined('ABSPATH') || exit;

/**
 * Monitoring Manager for Integrations
 *
 * Monitors performance, errors, and usage
 */
class MonitoringManager
{
    private Container $container;
    private Cache $cache;
    private array $metrics = [];
    private array $alerts = [];

    public function __construct(Container $container, Cache $cache)
    {
        $this->container = $container;
        $this->cache = $cache;
        $this->registerHooks();
    }

    /**
     * Register WordPress hooks
     */
    private function registerHooks(): void
    {
        add_action('rest_api_init', [$this, 'registerRestRoutes']);
        add_action('wp_loaded', [$this, 'startMonitoring']);

        // Monitor integration events
        add_action('fleekdash_integration_loaded', [$this, 'recordIntegrationLoad']);
        add_action('fleekdash_integration_error', [$this, 'recordError']);
        add_action('fleekdash_api_request', [$this, 'recordApiRequest']);
    }

    /**
     * Start monitoring
     */
    public function startMonitoring(): void
    {
        // Record system metrics
        $this->recordSystemMetrics();

        // Schedule daily cleanup
        if (!wp_next_scheduled('fleekdash_cleanup_metrics')) {
            wp_schedule_event(time(), 'daily', 'fleekdash_cleanup_metrics');
        }

        add_action('fleekdash_cleanup_metrics', [$this, 'cleanupMetrics']);
    }

    /**
     * Record a metric
     */
    public function recordMetric(string $key, $value, array $tags = []): void
    {
        $metric = [
            'key' => $key,
            'value' => $value,
            'tags' => $tags,
            'timestamp' => time(),
            'date' => date('Y-m-d'),
            'hour' => date('Y-m-d H')
        ];

        $this->metrics[] = $metric;

        // Save to database periodically
        $this->saveMetrics();
    }

    /**
     * Record integration load time
     */
    public function recordIntegrationLoad(string $integrationId, float $loadTime = null): void
    {
        if ($loadTime === null) {
            $loadTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
        }

        $this->recordMetric('integration.load_time', $loadTime, [
            'integration' => $integrationId
        ]);

        $this->recordMetric('integration.loaded', 1, [
            'integration' => $integrationId
        ]);
    }

    /**
     * Record error
     */
    public function recordError(string $source, string $message, array $context = []): void
    {
        $error = [
            'source' => $source,
            'message' => $message,
            'context' => $context,
            'timestamp' => time(),
            'date' => date('Y-m-d'),
            'user_id' => get_current_user_id(),
            'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown'
        ];

        $this->saveError($error);

        $this->recordMetric('error.count', 1, [
            'source' => $source,
            'type' => $context['type'] ?? 'unknown'
        ]);

        // Check for error rate alerts
        $this->checkErrorRateAlert($source);
    }

    /**
     * Record API request
     */
    public function recordApiRequest(string $endpoint, int $responseCode, float $responseTime): void
    {
        $this->recordMetric('api.request_time', $responseTime, [
            'endpoint' => $endpoint,
            'status_code' => (string) $responseCode
        ]);

        $this->recordMetric('api.request_count', 1, [
            'endpoint' => $endpoint,
            'status_code' => (string) $responseCode
        ]);

        // Record error if 4xx or 5xx
        if ($responseCode >= 400) {
            $this->recordError('api', "API request failed: {$endpoint}", [
                'endpoint' => $endpoint,
                'status_code' => $responseCode,
                'response_time' => $responseTime
            ]);
        }
    }

    /**
     * Record system metrics
     */
    private function recordSystemMetrics(): void
    {
        // Memory usage
        $this->recordMetric('system.memory_usage', memory_get_usage(true));
        $this->recordMetric('system.memory_peak', memory_get_peak_usage(true));

        // Cache metrics
        $cacheStats = $this->cache->getStats();
        foreach ($cacheStats as $key => $value) {
            $this->recordMetric("cache.{$key}", $value);
        }

        // Integration count
        $discovery = $this->container->make(DiscoveryEngine::class);
        $integrations = $discovery->discover();
        $active = get_option('fleekdash_active_integrations', []);
        $active = is_array($active) ? $active : [];

        $this->recordMetric('integrations.total', count($integrations));
        $this->recordMetric('integrations.active', count($active));

        // WordPress metrics
        $this->recordMetric('wordpress.version', get_bloginfo('version'), ['type' => 'string']);
        $this->recordMetric('php.version', PHP_VERSION, ['type' => 'string']);
    }

    /**
     * Get performance metrics
     */
    public function getPerformanceMetrics(string $period = '24h'): array
    {
        $metrics = $this->getMetrics($period);

        return [
            'load_times' => $this->aggregateMetrics($metrics, 'integration.load_time'),
            'api_times' => $this->aggregateMetrics($metrics, 'api.request_time'),
            'memory_usage' => $this->aggregateMetrics($metrics, 'system.memory_usage'),
            'error_rate' => $this->calculateErrorRate($metrics),
            'cache_hit_rate' => $this->calculateCacheHitRate($metrics)
        ];
    }

    /**
     * Get error summary
     */
    public function getErrorSummary(string $period = '24h'): array
    {
        $errors = $this->getErrors($period);

        $summary = [
            'total' => count($errors),
            'by_source' => [],
            'by_type' => [],
            'recent' => array_slice($errors, -10)
        ];

        foreach ($errors as $error) {
            $source = $error['source'];
            $type = $error['context']['type'] ?? 'unknown';

            $summary['by_source'][$source] = ($summary['by_source'][$source] ?? 0) + 1;
            $summary['by_type'][$type] = ($summary['by_type'][$type] ?? 0) + 1;
        }

        return $summary;
    }

    /**
     * Get health status
     */
    public function getHealthStatus(): array
    {
        $status = [
            'overall' => 'healthy',
            'checks' => []
        ];

        // Check error rate
        $errorRate = $this->getErrorRate('1h');
        $status['checks']['error_rate'] = [
            'status' => $errorRate < 0.05 ? 'healthy' : ($errorRate < 0.1 ? 'warning' : 'critical'),
            'value' => $errorRate,
            'threshold' => 0.05
        ];

        // Check average response time
        $avgResponseTime = $this->getAverageResponseTime('1h');
        $status['checks']['response_time'] = [
            'status' => $avgResponseTime < 200 ? 'healthy' : ($avgResponseTime < 500 ? 'warning' : 'critical'),
            'value' => $avgResponseTime,
            'threshold' => 200
        ];

        // Check memory usage
        $memoryUsage = memory_get_usage(true) / 1024 / 1024; // MB
        $status['checks']['memory'] = [
            'status' => $memoryUsage < 128 ? 'healthy' : ($memoryUsage < 256 ? 'warning' : 'critical'),
            'value' => $memoryUsage,
            'threshold' => 128
        ];

        // Check cache health
        $cacheStats = $this->cache->getStats();
        $status['checks']['cache'] = [
            'status' => $cacheStats['discovery_valid'] ? 'healthy' : 'warning',
            'value' => $cacheStats['memory_cache_size'],
            'using_object_cache' => $cacheStats['using_object_cache']
        ];

        // Determine overall status
        $criticalCount = 0;
        $warningCount = 0;

        foreach ($status['checks'] as $check) {
            if ($check['status'] === 'critical')
                $criticalCount++;
            if ($check['status'] === 'warning')
                $warningCount++;
        }

        if ($criticalCount > 0) {
            $status['overall'] = 'critical';
        } elseif ($warningCount > 0) {
            $status['overall'] = 'warning';
        }

        return $status;
    }

    /**
     * Save metrics to database
     */
    private function saveMetrics(): void
    {
        if (empty($this->metrics)) {
            return;
        }

        $existing = get_option('fleekdash_metrics', []);
        $existing = array_merge($existing, $this->metrics);

        // Keep only last 10000 metrics
        if (count($existing) > 10000) {
            $existing = array_slice($existing, -10000);
        }

        update_option('fleekdash_metrics', $existing);
        $this->metrics = [];
    }

    /**
     * Save error to database
     */
    private function saveError(array $error): void
    {
        $errors = get_option('fleekdash_errors', []);
        $errors = is_array($errors) ? $errors : [];
        $errors[] = $error;

        // Keep only last 1000 errors
        if (count($errors) > 1000) {
            $errors = array_slice($errors, -1000);
        }

        update_option('fleekdash_errors', $errors);
    }

    /**
     * Get metrics for period
     */
    private function getMetrics(string $period): array
    {
        $all_metrics = get_option('fleekdash_metrics', []);
        $all_metrics = is_array($all_metrics) ? $all_metrics : [];
        $cutoff = $this->getPeriodCutoff($period);

        return array_filter($all_metrics, function ($metric) use ($cutoff) {
            return $metric['timestamp'] >= $cutoff;
        });
    }

    /**
     * Get errors for period
     */
    private function getErrors(string $period): array
    {
        $all_errors = get_option('fleekdash_errors', []);
        $all_errors = is_array($all_errors) ? $all_errors : [];
        $cutoff = $this->getPeriodCutoff($period);

        return array_filter($all_errors, function ($error) use ($cutoff) {
            return $error['timestamp'] >= $cutoff;
        });
    }

    /**
     * Get period cutoff timestamp
     */
    private function getPeriodCutoff(string $period): int
    {
        switch ($period) {
            case '1h':
                return time() - 3600;
            case '24h':
                return time() - 86400;
            case '7d':
                return time() - 604800;
            case '30d':
                return time() - 2592000;
            default:
                return time() - 86400;
        }
    }

    /**
     * Aggregate metrics
     */
    private function aggregateMetrics(array $metrics, string $key): array
    {
        $values = array_column(
            array_filter($metrics, function ($m) use ($key) {
                return $m['key'] === $key;
            }),
            'value',
        );

        if (empty($values)) {
            return ['count' => 0, 'avg' => 0, 'min' => 0, 'max' => 0];
        }

        return [
            'count' => count($values),
            'avg' => array_sum($values) / count($values),
            'min' => min($values),
            'max' => max($values)
        ];
    }

    /**
     * Calculate error rate
     */
    private function calculateErrorRate(array $metrics): float
    {
        $totalRequests = count(array_filter($metrics, function ($m) {
            return $m['key'] === 'api.request_count';
        }));

        $errors = count(array_filter($metrics, function ($m) {
            return $m['key'] === 'error.count';
        }));

        return $totalRequests > 0 ? $errors / $totalRequests : 0;
    }

    /**
     * Calculate cache hit rate
     */
    private function calculateCacheHitRate(array $metrics): float
    {
        // Implementation would depend on cache metrics structure
        return 0.95; // Placeholder
    }

    /**
     * Get error rate for period
     */
    private function getErrorRate(string $period): float
    {
        $metrics = $this->getMetrics($period);
        return $this->calculateErrorRate($metrics);
    }

    /**
     * Get average response time
     */
    private function getAverageResponseTime(string $period): float
    {
        $metrics = $this->getMetrics($period);
        $apiTimes = $this->aggregateMetrics($metrics, 'api.request_time');
        return $apiTimes['avg'] * 1000; // Convert to milliseconds
    }

    /**
     * Check error rate alert
     */
    private function checkErrorRateAlert(string $source): void
    {
        $errorRate = $this->getErrorRate('1h');

        if ($errorRate > 0.1) { // 10% error rate threshold
            $this->triggerAlert('high_error_rate', [
                'source' => $source,
                'error_rate' => $errorRate,
                'threshold' => 0.1
            ]);
        }
    }

    /**
     * Trigger alert
     */
    private function triggerAlert(string $type, array $data): void
    {
        $alert = [
            'type' => $type,
            'data' => $data,
            'timestamp' => time(),
            'acknowledged' => false
        ];

        $alerts = get_option('fleekdash_alerts', []);
        $alerts[] = $alert;

        update_option('fleekdash_alerts', $alerts);

        // Fire event for alert handling
        do_action('fleekdash_alert_triggered', $type, $data);
    }

    /**
     * Cleanup old metrics
     */
    public function cleanupMetrics(): void
    {
        $cutoff = time() - (30 * 24 * 3600); // 30 days

        $metrics = get_option('fleekdash_metrics', []);
        $metrics = is_array($metrics) ? $metrics : [];
        $metrics = array_filter($metrics, function ($metric) use ($cutoff) {
            return $metric['timestamp'] >= $cutoff;
        });
        update_option('fleekdash_metrics', $metrics);

        $errors = get_option('fleekdash_errors', []);
        $errors = is_array($errors) ? $errors : [];
        $errors = array_filter($errors, function ($error) use ($cutoff) {
            return $error['timestamp'] >= $cutoff;
        });
        update_option('fleekdash_errors', $errors);
    }

    /**
     * Register REST API routes
     */
    public function registerRestRoutes(): void
    {
        // Get health status
        register_rest_route('fleekdash/v1', '/monitoring/health', [
            'methods' => 'GET',
            'callback' => [$this, 'restGetHealth'],
            'permission_callback' => function () {
                return current_user_can('manage_options');
            }
        ]);

        // Get performance metrics
        register_rest_route('fleekdash/v1', '/monitoring/performance', [
            'methods' => 'GET',
            'callback' => [$this, 'restGetPerformance'],
            'permission_callback' => function () {
                return current_user_can('manage_options');
            }
        ]);

        // Get error summary
        register_rest_route('fleekdash/v1', '/monitoring/errors', [
            'methods' => 'GET',
            'callback' => [$this, 'restGetErrors'],
            'permission_callback' => function () {
                return current_user_can('manage_options');
            }
        ]);
    }

    /**
     * REST: Get health status
     */
    public function restGetHealth(): \WP_REST_Response
    {
        return rest_ensure_response([
            'success' => true,
            'data' => $this->getHealthStatus()
        ]);
    }

    /**
     * REST: Get performance metrics
     */
    public function restGetPerformance(\WP_REST_Request $request): \WP_REST_Response
    {
        $period = $request->get_param('period') ?: '24h';

        return rest_ensure_response([
            'success' => true,
            'data' => $this->getPerformanceMetrics($period)
        ]);
    }

    /**
     * REST: Get error summary
     */
    public function restGetErrors(\WP_REST_Request $request): \WP_REST_Response
    {
        $period = $request->get_param('period') ?: '24h';

        return rest_ensure_response([
            'success' => true,
            'data' => $this->getErrorSummary($period)
        ]);
    }
}
