<?php

declare(strict_types=1);

namespace App\Framework\Database\ReadWrite;

use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Database\ReadWriteConnection;
use App\Framework\DateTime\Clock;

/**
 * Monitoring and metrics for Read/Write splitting
 */
final class ReadWriteMonitor
{
    private array $queryMetrics = [];

    private array $connectionMetrics = [];

    public function __construct(
        private readonly ReadWriteConnection $connection,
        private readonly Clock $clock
    ) {
    }

    /**
     * Record query execution
     */
    public function recordQuery(string $sql, float $executionTime, bool $usedWrite = false): void
    {
        $queryType = $this->getQueryType($sql);

        if (! isset($this->queryMetrics[$queryType])) {
            $this->queryMetrics[$queryType] = [
                'count' => 0,
                'write_count' => 0,
                'read_count' => 0,
                'total_time' => 0.0,
                'avg_time' => 0.0,
            ];
        }

        $this->queryMetrics[$queryType]['count']++;
        $this->queryMetrics[$queryType]['total_time'] += $executionTime;
        $this->queryMetrics[$queryType]['avg_time'] =
            $this->queryMetrics[$queryType]['total_time'] / $this->queryMetrics[$queryType]['count'];

        if ($usedWrite) {
            $this->queryMetrics[$queryType]['write_count']++;
        } else {
            $this->queryMetrics[$queryType]['read_count']++;
        }
    }

    /**
     * Get query type from SQL
     */
    private function getQueryType(string $sql): string
    {
        $sql = trim(strtoupper($sql));

        $writeOperations = ['INSERT', 'UPDATE', 'DELETE', 'CREATE', 'ALTER', 'DROP', 'TRUNCATE', 'REPLACE'];
        foreach ($writeOperations as $operation) {
            if (str_starts_with($sql, $operation)) {
                return strtolower($operation);
            }
        }

        if (str_starts_with($sql, 'SELECT')) {
            return 'select';
        }

        return 'other';
    }

    /**
     * Get comprehensive monitoring statistics
     */
    public function getStatistics(): array
    {
        $routerStats = $this->connection->getStatistics();

        return [
            'overview' => $this->getOverviewMetrics(),
            'router' => $routerStats,
            'queries' => $this->getQueryMetrics(),
            'connection_health' => $this->getConnectionHealthMetrics(),
            'performance' => $this->getPerformanceMetrics(),
            'recommendations' => $this->getRecommendations(),
        ];
    }

    /**
     * Get overview metrics
     */
    private function getOverviewMetrics(): array
    {
        $totalQueries = array_sum(array_column($this->queryMetrics, 'count'));
        $totalWrites = array_sum(array_column($this->queryMetrics, 'write_count'));
        $totalReads = array_sum(array_column($this->queryMetrics, 'read_count'));

        $readWriteRatio = $totalQueries > 0
            ? Percentage::fromRatio($totalReads, $totalQueries)
            : Percentage::zero();

        return [
            'total_queries' => $totalQueries,
            'read_queries' => $totalReads,
            'write_queries' => $totalWrites,
            'read_write_ratio' => $readWriteRatio->format(),
            'has_advanced_routing' => $this->connection->hasAdvancedRouting(),
            'replica_count' => count($this->connection->getReadConnections()),
        ];
    }

    /**
     * Get query metrics
     */
    private function getQueryMetrics(): array
    {
        $metrics = [];

        foreach ($this->queryMetrics as $queryType => $stats) {
            $readRatio = $stats['count'] > 0
                ? Percentage::fromRatio($stats['read_count'], $stats['count'])
                : Percentage::zero();

            $metrics[$queryType] = [
                'total_count' => $stats['count'],
                'write_count' => $stats['write_count'],
                'read_count' => $stats['read_count'],
                'read_ratio' => $readRatio->format(),
                'average_execution_time' => round($stats['avg_time'], 4),
                'total_execution_time' => round($stats['total_time'], 4),
            ];
        }

        return $metrics;
    }

    /**
     * Get connection health metrics
     */
    private function getConnectionHealthMetrics(): array
    {
        $writeConnection = $this->connection->getWriteConnection();
        $readConnections = $this->connection->getReadConnections();

        return [
            'write_connection' => $this->testConnectionHealth($writeConnection),
            'read_connections' => array_map(
                fn ($conn) => $this->testConnectionHealth($conn),
                $readConnections
            ),
        ];
    }

    /**
     * Test connection health
     */
    private function testConnectionHealth($connection): array
    {
        $startTime = $this->clock->time();
        $healthy = false;
        $error = null;

        try {
            $connection->queryScalar('SELECT 1');
            $healthy = true;
        } catch (\Throwable $e) {
            $error = $e->getMessage();
        }

        $responseTime = $this->clock->time()->diff($startTime)->toSeconds();

        return [
            'healthy' => $healthy,
            'response_time_ms' => round($responseTime * 1000, 2),
            'error' => $error,
        ];
    }

    /**
     * Get performance metrics
     */
    private function getPerformanceMetrics(): array
    {
        $totalQueries = array_sum(array_column($this->queryMetrics, 'count'));
        $totalTime = array_sum(array_column($this->queryMetrics, 'total_time'));

        $averageQueryTime = $totalQueries > 0 ? $totalTime / $totalQueries : 0;

        return [
            'total_execution_time' => round($totalTime, 4),
            'average_query_time' => round($averageQueryTime, 4),
            'queries_per_second' => $totalTime > 0 ? round($totalQueries / $totalTime, 2) : 0,
            'slowest_query_type' => $this->getSlowestQueryType(),
            'fastest_query_type' => $this->getFastestQueryType(),
        ];
    }

    /**
     * Get slowest query type
     */
    private function getSlowestQueryType(): array
    {
        $slowest = null;
        $maxTime = 0;

        foreach ($this->queryMetrics as $type => $stats) {
            if ($stats['avg_time'] > $maxTime) {
                $maxTime = $stats['avg_time'];
                $slowest = ['type' => $type, 'avg_time' => round($maxTime, 4)];
            }
        }

        return $slowest ?? ['type' => 'none', 'avg_time' => 0];
    }

    /**
     * Get fastest query type
     */
    private function getFastestQueryType(): array
    {
        $fastest = null;
        $minTime = PHP_FLOAT_MAX;

        foreach ($this->queryMetrics as $type => $stats) {
            if ($stats['count'] > 0 && $stats['avg_time'] < $minTime) {
                $minTime = $stats['avg_time'];
                $fastest = ['type' => $type, 'avg_time' => round($minTime, 4)];
            }
        }

        return $fastest ?? ['type' => 'none', 'avg_time' => 0];
    }

    /**
     * Get optimization recommendations
     */
    private function getRecommendations(): array
    {
        $recommendations = [];
        $totalQueries = array_sum(array_column($this->queryMetrics, 'count'));
        $totalReads = array_sum(array_column($this->queryMetrics, 'read_count'));

        // Check read/write ratio
        if ($totalQueries > 0) {
            $readRatio = ($totalReads / $totalQueries) * 100;

            if ($readRatio < 60) {
                $recommendations[] = [
                    'type' => 'warning',
                    'category' => 'load_distribution',
                    'message' => sprintf(
                        'Low read ratio (%.1f%%). Consider optimizing queries to use read replicas more effectively.',
                        $readRatio
                    ),
                ];
            }

            if ($readRatio > 90) {
                $recommendations[] = [
                    'type' => 'info',
                    'category' => 'optimization',
                    'message' => sprintf(
                        'Excellent read ratio (%.1f%%). Read/write splitting is working well.',
                        $readRatio
                    ),
                ];
            }
        }

        // Check replica count
        $replicaCount = count($this->connection->getReadConnections());
        if ($replicaCount < 2 && $totalQueries > 1000) {
            $recommendations[] = [
                'type' => 'suggestion',
                'category' => 'scalability',
                'message' => 'Consider adding more read replicas for better load distribution and fault tolerance.',
            ];
        }

        // Check for advanced routing
        if (! $this->connection->hasAdvancedRouting()) {
            $recommendations[] = [
                'type' => 'info',
                'category' => 'features',
                'message' => 'Advanced routing features (health checks, lag detection) are not enabled. Enable for better reliability.',
            ];
        }

        return $recommendations;
    }

    /**
     * Reset metrics
     */
    public function resetMetrics(): void
    {
        $this->queryMetrics = [];
        $this->connectionMetrics = [];
    }

    /**
     * Get metrics for specific query type
     */
    public function getQueryTypeMetrics(string $queryType): ?array
    {
        return $this->queryMetrics[$queryType] ?? null;
    }
}
