<?php

namespace Modules\AICore\App\Services;

use App\Services\Settings\ModuleSettingsService;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Modules\AICore\App\Models\AIModel;
use Modules\AICore\App\Models\AIModuleConfiguration;
use Modules\AICore\App\Models\AIProvider;
use Modules\AICore\App\Models\AIRequestLog;

class AIRequestService
{
    protected Client $httpClient;

    protected AIProviderService $providerService;

    protected AIUsageTracker $usageTracker;

    protected ModuleSettingsService $settingsService;

    public function __construct(
        AIProviderService $providerService,
        AIUsageTracker $usageTracker,
        ModuleSettingsService $settingsService
    ) {
        $this->settingsService = $settingsService;
        $timeout = $this->settingsService->get('AICore', 'request_timeout', 30);
        $this->httpClient = new Client(['timeout' => $timeout]);
        $this->providerService = $providerService;
        $this->usageTracker = $usageTracker;
    }

    /**
     * Send a chat completion request
     */
    public function chat(string $message, array $context = [], array $options = []): array
    {
        // Check if AI is enabled globally
        if (! $this->settingsService->get('AICore', 'ai_enabled', true)) {
            throw new \Exception('AI functionality is currently disabled. Please contact your administrator.');
        }

        // Check cache if enabled
        $cacheKey = null;
        if ($this->settingsService->get('AICore', 'cache_enabled', false)) {
            $cacheKey = 'ai_response:'.md5(json_encode([
                'message' => $message,
                'context' => $context,
                'options' => $options,
            ]));

            $cachedResponse = Cache::get($cacheKey);
            if ($cachedResponse) {
                return $cachedResponse;
            }
        }

        // First check if there's a module-specific configuration
        $moduleName = $options['module_name'] ?? 'AICore';
        $model = null;

        // Try to get module-specific model configuration
        $moduleConfig = AIModuleConfiguration::where('module_name', $moduleName)
            ->where('is_active', true)
            ->first();

        if ($moduleConfig && $moduleConfig->default_model_id) {
            // Use module-specific model if configured
            $model = AIModel::with('provider')
                ->where('id', $moduleConfig->default_model_id)
                ->where('is_active', true)
                ->first();

            // Apply module-specific settings to options
            if ($moduleConfig->max_tokens_limit) {
                $options['max_tokens'] = $options['max_tokens'] ?? $moduleConfig->max_tokens_limit;
            }
            if ($moduleConfig->temperature_default) {
                $options['temperature'] = $options['temperature'] ?? $moduleConfig->temperature_default;
            }
            if ($moduleConfig->streaming_enabled) {
                $options['streaming'] = $options['streaming'] ?? $moduleConfig->streaming_enabled;
            }
        }

        // If no module-specific model or module config not found, fall back to auto-selection
        if (! $model) {
            $providerType = $options['provider_type'] ?? null;

            // If module config has a default provider, use it
            if ($moduleConfig && $moduleConfig->default_provider_id) {
                $provider = AIProvider::find($moduleConfig->default_provider_id);
                if ($provider) {
                    $providerType = $provider->type;
                }
            }

            $model = $this->providerService->getBestModelForTask('text', $options['max_tokens'] ?? null, $providerType);
        }

        if (! $model) {
            $errorMsg = isset($providerType)
                ? "No available AI model for provider type: {$providerType}"
                : "No available AI model for module: {$moduleName}";
            throw new \Exception($errorMsg);
        }

        $startTime = microtime(true);

        // Create request log entry only if logging is enabled
        $requestLog = null;
        if ($this->settingsService->get('AICore', 'log_requests', true)) {
            $requestLog = AIRequestLog::create([
                'user_id' => auth()->id(),
                'module_name' => $moduleName,
                'operation_type' => $options['operation_type'] ?? 'chat',
                'model_id' => $model->id,
                'provider_name' => $model->provider->name,
                'model_name' => $model->name,
                'request_prompt' => $message,
                'request_options' => $options,
                'request_ip' => request()->ip(),
                'request_user_agent' => request()->userAgent(),
                'status' => 'pending',
            ]);
        }

        try {
            $response = $this->makeChatRequest($model, $message, $context, $options);
            $processingTime = (microtime(true) - $startTime) * 1000;

            $cost = $this->calculateCost($model, $response['usage'] ?? []);

            // Update request log with success response if logging is enabled
            if ($requestLog) {
                $requestLog->update([
                    'response_content' => $response['content'],
                    'response_metadata' => [
                        'model' => $model->name,
                        'provider' => $model->provider->name,
                    ],
                    'prompt_tokens' => $response['usage']['prompt_tokens'] ?? 0,
                    'completion_tokens' => $response['usage']['completion_tokens'] ?? 0,
                    'total_tokens' => $response['usage']['total_tokens'] ?? 0,
                    'cost' => $cost,
                    'processing_time_ms' => $processingTime,
                    'status' => 'success',
                ]);
            }

            // Log usage independently to ensure it's always saved
            $this->logUsageIndependently([
                'user_id' => auth()->id(),
                'company_id' => $options['company_id'] ?? null,
                'module_name' => $options['module_name'] ?? 'AICore',
                'operation_type' => 'chat',
                'model_id' => $model->id,
                'prompt_tokens' => $response['usage']['prompt_tokens'] ?? 0,
                'completion_tokens' => $response['usage']['completion_tokens'] ?? 0,
                'total_tokens' => $response['usage']['total_tokens'] ?? 0,
                'cost' => $cost,
                'processing_time_ms' => $processingTime,
                'status' => 'success',
            ]);

            $result = [
                'success' => true,
                'response' => $response['content'],
                'model' => $model->name,
                'model_id' => $model->id,
                'usage' => array_merge($response['usage'] ?? [], [
                    'cost' => $this->calculateCost($model, $response['usage'] ?? []),
                    'processing_time_ms' => $processingTime,
                ]),
                'processing_time' => round($processingTime, 2),
            ];

            // Cache the response if caching is enabled
            if ($cacheKey && $this->settingsService->get('AICore', 'cache_enabled', false)) {
                $cacheTtl = $this->settingsService->get('AICore', 'cache_ttl', 3600); // Default 1 hour
                Cache::put($cacheKey, $result, $cacheTtl);
            }

            return $result;

        } catch (\Exception $e) {
            $processingTime = (microtime(true) - $startTime) * 1000;

            // Update request log with error if logging is enabled
            if ($requestLog) {
                $requestLog->update([
                    'processing_time_ms' => $processingTime,
                    'status' => 'error',
                    'error_message' => $e->getMessage(),
                    'error_code' => $e->getCode() ? (string) $e->getCode() : null,
                ]);
            }

            // Log the error for debugging
            Log::error('AI Request Failed - Logging Usage', [
                'module' => $options['module_name'] ?? 'AICore',
                'model_id' => $model->id,
                'error' => $e->getMessage(),
                'user_id' => auth()->id(),
            ]);

            // Always log usage even for errors - with 0 tokens if we don't have usage data
            // We need to save this independently of any parent transaction
            $usageData = [
                'user_id' => auth()->id(),
                'company_id' => $options['company_id'] ?? null,
                'module_name' => $options['module_name'] ?? 'AICore',
                'operation_type' => 'chat',
                'model_id' => $model->id,
                'prompt_tokens' => 0,  // No tokens for errors
                'completion_tokens' => 0,
                'total_tokens' => 0,
                'cost' => 0,
                'processing_time_ms' => $processingTime,
                'status' => 'error',
                'error_message' => substr($e->getMessage(), 0, 500), // Limit error message length
            ];

            // Save usage log in a separate database connection to avoid transaction rollback
            try {
                // Use a fresh database connection to bypass any active transactions
                $this->logUsageIndependently($usageData);
                Log::info('Error usage logged successfully for module: '.($options['module_name'] ?? 'AICore'));
            } catch (\Exception $logException) {
                Log::error('Failed to log error usage', [
                    'usage_data' => $usageData,
                    'log_error' => $logException->getMessage(),
                    'module' => $options['module_name'] ?? 'AICore',
                ]);
            }

            throw $e;
        }
    }

    /**
     * Text completion request
     */
    public function complete(string $prompt, array $options = []): array
    {
        return $this->chat($prompt, [], $options);
    }

    /**
     * Send a vision/multimodal chat request with images
     *
     * @param  string  $message  The prompt/message
     * @param  array  $images  Array of image paths or base64 encoded images
     * @param  array  $context  Previous conversation messages
     * @param  array  $options  Additional options including module_name, max_tokens, temperature
     */
    public function chatWithVision(string $message, array $images, array $context = [], array $options = []): array
    {
        // Check if AI is enabled globally
        if (! $this->settingsService->get('AICore', 'ai_enabled', true)) {
            throw new \Exception('AI functionality is currently disabled. Please contact your administrator.');
        }

        if (empty($images)) {
            throw new \Exception('At least one image is required for vision requests');
        }

        // First check if there's a module-specific configuration
        $moduleName = $options['module_name'] ?? 'DocumentAI';
        $model = null;

        // Try to get module-specific model configuration
        $moduleConfig = AIModuleConfiguration::where('module_name', $moduleName)
            ->where('is_active', true)
            ->first();

        if ($moduleConfig && $moduleConfig->default_model_id) {
            $model = AIModel::with('provider')
                ->where('id', $moduleConfig->default_model_id)
                ->where('is_active', true)
                ->first();
        }

        // If no module-specific model, try to find a vision-capable model
        if (! $model) {
            $providerType = $options['provider_type'] ?? null;

            // For local provider, use vision-capable model
            if ($providerType === 'local' || ! $providerType) {
                $model = $this->providerService->getBestModelForTask('vision', $options['max_tokens'] ?? null, $providerType ?? 'local');
            }

            if (! $model) {
                // Fall back to any available model with vision capability
                $model = $this->providerService->getBestModelForTask('text', $options['max_tokens'] ?? null, $providerType);
            }
        }

        if (! $model) {
            throw new \Exception('No available AI model for vision requests');
        }

        $startTime = microtime(true);

        // Create request log entry if logging is enabled
        $requestLog = null;
        if ($this->settingsService->get('AICore', 'log_requests', true)) {
            $requestLog = AIRequestLog::create([
                'user_id' => auth()->id(),
                'module_name' => $moduleName,
                'operation_type' => 'vision',
                'model_id' => $model->id,
                'provider_name' => $model->provider->name,
                'model_name' => $model->name,
                'request_prompt' => $message,
                'request_options' => array_merge($options, ['image_count' => count($images)]),
                'request_ip' => request()->ip(),
                'request_user_agent' => request()->userAgent(),
                'status' => 'pending',
            ]);
        }

        try {
            $response = $this->makeVisionRequest($model, $message, $images, $context, $options);
            $processingTime = (microtime(true) - $startTime) * 1000;

            $cost = $this->calculateCost($model, $response['usage'] ?? []);

            // Update request log with success response
            if ($requestLog) {
                $requestLog->update([
                    'response_content' => $response['content'],
                    'response_metadata' => [
                        'model' => $model->name,
                        'provider' => $model->provider->name,
                        'image_count' => count($images),
                    ],
                    'prompt_tokens' => $response['usage']['prompt_tokens'] ?? 0,
                    'completion_tokens' => $response['usage']['completion_tokens'] ?? 0,
                    'total_tokens' => $response['usage']['total_tokens'] ?? 0,
                    'cost' => $cost,
                    'processing_time_ms' => $processingTime,
                    'status' => 'success',
                ]);
            }

            // Log usage
            $this->logUsageIndependently([
                'user_id' => auth()->id(),
                'company_id' => $options['company_id'] ?? null,
                'module_name' => $moduleName,
                'operation_type' => 'vision',
                'model_id' => $model->id,
                'prompt_tokens' => $response['usage']['prompt_tokens'] ?? 0,
                'completion_tokens' => $response['usage']['completion_tokens'] ?? 0,
                'total_tokens' => $response['usage']['total_tokens'] ?? 0,
                'cost' => $cost,
                'processing_time_ms' => $processingTime,
                'status' => 'success',
            ]);

            return [
                'success' => true,
                'response' => $response['content'],
                'model' => $model->name,
                'model_id' => $model->id,
                'provider' => $model->provider->name,
                'usage' => array_merge($response['usage'] ?? [], [
                    'cost' => $cost,
                    'processing_time_ms' => $processingTime,
                ]),
                'processing_time' => round($processingTime, 2),
            ];

        } catch (\Exception $e) {
            $processingTime = (microtime(true) - $startTime) * 1000;

            if ($requestLog) {
                $requestLog->update([
                    'processing_time_ms' => $processingTime,
                    'status' => 'error',
                    'error_message' => $e->getMessage(),
                    'error_code' => $e->getCode() ? (string) $e->getCode() : null,
                ]);
            }

            Log::error('AI Vision Request Failed', [
                'module' => $moduleName,
                'model_id' => $model->id,
                'error' => $e->getMessage(),
                'user_id' => auth()->id(),
            ]);

            $this->logUsageIndependently([
                'user_id' => auth()->id(),
                'company_id' => $options['company_id'] ?? null,
                'module_name' => $moduleName,
                'operation_type' => 'vision',
                'model_id' => $model->id,
                'prompt_tokens' => 0,
                'completion_tokens' => 0,
                'total_tokens' => 0,
                'cost' => 0,
                'processing_time_ms' => $processingTime,
                'status' => 'error',
                'error_message' => substr($e->getMessage(), 0, 500),
            ]);

            throw $e;
        }
    }

    /**
     * Make vision request based on provider type
     */
    protected function makeVisionRequest(AIModel $model, string $message, array $images, array $context, array $options): array
    {
        $messages = [];

        foreach ($context as $ctx) {
            $messages[] = [
                'role' => $ctx['role'] ?? 'user',
                'content' => $ctx['content'],
            ];
        }

        $messages[] = ['role' => 'user', 'content' => $message];

        switch ($model->provider->type) {
            case 'openai':
                return $this->makeOpenAIVisionRequest($model, $messages, $images, $options);
            case 'claude':
                return $this->makeClaudeVisionRequest($model, $messages, $images, $options);
            case 'gemini':
                return $this->makeGeminiVisionRequest($model, $messages, $images, $options);
            case 'local':
                return $this->makeLocalAIVisionRequest($model, $messages, $images, $options);
            default:
                throw new \Exception("Vision is not supported for provider type: {$model->provider->type}");
        }
    }

    /**
     * OpenAI vision request
     */
    protected function makeOpenAIVisionRequest(AIModel $model, array $messages, array $images, array $options): array
    {
        // Format the last message with images
        $formattedMessages = [];
        foreach ($messages as $index => $msg) {
            if ($index === count($messages) - 1 && $msg['role'] === 'user') {
                $content = [
                    ['type' => 'text', 'text' => $msg['content']],
                ];

                foreach ($images as $image) {
                    if (file_exists($image)) {
                        $mimeType = mime_content_type($image) ?: 'image/png';
                        $base64 = base64_encode(file_get_contents($image));
                        $content[] = [
                            'type' => 'image_url',
                            'image_url' => ['url' => "data:{$mimeType};base64,{$base64}"],
                        ];
                    } else {
                        $url = str_starts_with($image, 'http') || str_starts_with($image, 'data:')
                            ? $image
                            : "data:image/png;base64,{$image}";
                        $content[] = [
                            'type' => 'image_url',
                            'image_url' => ['url' => $url],
                        ];
                    }
                }

                $formattedMessages[] = ['role' => 'user', 'content' => $content];
            } else {
                $formattedMessages[] = $msg;
            }
        }

        // Use provider's custom endpoint if set, otherwise default to OpenAI API
        $baseUrl = $model->provider->endpoint_url
            ? rtrim($model->provider->endpoint_url, '/')
            : 'https://api.openai.com/v1';

        $response = $this->httpClient->post($baseUrl.'/chat/completions', [
            'headers' => [
                'Authorization' => 'Bearer '.$model->provider->decrypted_api_key,
                'Content-Type' => 'application/json',
            ],
            'json' => [
                'model' => $model->model_identifier,
                'messages' => $formattedMessages,
                'max_tokens' => $options['max_tokens'] ?? 4096,
            ],
            'timeout' => $options['timeout'] ?? 180,
        ]);

        $data = json_decode($response->getBody()->getContents(), true);

        return [
            'content' => $data['choices'][0]['message']['content'] ?? '',
            'usage' => $data['usage'] ?? [],
        ];
    }

    /**
     * Claude vision request
     */
    protected function makeClaudeVisionRequest(AIModel $model, array $messages, array $images, array $options): array
    {
        // Format messages with images for Claude
        $formattedMessages = [];
        foreach ($messages as $index => $msg) {
            if ($index === count($messages) - 1 && $msg['role'] === 'user') {
                $content = [];

                foreach ($images as $image) {
                    if (file_exists($image)) {
                        $mimeType = mime_content_type($image) ?: 'image/png';
                        $base64 = base64_encode(file_get_contents($image));
                    } else {
                        $base64 = $image;
                        $mimeType = 'image/png';
                    }

                    $content[] = [
                        'type' => 'image',
                        'source' => [
                            'type' => 'base64',
                            'media_type' => $mimeType,
                            'data' => $base64,
                        ],
                    ];
                }

                $content[] = ['type' => 'text', 'text' => $msg['content']];

                $formattedMessages[] = ['role' => 'user', 'content' => $content];
            } else {
                $formattedMessages[] = $msg;
            }
        }

        $response = $this->httpClient->post('https://api.anthropic.com/v1/messages', [
            'headers' => [
                'x-api-key' => $model->provider->decrypted_api_key,
                'Content-Type' => 'application/json',
                'anthropic-version' => '2023-06-01',
            ],
            'json' => [
                'model' => $model->model_identifier,
                'messages' => $formattedMessages,
                'max_tokens' => $options['max_tokens'] ?? 4096,
            ],
            'timeout' => $options['timeout'] ?? 180,
        ]);

        $data = json_decode($response->getBody()->getContents(), true);

        return [
            'content' => $data['content'][0]['text'] ?? '',
            'usage' => [
                'prompt_tokens' => $data['usage']['input_tokens'] ?? 0,
                'completion_tokens' => $data['usage']['output_tokens'] ?? 0,
                'total_tokens' => ($data['usage']['input_tokens'] ?? 0) + ($data['usage']['output_tokens'] ?? 0),
            ],
        ];
    }

    /**
     * Gemini vision request
     */
    protected function makeGeminiVisionRequest(AIModel $model, array $messages, array $images, array $options): array
    {
        $parts = [];

        // Add images first
        foreach ($images as $image) {
            if (file_exists($image)) {
                $mimeType = mime_content_type($image) ?: 'image/png';
                $base64 = base64_encode(file_get_contents($image));
            } else {
                $base64 = $image;
                $mimeType = 'image/png';
            }

            $parts[] = [
                'inline_data' => [
                    'mime_type' => $mimeType,
                    'data' => $base64,
                ],
            ];
        }

        // Add text from the last user message
        $lastMessage = end($messages);
        $parts[] = ['text' => $lastMessage['content']];

        $response = $this->httpClient->post(
            "https://generativelanguage.googleapis.com/v1beta/models/{$model->model_identifier}:generateContent",
            [
                'headers' => ['Content-Type' => 'application/json'],
                'query' => ['key' => $model->provider->decrypted_api_key],
                'json' => [
                    'contents' => [['parts' => $parts]],
                    'generationConfig' => [
                        'maxOutputTokens' => $options['max_tokens'] ?? 4096,
                        'temperature' => $options['temperature'] ?? 0.7,
                    ],
                ],
                'timeout' => $options['timeout'] ?? 180,
            ]
        );

        $data = json_decode($response->getBody()->getContents(), true);

        return [
            'content' => $data['candidates'][0]['content']['parts'][0]['text'] ?? '',
            'usage' => [
                'prompt_tokens' => $data['usageMetadata']['promptTokenCount'] ?? 0,
                'completion_tokens' => $data['usageMetadata']['candidatesTokenCount'] ?? 0,
                'total_tokens' => ($data['usageMetadata']['promptTokenCount'] ?? 0) + ($data['usageMetadata']['candidatesTokenCount'] ?? 0),
            ],
        ];
    }

    /**
     * Local AI vision request using LocalAIProvider module
     */
    protected function makeLocalAIVisionRequest(AIModel $model, array $messages, array $images, array $options): array
    {
        if (! class_exists('\Modules\LocalAIProvider\App\Services\LocalAIProviderService')) {
            throw new \Exception('LocalAIProvider module is not installed or enabled');
        }

        $localService = new \Modules\LocalAIProvider\App\Services\LocalAIProviderService;
        $provider = $model->provider;

        // Read from LocalAIProvider module settings first, fallback to provider config
        $backendType = $this->settingsService->get('LocalAIProvider', 'provider_type',
            $provider->configuration['provider_type'] ?? 'ollama');
        $baseUrl = $this->settingsService->get('LocalAIProvider', 'base_url',
            $provider->endpoint_url ?? 'http://localhost:11434');

        $localService->setProvider($backendType, true);
        $localService->setBaseUrl($baseUrl);

        if ($model->model_identifier) {
            $localService->setModel($model->model_identifier);
        }

        return $localService->chatWithVision($messages, $images, [
            'max_tokens' => $options['max_tokens'] ?? 4096,
            'temperature' => $options['temperature'] ?? 0.7,
            'timeout' => $options['timeout'] ?? 180,
        ]);
    }

    /**
     * Summarize text
     */
    public function summarize(string $text, array $options = []): array
    {
        $maxLength = $options['max_length'] ?? 200;
        $style = $options['style'] ?? 'concise';

        $prompt = "Please summarize the following text in a {$style} style, limiting the summary to approximately {$maxLength} words:\n\n{$text}\n\nSummary:";

        return $this->complete($prompt, array_merge($options, [
            'module_name' => $options['module_name'] ?? 'DocumentSummarizerAI',
        ]));
    }

    /**
     * Extract data from text
     */
    public function extract(string $text, array $fields, array $options = []): array
    {
        $fieldsList = implode(', ', $fields);
        $prompt = "Extract the following information from the text and return it as JSON: {$fieldsList}\n\nText:\n{$text}\n\nExtracted data (JSON):";

        return $this->complete($prompt, array_merge($options, [
            'module_name' => $options['module_name'] ?? 'PDFAnalyzerAI',
        ]));
    }

    /**
     * Make chat request based on provider type
     */
    protected function makeChatRequest(AIModel $model, string $message, array $context, array $options): array
    {
        switch ($model->provider->type) {
            case 'openai':
                return $this->makeOpenAIChatRequest($model, $message, $context, $options);
            case 'claude':
                return $this->makeClaudeChatRequest($model, $message, $context, $options);
            case 'gemini':
                return $this->makeGeminiChatRequest($model, $message, $context, $options);
            case 'local':
                return $this->makeLocalAIChatRequest($model, $message, $context, $options);
            default:
                throw new \Exception("Unsupported provider type: {$model->provider->type}");
        }
    }

    /**
     * OpenAI chat request
     */
    protected function makeOpenAIChatRequest(AIModel $model, string $message, array $context, array $options): array
    {
        $messages = [];

        foreach ($context as $ctx) {
            $messages[] = [
                'role' => $ctx['role'] ?? 'user',
                'content' => $ctx['content'],
            ];
        }

        $messages[] = ['role' => 'user', 'content' => $message];

        // Use provider's custom endpoint if set, otherwise default to OpenAI API
        $baseUrl = $model->provider->endpoint_url
            ? rtrim($model->provider->endpoint_url, '/')
            : 'https://api.openai.com/v1';

        $response = $this->httpClient->post($baseUrl.'/chat/completions', [
            'headers' => [
                'Authorization' => 'Bearer '.$model->provider->decrypted_api_key,
                'Content-Type' => 'application/json',
            ],
            'json' => [
                'model' => $model->model_identifier,
                'messages' => $messages,
                'max_tokens' => $options['max_tokens'] ?? min($model->max_tokens, $this->settingsService->get('AICore', 'default_max_tokens', 1000)),
                'temperature' => $options['temperature'] ?? $this->settingsService->get('AICore', 'default_temperature', 0.7),
            ],
        ]);

        $data = json_decode($response->getBody()->getContents(), true);

        return [
            'content' => $data['choices'][0]['message']['content'] ?? '',
            'usage' => $data['usage'] ?? [],
        ];
    }

    /**
     * Claude chat request
     */
    protected function makeClaudeChatRequest(AIModel $model, string $message, array $context, array $options): array
    {
        $messages = [];

        foreach ($context as $ctx) {
            $messages[] = [
                'role' => $ctx['role'] ?? 'user',
                'content' => $ctx['content'],
            ];
        }

        $messages[] = ['role' => 'user', 'content' => $message];

        $response = $this->httpClient->post('https://api.anthropic.com/v1/messages', [
            'headers' => [
                'x-api-key' => $model->provider->decrypted_api_key,
                'Content-Type' => 'application/json',
                'anthropic-version' => '2023-06-01',
            ],
            'json' => [
                'model' => $model->model_identifier,
                'messages' => $messages,
                'max_tokens' => $options['max_tokens'] ?? min($model->max_tokens, $this->settingsService->get('AICore', 'default_max_tokens', 1000)),
            ],
        ]);

        $data = json_decode($response->getBody()->getContents(), true);

        return [
            'content' => $data['content'][0]['text'] ?? '',
            'usage' => [
                'prompt_tokens' => $data['usage']['input_tokens'] ?? 0,
                'completion_tokens' => $data['usage']['output_tokens'] ?? 0,
                'total_tokens' => ($data['usage']['input_tokens'] ?? 0) + ($data['usage']['output_tokens'] ?? 0),
            ],
        ];
    }

    /**
     * Gemini chat request
     */
    protected function makeGeminiChatRequest(AIModel $model, string $message, array $context, array $options): array
    {
        $messages = [];

        foreach ($context as $ctx) {
            $messages[] = [
                'role' => $ctx['role'] ?? 'user',
                'content' => $ctx['content'],
            ];
        }

        $messages[] = ['role' => 'user', 'content' => $message];

        // Log the request details for debugging
        Log::info('Making Gemini chat request', [
            'model' => $model->model_identifier,
            'provider' => $model->provider->name,
            'has_api_key' => ! empty($model->provider->decrypted_api_key),
            'message_count' => count($messages),
        ]);

        // Use Gemini service if available
        if (class_exists('\Modules\GeminiAIProvider\Services\GeminiProviderService')) {
            try {
                $geminiService = new \Modules\GeminiAIProvider\Services\GeminiProviderService;
                $apiKey = $model->provider->decrypted_api_key;

                if (empty($apiKey)) {
                    throw new \Exception('No API key configured for Gemini provider');
                }

                $geminiService->setApiKey($apiKey);

                $response = $geminiService->chat(
                    $model->model_identifier,
                    $messages,
                    [
                        'max_tokens' => $options['max_tokens'] ?? min($model->max_tokens, $this->settingsService->get('AICore', 'default_max_tokens', 1000)),
                        'temperature' => $options['temperature'] ?? $this->settingsService->get('AICore', 'default_temperature', 0.7),
                    ]
                );

                return $response;
            } catch (\Exception $e) {
                Log::error('Gemini service error', [
                    'error' => $e->getMessage(),
                    'model' => $model->model_identifier,
                ]);
                // Fall through to direct API call
            }
        }

        // Fallback to direct API call
        $contents = [];
        foreach ($messages as $msg) {
            $role = $msg['role'] === 'assistant' ? 'model' : 'user';
            $contents[] = [
                'role' => $role,
                'parts' => [['text' => $msg['content']]],
            ];
        }

        $apiKey = $model->provider->decrypted_api_key;
        if (empty($apiKey)) {
            throw new \Exception('No API key configured for Gemini provider');
        }

        try {
            $response = $this->httpClient->post(
                "https://generativelanguage.googleapis.com/v1beta/models/{$model->model_identifier}:generateContent",
                [
                    'headers' => [
                        'Content-Type' => 'application/json',
                    ],
                    'query' => [
                        'key' => $apiKey,
                    ],
                    'json' => [
                        'contents' => $contents,
                        'generationConfig' => [
                            'temperature' => $options['temperature'] ?? $this->settingsService->get('AICore', 'default_temperature', 0.7),
                            'maxOutputTokens' => $options['max_tokens'] ?? min($model->max_tokens, $this->settingsService->get('AICore', 'default_max_tokens', 1000)),
                            'topP' => 0.95,
                            'topK' => 40,
                        ],
                    ],
                ]
            );

            $data = json_decode($response->getBody()->getContents(), true);
        } catch (\GuzzleHttp\Exception\ClientException $e) {
            $responseBody = $e->getResponse()->getBody()->getContents();
            Log::error('Gemini API error', [
                'status' => $e->getResponse()->getStatusCode(),
                'response' => $responseBody,
                'model' => $model->model_identifier,
            ]);

            $errorData = json_decode($responseBody, true);
            $errorMessage = $errorData['error']['message'] ?? 'Unknown Gemini API error';

            if ($e->getResponse()->getStatusCode() === 401) {
                throw new \Exception('Unauthorized: Please check your Gemini API key configuration');
            }

            throw new \Exception('Gemini API error: '.$errorMessage);
        } catch (\GuzzleHttp\Exception\ServerException $e) {
            // Handle 5xx server errors (like 503)
            $responseBody = $e->getResponse()->getBody()->getContents();
            Log::error('Gemini API server error', [
                'status' => $e->getResponse()->getStatusCode(),
                'response' => $responseBody,
                'model' => $model->model_identifier,
            ]);

            $errorData = json_decode($responseBody, true);
            $errorMessage = $errorData['error']['message'] ?? 'Gemini service unavailable';

            throw new \Exception('Gemini API error: '.$errorMessage);
        }

        $content = '';
        if (isset($data['candidates'][0]['content']['parts'][0]['text'])) {
            $content = $data['candidates'][0]['content']['parts'][0]['text'];
        }

        return [
            'content' => $content,
            'usage' => [
                'prompt_tokens' => $data['usageMetadata']['promptTokenCount'] ?? 0,
                'completion_tokens' => $data['usageMetadata']['candidatesTokenCount'] ?? 0,
                'total_tokens' => ($data['usageMetadata']['promptTokenCount'] ?? 0) + ($data['usageMetadata']['candidatesTokenCount'] ?? 0),
            ],
        ];
    }

    /**
     * Local AI chat request using LocalAIProvider module
     */
    protected function makeLocalAIChatRequest(AIModel $model, string $message, array $context, array $options): array
    {
        // Check if LocalAIProvider module is available
        if (! class_exists('\Modules\LocalAIProvider\App\Services\LocalAIProviderService')) {
            throw new \Exception('LocalAIProvider module is not installed or enabled');
        }

        $messages = [];

        foreach ($context as $ctx) {
            $messages[] = [
                'role' => $ctx['role'] ?? 'user',
                'content' => $ctx['content'],
            ];
        }

        $messages[] = ['role' => 'user', 'content' => $message];

        try {
            $localService = new \Modules\LocalAIProvider\App\Services\LocalAIProviderService;
            $provider = $model->provider;

            // Read from LocalAIProvider module settings first, fallback to provider config
            $backendType = $this->settingsService->get('LocalAIProvider', 'provider_type',
                $provider->configuration['provider_type'] ?? 'ollama');
            $baseUrl = $this->settingsService->get('LocalAIProvider', 'base_url',
                $provider->endpoint_url ?? 'http://localhost:11434');

            // Set provider first (preserve URL), then override URL with settings
            $localService->setProvider($backendType, true);
            $localService->setBaseUrl($baseUrl);

            // Set the model from the AIModel configuration
            if ($model->model_identifier) {
                $localService->setModel($model->model_identifier);
            }

            // Get generation options from LocalAIProvider module settings
            $response = $localService->chat($messages, [
                'max_tokens' => $options['max_tokens'] ?? (int) $this->settingsService->get('LocalAIProvider', 'max_tokens',
                    min($model->max_tokens, $this->settingsService->get('AICore', 'default_max_tokens', 2048))),
                'temperature' => $options['temperature'] ?? (float) $this->settingsService->get('LocalAIProvider', 'temperature',
                    $this->settingsService->get('AICore', 'default_temperature', 0.7)),
                'top_p' => (float) $this->settingsService->get('LocalAIProvider', 'top_p', 0.9),
            ]);

            return [
                'content' => $response['content'] ?? '',
                'usage' => [
                    'prompt_tokens' => $response['usage']['prompt_tokens'] ?? 0,
                    'completion_tokens' => $response['usage']['completion_tokens'] ?? 0,
                    'total_tokens' => $response['usage']['total_tokens'] ?? 0,
                ],
            ];
        } catch (\Exception $e) {
            Log::error('Local AI request failed', [
                'error' => $e->getMessage(),
                'model' => $model->model_identifier,
            ]);
            throw new \Exception('Local AI error: '.$e->getMessage());
        }
    }

    /**
     * Log usage independently of any active transaction
     * This ensures usage is always logged even if parent transaction rolls back
     */
    protected function logUsageIndependently(array $data): void
    {
        // Skip straight to PDO connection to bypass any Laravel database transaction handling
        try {
            // Create a completely separate PDO connection that's independent of Laravel's DB facade
            $config = config('database.connections.'.config('database.default'));
            $pdo = new \PDO(
                "mysql:host={$config['host']};dbname={$config['database']};port={$config['port']}",
                $config['username'],
                $config['password'],
                [\PDO::ATTR_AUTOCOMMIT => true] // Ensure autocommit is on
            );

            $stmt = $pdo->prepare('INSERT INTO ai_usage_logs (
                user_id, company_id, module_name, operation_type, model_id, 
                prompt_tokens, completion_tokens, total_tokens, cost, 
                processing_time_ms, status, error_message, request_hash, 
                created_at, updated_at
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');

            $timestamp = date('Y-m-d H:i:s');
            $success = $stmt->execute([
                $data['user_id'] ?? null,
                $data['company_id'] ?? null,
                $data['module_name'],
                $data['operation_type'],
                $data['model_id'],
                $data['prompt_tokens'] ?? 0,
                $data['completion_tokens'] ?? 0,
                $data['total_tokens'] ?? 0,
                $data['cost'] ?? 0,
                $data['processing_time_ms'] ?? null,
                $data['status'] ?? 'success',
                $data['error_message'] ?? null,
                $data['request_hash'] ?? null,
                $timestamp,
                $timestamp,
            ]);

            if (! $success) {
                $errorInfo = $stmt->errorInfo();
                throw new \Exception('PDO insert failed: '.json_encode($errorInfo));
            }

            Log::info('Usage logged using separate PDO connection', [
                'module' => $data['module_name'],
                'status' => $data['status'],
                'error' => $data['error_message'] ?? null,
                'inserted_id' => $pdo->lastInsertId(),
            ]);
        } catch (\Exception $pdoError) {
            Log::error('Failed to log usage with PDO', [
                'error' => $pdoError->getMessage(),
                'data' => $data,
            ]);

            // Try one more time with a completely fresh connection and explicit commit
            try {
                $config = config('database.connections.'.config('database.default'));
                $dsn = "mysql:host={$config['host']};dbname={$config['database']};port={$config['port']};charset=utf8mb4";
                $pdo = new \PDO($dsn, $config['username'], $config['password']);
                $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
                $pdo->setAttribute(\PDO::ATTR_AUTOCOMMIT, true);

                // Start our own transaction that we control
                $pdo->beginTransaction();

                $sql = 'INSERT INTO ai_usage_logs (
                    user_id, company_id, module_name, operation_type, model_id, 
                    prompt_tokens, completion_tokens, total_tokens, cost, 
                    processing_time_ms, status, error_message, request_hash, 
                    created_at, updated_at
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';

                $stmt = $pdo->prepare($sql);
                $timestamp = date('Y-m-d H:i:s');

                $stmt->execute([
                    $data['user_id'] ?? null,
                    $data['company_id'] ?? null,
                    $data['module_name'],
                    $data['operation_type'],
                    $data['model_id'],
                    $data['prompt_tokens'] ?? 0,
                    $data['completion_tokens'] ?? 0,
                    $data['total_tokens'] ?? 0,
                    $data['cost'] ?? 0,
                    $data['processing_time_ms'] ?? null,
                    $data['status'] ?? 'success',
                    $data['error_message'] ?? null,
                    $data['request_hash'] ?? null,
                    $timestamp,
                    $timestamp,
                ]);

                // Explicitly commit our transaction
                $pdo->commit();

                Log::info('Usage logged with explicit PDO transaction', [
                    'module' => $data['module_name'],
                    'status' => $data['status'],
                    'inserted_id' => $pdo->lastInsertId(),
                ]);

                // Close the connection
                $pdo = null;
            } catch (\Exception $finalError) {
                Log::error('Final attempt to log usage failed', [
                    'error' => $finalError->getMessage(),
                    'trace' => $finalError->getTraceAsString(),
                    'data' => $data,
                ]);
            }
        }
    }

    /**
     * Calculate cost based on usage
     */
    protected function calculateCost(AIModel $model, array $usage): float
    {
        $inputTokens = $usage['prompt_tokens'] ?? 0;
        $outputTokens = $usage['completion_tokens'] ?? 0;

        return $model->calculateCost($inputTokens, $outputTokens);
    }

    /**
     * Get the configured model for a specific module
     */
    public function getModuleModel(string $moduleName): ?AIModel
    {
        $moduleConfig = AIModuleConfiguration::where('module_name', $moduleName)
            ->where('is_active', true)
            ->first();

        if (! $moduleConfig) {
            return null;
        }

        if ($moduleConfig->default_model_id) {
            return AIModel::with('provider')
                ->where('id', $moduleConfig->default_model_id)
                ->where('is_active', true)
                ->first();
        }

        if ($moduleConfig->default_provider_id) {
            $provider = AIProvider::find($moduleConfig->default_provider_id);
            if ($provider) {
                return $this->providerService->getBestModelForTask('text', null, $provider->type);
            }
        }

        return null;
    }

    /**
     * Get module configuration with defaults
     */
    public function getModuleConfiguration(string $moduleName): array
    {
        $moduleConfig = AIModuleConfiguration::where('module_name', $moduleName)
            ->where('is_active', true)
            ->first();

        if (! $moduleConfig) {
            return [
                'max_tokens' => 2048,
                'temperature' => 0.7,
                'streaming' => false,
                'model' => null,
                'provider' => null,
            ];
        }

        return [
            'max_tokens' => $moduleConfig->max_tokens_limit ?? 2048,
            'temperature' => $moduleConfig->temperature_default ?? 0.7,
            'streaming' => $moduleConfig->streaming_enabled ?? false,
            'model' => $moduleConfig->defaultModel,
            'provider' => $moduleConfig->defaultProvider,
        ];
    }
}
