<?php

namespace App\Services;

use App\Models\ErrorLog;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;

class ErrorLogService
{
    private const MAX_MESSAGE_LENGTH = 65535;
    private const MAX_STACK_LENGTH = 50000;
    private const MAX_REQUEST_JSON_LENGTH = 50000;

    /** Truncate string for DB text columns and ensure valid UTF-8 */
    private static function truncate(string $s, int $max): string
    {
        $s = mb_convert_encoding($s, 'UTF-8', 'UTF-8') ?: $s;
        return mb_strlen($s) > $max ? mb_substr($s, 0, $max) . '…' : $s;
    }

    /** Build request_data that is small and JSON-safe so insert never fails */
    private static function safeRequestData(?Request $request): array
    {
        if (!$request) {
            return [];
        }
        try {
            $body = $request->all();
            $query = $request->query();
            $encoded = json_encode(['body' => $body, 'query' => $query], JSON_THROW_ON_ERROR | JSON_INVALID_UTF8_SUBSTITUTE);
            if (strlen($encoded) > self::MAX_REQUEST_JSON_LENGTH) {
                return [
                    'body_truncated' => true,
                    'items_count' => is_array($body['items'] ?? null) ? count($body['items']) : null,
                    'keys' => array_keys($body),
                ];
            }
            return json_decode($encoded, true, 512, JSON_THROW_ON_ERROR);
        } catch (\Throwable $e) {
            return ['_encode_error' => $e->getMessage(), 'endpoint' => $request->fullUrl()];
        }
    }

    /**
     * Log an error to the database
     */
    public static function log(
        \Throwable $exception,
        ?Request $request = null,
        ?string $source = 'Backend',
        ?int $statusCode = 500,
        ?array $additionalData = []
    ): ?ErrorLog {
        $request = $request ?? request();
        
        try {
            // Check if error_logs table exists
            if (!Schema::hasTable('error_logs')) {
                // Table doesn't exist, just log to file
                Log::error('Error logging failed: error_logs table does not exist', [
                    'error' => $exception->getMessage(),
                    'endpoint' => $request ? $request->fullUrl() : 'N/A',
                    'stack_trace' => $exception->getTraceAsString(),
                ]);
                return null;
            }

            $errorMessage = self::truncate($exception->getMessage(), self::MAX_MESSAGE_LENGTH);
            $stackTrace = self::truncate($exception->getTraceAsString(), self::MAX_STACK_LENGTH);
            $errorDetails = array_merge([
                'file' => $exception->getFile(),
                'line' => $exception->getLine(),
                'code' => $exception->getCode(),
            ], $additionalData);
            foreach ($errorDetails as $k => $v) {
                if (!is_scalar($v)) {
                    $errorDetails[$k] = is_array($v) ? json_encode($v) : (string) $v;
                }
            }
            
            $errorLog = ErrorLog::create([
                'source' => $source,
                'endpoint' => $request ? $request->fullUrl() : null,
                'method' => $request ? $request->method() : 'POST',
                'error_type' => get_class($exception),
                'error_message' => $errorMessage,
                'error_details' => $errorDetails,
                'stack_trace' => $stackTrace,
                'request_data' => self::safeRequestData($request),
                'user_id' => $request ? ($request->user()?->id) : null,
                'ip_address' => $request ? $request->ip() : null,
                'user_agent' => $request ? self::truncate((string) $request->userAgent(), 500) : null,
                'status_code' => $statusCode,
                'resolved' => false,
            ]);

            // Also log to Laravel's log file
            Log::error('Error logged to database', [
                'error_log_id' => $errorLog->id,
                'error' => $exception->getMessage(),
                'endpoint' => $request ? $request->fullUrl() : 'N/A',
            ]);

            return $errorLog;
        } catch (\Exception $e) {
            // If database logging fails, just log to file
            Log::error('Failed to log error to database', [
                'original_error' => $exception->getMessage(),
                'logging_error' => $e->getMessage(),
                'endpoint' => $request ? $request->fullUrl() : 'N/A',
            ]);
            // Return null if logging fails (caller should handle this)
            return null;
        }
    }

    /**
     * Log a frontend error
     */
    public static function logFrontend(
        string $message,
        ?array $details = null,
        ?string $endpoint = null,
        ?Request $request = null
    ): ?ErrorLog {
        try {
            // Check if error_logs table exists
            if (!Schema::hasTable('error_logs')) {
                // Table doesn't exist, just log to file
                Log::error('Frontend error (error_logs table does not exist)', [
                    'message' => $message,
                    'endpoint' => $endpoint ?? ($request ? $request->fullUrl() : 'N/A'),
                ]);
                return null;
            }
            
            $request = $request ?? request();
            $msg = self::truncate($message, self::MAX_MESSAGE_LENGTH);
            $stack = isset($details['stack']) ? self::truncate((string) $details['stack'], self::MAX_STACK_LENGTH) : null;
            $reqData = $details['request_data'] ?? [];
            if ($request && empty($reqData)) {
                $reqData = self::safeRequestData($request);
            }

            return ErrorLog::create([
                'source' => 'Frontend',
                'endpoint' => $endpoint ?? ($request ? $request->fullUrl() : null),
                'method' => ($request && $request->has('method')) ? $request->input('method') : ($request ? $request->method() : 'POST'),
                'error_type' => 'Frontend Error',
                'error_message' => $msg,
                'error_details' => $details ?? [],
                'request_data' => $reqData,
                'stack_trace' => $stack,
                'user_id' => $request ? ($request->user()?->id) : null,
                'ip_address' => $request ? $request->ip() : null,
                'user_agent' => $request ? self::truncate((string) $request->userAgent(), 500) : null,
                'status_code' => $details['status'] ?? 500,
                'resolved' => false,
            ]);
        } catch (\Exception $e) {
            // If database logging fails, just log to file
            Log::error('Failed to log frontend error to database', [
                'message' => $message,
                'logging_error' => $e->getMessage(),
            ]);
            return null;
        }
    }
}
