<?php

declare(strict_types=1);

namespace App\Framework\Database\Profiling;

use App\Framework\Core\ValueObjects\Byte;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Database\Events\Timestamp;
use App\Framework\Database\ValueObjects\SqlQuery;

/**
 * Immutable query profile with execution metrics
 */
final readonly class QueryProfile
{
    public function __construct(
        public string $id,
        public SqlQuery $query,
        public Duration $executionTime,
        public Timestamp $startTimestamp,
        public Timestamp $endTimestamp,
        public int $memoryUsage,
        public int $peakMemoryUsage,
        public ?int $affectedRows = null,
        public ?string $error = null,
        public bool $isSlow = false
    ) {
    }

    /**
     * Get query type (SELECT, INSERT, UPDATE, etc.)
     */
    public function getQueryType(): string
    {
        $sql = trim(strtoupper($this->query->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';
        }

        if (str_starts_with($sql, 'SHOW')) {
            return 'show';
        }

        if (str_starts_with($sql, 'DESCRIBE') || str_starts_with($sql, 'DESC')) {
            return 'describe';
        }

        if (str_starts_with($sql, 'EXPLAIN')) {
            return 'explain';
        }

        return 'other';
    }

    /**
     * Check if query was successful
     */
    public function isSuccessful(): bool
    {
        return $this->error === null;
    }

    /**
     * Check if query is a read operation
     */
    public function isReadQuery(): bool
    {
        return in_array($this->getQueryType(), ['select', 'show', 'describe', 'explain']);
    }

    /**
     * Check if query is a write operation
     */
    public function isWriteQuery(): bool
    {
        return in_array($this->getQueryType(), ['insert', 'update', 'delete', 'create', 'alter', 'drop', 'truncate', 'replace']);
    }

    /**
     * Get memory usage as Byte object
     */
    public function getMemoryUsageBytes(): Byte
    {
        return Byte::fromBytes($this->memoryUsage);
    }

    /**
     * Get peak memory usage as Byte object
     */
    public function getPeakMemoryUsageBytes(): Byte
    {
        return Byte::fromBytes($this->peakMemoryUsage);
    }

    /**
     * Get normalized SQL (for grouping similar queries)
     */
    public function getNormalizedSql(): string
    {
        // Remove extra whitespace and normalize
        $normalized = preg_replace('/\s+/', ' ', trim($this->query->sql));

        // Replace parameter placeholders and values with ? for grouping
        $normalized = preg_replace('/\$\d+/', '?', $normalized);
        $normalized = preg_replace('/(=|IN|LIKE)\s*([\'"][^\'\"]*[\'"]|\d+(\.\d+)?)/i', '$1 ?', $normalized);

        return $normalized;
    }

    /**
     * Get query complexity score (simplified heuristic)
     */
    public function getComplexityScore(): int
    {
        $sql = strtoupper($this->query->sql);
        $score = 1;

        // Count JOINs
        $score += substr_count($sql, 'JOIN');

        // Count subqueries
        $score += substr_count($sql, 'SELECT') - 1; // Subtract main SELECT

        // Count WHERE conditions
        $score += substr_count($sql, 'WHERE');
        $score += substr_count($sql, 'AND');
        $score += substr_count($sql, 'OR');

        // Count aggregations
        $score += substr_count($sql, 'GROUP BY');
        $score += substr_count($sql, 'ORDER BY');
        $score += substr_count($sql, 'HAVING');

        // Count functions
        $functions = ['COUNT', 'SUM', 'AVG', 'MAX', 'MIN', 'CONCAT', 'SUBSTRING'];
        foreach ($functions as $func) {
            $score += substr_count($sql, $func);
        }

        return max(1, $score);
    }

    /**
     * Estimate query performance rating
     */
    public function getPerformanceRating(): string
    {
        $timeMs = $this->executionTime->toMilliseconds();

        return match (true) {
            $timeMs < 10 => 'excellent',
            $timeMs < 100 => 'good',
            $timeMs < 500 => 'acceptable',
            $timeMs < 1000 => 'slow',
            default => 'very_slow'
        };
    }

    /**
     * Convert to array for serialization
     */
    public function toArray(): array
    {
        return [
            'id' => $this->id,
            'sql' => $this->query->sql,
            'normalized_sql' => $this->getNormalizedSql(),
            'parameters' => $this->query->parameters->toArray(),
            'query_type' => $this->getQueryType(),
            'execution_time_ms' => $this->executionTime->toMilliseconds(),
            'execution_time_seconds' => $this->executionTime->toSeconds(),
            'start_time' => $this->startTimestamp->format('Y-m-d H:i:s.u'),
            'end_time' => $this->endTimestamp->format('Y-m-d H:i:s.u'),
            'memory_usage' => $this->getMemoryUsageBytes()->toHumanReadable(),
            'memory_usage_bytes' => $this->memoryUsage,
            'peak_memory_usage' => $this->getPeakMemoryUsageBytes()->toHumanReadable(),
            'peak_memory_usage_bytes' => $this->peakMemoryUsage,
            'affected_rows' => $this->affectedRows,
            'error' => $this->error,
            'is_slow' => $this->isSlow,
            'is_successful' => $this->isSuccessful(),
            'is_read_query' => $this->isReadQuery(),
            'is_write_query' => $this->isWriteQuery(),
            'complexity_score' => $this->getComplexityScore(),
            'performance_rating' => $this->getPerformanceRating(),
        ];
    }

    /**
     * Get formatted summary string
     */
    public function getSummary(): string
    {
        $type = strtoupper($this->getQueryType());
        $time = $this->executionTime->toMilliseconds();
        $rating = ucfirst($this->getPerformanceRating());

        $summary = "[$type] {$time}ms - {$rating}";

        if ($this->affectedRows !== null) {
            $summary .= " ({$this->affectedRows} rows)";
        }

        if ($this->error) {
            $summary .= " - ERROR: " . substr($this->error, 0, 50);
        }

        return $summary;
    }
}
