<?php

declare(strict_types=1);

namespace App\Framework\Performance\Middleware;

use App\Framework\Database\Middleware\QueryContext;
use App\Framework\Database\Middleware\QueryMiddleware;
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
use App\Framework\Performance\PerformanceCategory;
use App\Framework\Performance\PerformanceConfig;

final readonly class DatabasePerformanceMiddleware implements QueryMiddleware
{
    public function __construct(
        private PerformanceCollectorInterface $collector,
        private PerformanceConfig $config
    ) {
    }

    public function process(QueryContext $context, callable $next): mixed
    {
        if (! $this->config->isTrackingEnabled(PerformanceCategory::DATABASE)) {
            return $next();
        }

        $query = $context->sql;
        $bindings = $context->parameters;
        $queryType = $this->getQueryType($query);
        $queryKey = "db_query_{$queryType}";

        $performanceContext = [
            'query_type' => $queryType,
            'bindings_count' => count($bindings),
            'query_hash' => md5($query),
        ];

        // Add query text for detailed reports (careful with sensitive data)
        if ($this->config->detailedReports) {
            $performanceContext['query'] = $this->sanitizeQuery($query);
            $performanceContext['bindings'] = $this->sanitizeBindings($bindings);
        }

        $this->collector->startTiming($queryKey, PerformanceCategory::DATABASE, $performanceContext);

        // Count database queries
        $this->collector->increment('database_queries_total', PerformanceCategory::DATABASE, 1, $performanceContext);
        $this->collector->increment("database_queries_{$queryType}", PerformanceCategory::DATABASE, 1, $performanceContext);

        try {
            $result = $next();

            // Record successful query
            $this->collector->increment('database_queries_success', PerformanceCategory::DATABASE, 1, $performanceContext);

            return $result;
        } catch (\Throwable $e) {
            // Record failed query
            $this->collector->increment(
                'database_queries_failed',
                PerformanceCategory::DATABASE,
                1,
                array_merge($performanceContext, ['error' => $e::class])
            );

            throw $e;
        } finally {
            $duration = $this->collector->endTiming($queryKey);

            // Check for slow queries
            if ($this->config->isSlowQuery($duration)) {
                $this->collector->increment(
                    'database_slow_queries',
                    PerformanceCategory::DATABASE,
                    1,
                    array_merge($performanceContext, ['duration_ms' => $duration])
                );
            }
        }
    }

    public function getPriority(): int
    {
        return 100; // Performance middleware should run with moderate priority
    }

    private function getQueryType(string $query): string
    {
        $query = trim(strtoupper($query));

        if (str_starts_with($query, 'SELECT')) {
            return 'select';
        }
        if (str_starts_with($query, 'INSERT')) {
            return 'insert';
        }
        if (str_starts_with($query, 'UPDATE')) {
            return 'update';
        }
        if (str_starts_with($query, 'DELETE')) {
            return 'delete';
        }
        if (str_starts_with($query, 'CREATE')) {
            return 'create';
        }
        if (str_starts_with($query, 'DROP')) {
            return 'drop';
        }
        if (str_starts_with($query, 'ALTER')) {
            return 'alter';
        }
        if (str_starts_with($query, 'SHOW')) {
            return 'show';
        }
        if (str_starts_with($query, 'DESCRIBE') || str_starts_with($query, 'DESC')) {
            return 'describe';
        }

        return 'other';
    }

    private function sanitizeQuery(string $query): string
    {
        // Remove potential sensitive data from queries for logging
        $query = preg_replace('/\b\d{4}[-\s]\d{4}[-\s]\d{4}[-\s]\d{4}\b/', '****-****-****-****', $query);
        $query = preg_replace('/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/', '***@***.***', $query);

        // Truncate very long queries
        if (strlen($query) > 500) {
            $query = substr($query, 0, 500) . '...';
        }

        return $query;
    }

    private function sanitizeBindings(array $bindings): array
    {
        // Sanitize binding values to prevent sensitive data exposure
        return array_map(function ($value) {
            if (is_string($value)) {
                // Hide potential sensitive strings
                if (strlen($value) > 50) {
                    return '[LONG_STRING:' . strlen($value) . '_chars]';
                }
                if (preg_match('/^\d{4}[-\s]\d{4}[-\s]\d{4}[-\s]\d{4}$/', $value)) {
                    return '[CREDIT_CARD]';
                }
                if (filter_var($value, FILTER_VALIDATE_EMAIL)) {
                    return '[EMAIL]';
                }
            }

            return $value;
        }, $bindings);
    }
}
