<?php

declare(strict_types=1);

namespace App\Framework\Database\Profiling;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Logging\Logger;
use App\Framework\Logging\LogLevel;

/**
 * Query logger with multiple output formats and filtering
 */
final class QueryLogger
{
    private array $loggedQueries = [];

    private bool $logSlowQueriesOnly;

    private bool $logParameters;

    private bool $logStackTrace;

    private int $maxLoggedQueries;

    public function __construct(
        private readonly Logger $logger,
        private readonly Duration $slowQueryThreshold,
        bool $logSlowQueriesOnly = false,
        bool $logParameters = true,
        bool $logStackTrace = false,
        int $maxLoggedQueries = 1000
    ) {
        $this->logSlowQueriesOnly = $logSlowQueriesOnly;
        $this->logParameters = $logParameters;
        $this->logStackTrace = $logStackTrace;
        $this->maxLoggedQueries = $maxLoggedQueries;
    }

    /**
     * Create with default threshold
     */
    public static function withDefaults(Logger $logger): self
    {
        return new self(
            $logger,
            Duration::fromSeconds(1.0), // 1 second
        );
    }

    /**
     * Log a query profile
     */
    public function logQuery(QueryProfile $profile): void
    {
        // Skip if only logging slow queries and this isn't slow
        if ($this->logSlowQueriesOnly && ! $profile->isSlow) {
            return;
        }

        // Maintain maximum logged queries
        if (count($this->loggedQueries) >= $this->maxLoggedQueries) {
            array_shift($this->loggedQueries);
        }

        $this->loggedQueries[] = $profile;

        // Log to system logger
        $this->writeToSystemLog($profile);
    }

    /**
     * Write profile to system log
     */
    private function writeToSystemLog(QueryProfile $profile): void
    {
        $level = $this->getLogLevel($profile);
        $message = $this->formatLogMessage($profile);
        $context = $this->buildLogContext($profile);

        $this->logger->log($level, $message, $context);
    }

    /**
     * Determine log level based on profile
     */
    private function getLogLevel(QueryProfile $profile): LogLevel
    {
        if ($profile->error !== null) {
            return LogLevel::ERROR;
        }

        if ($profile->isSlow) {
            return LogLevel::WARNING;
        }

        if ($profile->executionTime->toMilliseconds() > 100) {
            return LogLevel::INFO;
        }

        return LogLevel::DEBUG;
    }

    /**
     * Format log message
     */
    private function formatLogMessage(QueryProfile $profile): string
    {
        $type = strtoupper($profile->getQueryType());
        $time = $profile->executionTime->toMilliseconds();

        $message = "[{$type}] Query executed in {$time}ms";

        if ($profile->affectedRows !== null) {
            $message .= " ({$profile->affectedRows} rows affected)";
        }

        if ($profile->isSlow) {
            $message .= " [SLOW QUERY]";
        }

        if ($profile->error) {
            $message .= " [ERROR]";
        }

        return $message;
    }

    /**
     * Build log context
     */
    private function buildLogContext(QueryProfile $profile): array
    {
        $context = [
            'profile_id' => $profile->id,
            'sql' => $profile->query->sql,
            'normalized_sql' => $profile->getNormalizedSql(),
            'execution_time_ms' => $profile->executionTime->toMilliseconds(),
            'memory_usage_bytes' => $profile->memoryUsage,
            'query_type' => $profile->getQueryType(),
            'is_slow' => $profile->isSlow,
            'performance_rating' => $profile->getPerformanceRating(),
            'complexity_score' => $profile->getComplexityScore(),
        ];

        if ($this->logParameters && ! $profile->query->parameters->isEmpty()) {
            $context['parameters'] = $this->sanitizeParameters($profile->query->parameters->toArray());
        }

        if ($profile->affectedRows !== null) {
            $context['affected_rows'] = $profile->affectedRows;
        }

        if ($profile->error !== null) {
            $context['error'] = $profile->error;
        }

        if ($this->logStackTrace) {
            $context['stack_trace'] = $this->captureStackTrace();
        }

        return $context;
    }

    /**
     * Sanitize parameters for logging (remove sensitive data)
     */
    private function sanitizeParameters(array $parameters): array
    {
        $sanitized = [];
        $sensitivePatterns = [
            'password', 'pwd', 'pass', 'secret', 'token', 'api_key', 'key',
            'auth', 'credential', 'salt', 'hash',
        ];

        foreach ($parameters as $key => $value) {
            $keyLower = strtolower((string) $key);
            $isSensitive = false;

            foreach ($sensitivePatterns as $pattern) {
                if (str_contains($keyLower, $pattern)) {
                    $isSensitive = true;

                    break;
                }
            }

            if ($isSensitive) {
                $sanitized[$key] = '[REDACTED]';
            } else {
                // Limit length of logged values
                if (is_string($value) && strlen($value) > 100) {
                    $sanitized[$key] = substr($value, 0, 100) . '...';
                } else {
                    $sanitized[$key] = $value;
                }
            }
        }

        return $sanitized;
    }

    /**
     * Capture stack trace (excluding internal profiling calls)
     */
    private function captureStackTrace(): array
    {
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);

        // Remove internal profiling frames
        $filtered = [];
        $skipClasses = [
            'App\\Framework\\Database\\Profiling\\QueryLogger',
            'App\\Framework\\Database\\Profiling\\QueryProfiler',
            'App\\Framework\\Database\\Profiling\\ProfilingConnection',
        ];

        foreach ($trace as $frame) {
            if (isset($frame['class']) && in_array($frame['class'], $skipClasses)) {
                continue;
            }

            $filtered[] = [
                'file' => $frame['file'] ?? 'unknown',
                'line' => $frame['line'] ?? 0,
                'function' => $frame['function'] ?? 'unknown',
                'class' => $frame['class'] ?? null,
            ];

            // Limit stack trace depth
            if (count($filtered) >= 10) {
                break;
            }
        }

        return $filtered;
    }

    /**
     * Get all logged queries
     */
    public function getLoggedQueries(): array
    {
        return $this->loggedQueries;
    }

    /**
     * Get slow queries
     */
    public function getSlowQueries(): array
    {
        return array_filter($this->loggedQueries, fn (QueryProfile $profile) => $profile->isSlow);
    }

    /**
     * Export queries to different formats
     */
    public function export(string $format = 'json'): string
    {
        return match (strtolower($format)) {
            'json' => $this->exportAsJson(),
            'csv' => $this->exportAsCsv(),
            'html' => $this->exportAsHtml(),
            'sql' => $this->exportAsSql(),
            default => throw new \InvalidArgumentException("Unsupported export format: $format")
        };
    }

    /**
     * Export as JSON
     */
    private function exportAsJson(): string
    {
        $data = [
            'metadata' => [
                'total_queries' => count($this->loggedQueries),
                'slow_queries' => count($this->getSlowQueries()),
                'export_timestamp' => date('c'),
                'slow_query_threshold_ms' => $this->slowQueryThreshold->toMilliseconds(),
            ],
            'queries' => array_map(fn (QueryProfile $profile) => $profile->toArray(), $this->loggedQueries),
        ];

        return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
    }

    /**
     * Export as CSV
     */
    private function exportAsCsv(): string
    {
        $csv = "ID,SQL,Query Type,Execution Time (ms),Memory Usage (bytes),Affected Rows,Is Slow,Error\n";

        foreach ($this->loggedQueries as $profile) {
            $csv .= sprintf(
                "%s,\"%s\",%s,%s,%s,%s,%s,\"%s\"\n",
                $profile->id,
                str_replace('"', '""', $profile->query->sql),
                $profile->getQueryType(),
                $profile->executionTime->toMilliseconds(),
                $profile->memoryUsage,
                $profile->affectedRows ?? '',
                $profile->isSlow ? 'Yes' : 'No',
                str_replace('"', '""', $profile->error ?? '')
            );
        }

        return $csv;
    }

    /**
     * Export as HTML
     */
    private function exportAsHtml(): string
    {
        $html = '<html><head><title>Query Log</title><style>
            table { border-collapse: collapse; width: 100%; }
            th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
            th { background-color: #f2f2f2; }
            .slow { background-color: #ffe6e6; }
            .error { background-color: #ffcccc; }
        </style></head><body>';

        $html .= '<h1>Database Query Log</h1>';
        $html .= '<p>Total Queries: ' . count($this->loggedQueries) . '</p>';
        $html .= '<p>Slow Queries: ' . count($this->getSlowQueries()) . '</p>';

        $html .= '<table><tr>
            <th>Time</th><th>Type</th><th>Duration</th><th>SQL</th><th>Memory</th><th>Rows</th>
        </tr>';

        foreach ($this->loggedQueries as $profile) {
            $rowClass = '';
            if ($profile->error) {
                $rowClass = 'error';
            } elseif ($profile->isSlow) {
                $rowClass = 'slow';
            }

            $html .= sprintf(
                '<tr class="%s"><td>%s</td><td>%s</td><td>%s ms</td><td>%s</td><td>%s</td><td>%s</td></tr>',
                $rowClass,
                $profile->startTimestamp->format('H:i:s.u'),
                strtoupper($profile->getQueryType()),
                $profile->executionTime->toMilliseconds(),
                htmlspecialchars(substr($profile->query->sql, 0, 100) . '...'),
                $profile->getMemoryUsageBytes()->toHumanReadable(),
                $profile->affectedRows ?? '-'
            );
        }

        $html .= '</table></body></html>';

        return $html;
    }

    /**
     * Export queries as SQL
     */
    private function exportAsSql(): string
    {
        $sql = "-- Database Query Log Export\n";
        $sql .= "-- Generated: " . date('c') . "\n";
        $sql .= "-- Total Queries: " . count($this->loggedQueries) . "\n\n";

        foreach ($this->loggedQueries as $profile) {
            $sql .= "-- Query ID: {$profile->id}\n";
            $sql .= "-- Execution Time: {$profile->executionTime->toMilliseconds()}ms\n";
            $sql .= "-- Query Type: " . strtoupper($profile->getQueryType()) . "\n";

            if ($profile->isSlow) {
                $sql .= "-- WARNING: SLOW QUERY\n";
            }

            if ($profile->error) {
                $sql .= "-- ERROR: {$profile->error}\n";
            }

            $sql .= $profile->query->sql . ";\n\n";
        }

        return $sql;
    }

    /**
     * Clear logged queries
     */
    public function clearLog(): void
    {
        $this->loggedQueries = [];
    }

    /**
     * Get log statistics
     */
    public function getLogStatistics(): array
    {
        $totalQueries = count($this->loggedQueries);
        $slowQueries = count($this->getSlowQueries());

        return [
            'total_queries' => $totalQueries,
            'slow_queries' => $slowQueries,
            'slow_query_percentage' => $totalQueries > 0 ? ($slowQueries / $totalQueries) * 100 : 0,
            'max_logged_queries' => $this->maxLoggedQueries,
            'log_slow_queries_only' => $this->logSlowQueriesOnly,
            'log_parameters' => $this->logParameters,
            'log_stack_trace' => $this->logStackTrace,
            'slow_query_threshold_ms' => $this->slowQueryThreshold->toMilliseconds(),
        ];
    }
}
