<?php

namespace App\Filters;

use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Config\Services;
use Throwable;

class ErrorLogger implements FilterInterface
{
    public function before(RequestInterface $request, $arguments = null)
    {
        // Özel hata yakalayıcıyı ayarla
        $this->setupErrorHandlers();
        return $request;
    }

    public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
    {
        // After filter'da yapılacak bir işlem yok
        return $response;
    }

    private function setupErrorHandlers()
    {
        // PHP Hatalarını yakala
        set_error_handler(function($errno, $errstr, $errfile, $errline) {
            $this->logError($errno, $errstr, $errfile, $errline);
            // Hata işlemeyi devam ettir
            return false;
        });

        // İşlenmemiş istisnalar için
        set_exception_handler(function(Throwable $exception) {
            $this->logException($exception);
        });

        // Betik tamamlandığında
        register_shutdown_function(function() {
            $error = error_get_last();
            if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
                $this->logError($error['type'], $error['message'], $error['file'], $error['line']);
            }
        });
    }

    private function logError($errno, $errstr, $errfile, $errline)
    {
        // Hata tipini belirle
        $errorType = $this->getErrorType($errno);
        
        // Hata mesajını oluştur
        $message = "[$errorType] $errstr in $errfile on line $errline";
        
        // Hata log dosyasına yaz
        $logFile = WRITEPATH . 'logs/php_errors.log';
        file_put_contents($logFile, date('Y-m-d H:i:s') . " - $message\n", FILE_APPEND);
        
        // Veritabanına kaydet
        $this->saveToDatabase($errno, $errstr, $errfile, $errline);
    }

    private function logException(Throwable $exception)
    {
        // İstisna bilgilerini al
        $message = $exception->getMessage();
        $file = $exception->getFile();
        $line = $exception->getLine();
        $trace = $exception->getTraceAsString();
        
        // Hata log dosyasına yaz
        $logFile = WRITEPATH . 'logs/php_exceptions.log';
        $logMessage = date('Y-m-d H:i:s') . " - [EXCEPTION] $message in $file on line $line\n$trace\n";
        file_put_contents($logFile, $logMessage, FILE_APPEND);
        
        // Veritabanına kaydet - istisna olarak
        $this->saveExceptionToDatabase($exception);
    }

    private function getErrorType($errno)
    {
        switch ($errno) {
            case E_ERROR: return 'E_ERROR';
            case E_WARNING: return 'E_WARNING';
            case E_PARSE: return 'E_PARSE';
            case E_NOTICE: return 'E_NOTICE';
            case E_CORE_ERROR: return 'E_CORE_ERROR';
            case E_CORE_WARNING: return 'E_CORE_WARNING';
            case E_COMPILE_ERROR: return 'E_COMPILE_ERROR';
            case E_COMPILE_WARNING: return 'E_COMPILE_WARNING';
            case E_USER_ERROR: return 'E_USER_ERROR';
            case E_USER_WARNING: return 'E_USER_WARNING';
            case E_USER_NOTICE: return 'E_USER_NOTICE';
            case E_STRICT: return 'E_STRICT';
            case E_RECOVERABLE_ERROR: return 'E_RECOVERABLE_ERROR';
            case E_DEPRECATED: return 'E_DEPRECATED';
            case E_USER_DEPRECATED: return 'E_USER_DEPRECATED';
            default: return 'UNKNOWN';
        }
    }

    private function getErrorSeverity($errno)
    {
        $severities = [
            E_ERROR => 'CRITICAL',
            E_PARSE => 'CRITICAL',
            E_CORE_ERROR => 'CRITICAL',
            E_COMPILE_ERROR => 'CRITICAL',
            E_USER_ERROR => 'CRITICAL',
            E_RECOVERABLE_ERROR => 'CRITICAL',
            
            E_WARNING => 'WARNING',
            E_CORE_WARNING => 'WARNING',
            E_COMPILE_WARNING => 'WARNING',
            E_USER_WARNING => 'WARNING',
            
            E_NOTICE => 'NOTICE',
            E_USER_NOTICE => 'NOTICE',
            E_STRICT => 'NOTICE',
            E_DEPRECATED => 'NOTICE',
            E_USER_DEPRECATED => 'NOTICE'
        ];
        
        return $severities[$errno] ?? 'WARNING';
    }

    private function saveToDatabase($errno, $errstr, $errfile, $errline)
    {
        try {
            // Request ve response bilgilerini al
            $request = Services::request();
            $response = Services::response();
            
            // API isteği yapıldığı anı al
            $requestTime = date('Y-m-d H:i:s');
            
            // URI bilgisini al
            $uri = $request->getUri()->getPath();
            
            // Hata bilgilerini hazırla
            $logData = [
                'request_time' => $requestTime,
                'ip_address' => $request->getIPAddress(),
                'uri' => $uri,
                'method' => $request->getMethod(),
                'request_headers' => json_encode($this->getFilteredHeaders($request)),
                'request_body' => $request->getBody(),
                'response_code' => $response->getStatusCode(),
                'response_body' => $response->getBody(),
                'execution_time' => microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'],
                'user_id' => $this->getUserId($request),
                'user_agent' => $request->getUserAgent()->getAgentString(),
                'error_type' => $this->getErrorType($errno),
                'error_severity' => $this->getErrorSeverity($errno),
                'error_message' => "$errstr in $errfile on line $errline"
            ];
            
            // Log modelini kullanarak kaydet
            $logModel = model('App\Models\ApiLogModel');
            $logModel->insert($logData);
            
        } catch (Throwable $e) {
            // Loglama hatalarını dosyaya yaz
            $logFile = WRITEPATH . 'logs/logger_errors.log';
            file_put_contents($logFile, date('Y-m-d H:i:s') . " - Loglama hatası: " . $e->getMessage() . "\n", FILE_APPEND);
        }
    }

    private function saveExceptionToDatabase(Throwable $exception)
    {
        try {
            // Request ve response bilgilerini al
            $request = Services::request();
            $response = Services::response();
            
            // API isteği yapıldığı anı al
            $requestTime = date('Y-m-d H:i:s');
            
            // URI bilgisini al
            $uri = $request->getUri()->getPath();
            
            // İstisna bilgilerini hazırla
            $logData = [
                'request_time' => $requestTime,
                'ip_address' => $request->getIPAddress(),
                'uri' => $uri,
                'method' => $request->getMethod(),
                'request_headers' => json_encode($this->getFilteredHeaders($request)),
                'request_body' => $request->getBody(),
                'response_code' => 500, // İstisna olduğu için 500 kabul ediyoruz
                'response_body' => json_encode(['error' => $exception->getMessage()]),
                'execution_time' => microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'],
                'user_id' => $this->getUserId($request),
                'user_agent' => $request->getUserAgent()->getAgentString(),
                'error_type' => get_class($exception),
                'error_severity' => 'CRITICAL',
                'error_message' => $exception->getMessage() . " in " . $exception->getFile() . " on line " . $exception->getLine() . "\n" . $exception->getTraceAsString()
            ];
            
            // Log modelini kullanarak kaydet
            $logModel = model('App\Models\ApiLogModel');
            $logModel->insert($logData);
            
        } catch (Throwable $e) {
            // Loglama hatalarını dosyaya yaz
            $logFile = WRITEPATH . 'logs/logger_errors.log';
            file_put_contents($logFile, date('Y-m-d H:i:s') . " - İstisna loglama hatası: " . $e->getMessage() . "\n", FILE_APPEND);
        }
    }

    private function getFilteredHeaders(RequestInterface $request)
    {
        // İstek başlıklarını al, hassas bilgileri filtrele
        $headers = $request->getHeaders();
        $filteredHeaders = [];
        
        foreach ($headers as $name => $value) {
            if (strtolower($name) !== 'authorization' && strtolower($name) !== 'cookie') {
                $filteredHeaders[$name] = $value->getValueLine();
            }
        }
        
        return $filteredHeaders;
    }

    private function getUserId(RequestInterface $request)
    {
        // Kullanıcı ID'sini almaya çalış
        $userId = null;
        $tokenParam = $request->getGet('token');
        
        if (!empty($tokenParam)) {
            try {
                $tokenModel = model('App\Models\TokenModel');
                $token = $tokenModel->where('token', $tokenParam)->first();
                if ($token) {
                    $userId = $token['user_id'];
                }
            } catch (Throwable $e) {
                // Token sorgusunda hata olursa loglama
                $logFile = WRITEPATH . 'logs/logger_errors.log';
                file_put_contents($logFile, date('Y-m-d H:i:s') . " - Token sorgusunda hata: " . $e->getMessage() . "\n", FILE_APPEND);
            }
        }
        
        // JSON request body'den kullanıcı kimliğini kontrol et
        if ($userId === null) {
            $requestBody = $request->getBody();
            if (!empty($requestBody)) {
                $jsonBody = json_decode($requestBody, true);
                if (json_last_error() === JSON_ERROR_NONE && isset($jsonBody['user_id'])) {
                    $userId = $jsonBody['user_id'];
                }
            }
        }
        
        return $userId;
    }
}