<?php

namespace App\Controllers;

use CodeIgniter\RESTful\ResourceController;
use App\Models\PolicyModel;

class PolicyRagController extends ResourceController
{
    protected PolicyModel $PolicyModel;

    protected array $tier1VehicleTypeMap = [
        'two wheeler'            => 'Two Wheeler',
        '2 wheeler'              => 'Two Wheeler',
        '2w'                     => 'Two Wheeler',
        'motorcycle'             => 'Two Wheeler',
        'bike'                   => 'Two Wheeler',
        'scooter'                => 'Two Wheeler',
        'moped'                  => 'Two Wheeler',
        'four wheeler'           => 'Four Wheeler',
        '4 wheeler'              => 'Four Wheeler',
        '4w'                     => 'Four Wheeler',
        'private car'            => 'Four Wheeler',
        'car'                    => 'Four Wheeler',
        'jeep'                   => 'Four Wheeler',
        'goods vehicle'          => 'Goods Vehicle',
        'good vehicle'           => 'Goods Vehicle',
        'goods carrier'          => 'Goods Vehicle',
        'goods carrying vehicle' => 'Goods Vehicle',
        'cargo vehicle'          => 'Goods Vehicle',
        'lorry'                  => 'Goods Vehicle',
        'truck'                  => 'Goods Vehicle',
        'transport vehicle goods'=> 'Goods Vehicle',
        'commercial vehicle'     => 'Commercial Vehicle',
        'commercial car'         => 'Commercial Vehicle',
        'taxi'                   => 'Commercial Vehicle',
        'cab'                    => 'Commercial Vehicle',
        'hire vehicle'           => 'Commercial Vehicle',
        'passenger commercial'   => 'Commercial Vehicle',
        'bus'                    => 'Bus',
        'passenger bus'          => 'Bus',
        'school bus'             => 'Bus',
        'private bus'            => 'Bus',
        'stage carriage'         => 'Bus',
        'tractor'                => 'Tractor',
        'agricultural tractor'   => 'Tractor',
        'farm tractor'           => 'Tractor',
    ];

    protected array $fuelType = [
        'Petrol' => 'Petrol',
        'Diesel' => 'Diesel',
        'CNG'    => 'CNG',
        'LPG'    => 'LPG',
    ];

    public function __construct()
    {
        $this->PolicyModel = new PolicyModel();
    }

    /**
     * Job handler: read policy PDF via RAG API, update DB, then calculate commission.
     */
    public function readFileAndCalculateCommission(array $data): array
    {
        $policyId = $data['policy_id'];

        $this->logRagStep('JOB_START', 'started', 'readFileAndCalculateCommission', ['policy_id' => $policyId]);

        $readDoc = $this->readPolicyDocViaRag($policyId);
        $calculateCommission = ['status' => 'failed'];

        if ($readDoc['status'] === 'success') {
            $extracted = $readDoc['data'];
            $policyData = $this->mapExtractedDataToPolicyFields($extracted);

            $this->logRagStep('DB_UPDATE', 'started', 'Updating policy from RAG data', [
                'policy_id' => $policyId,
                'payload'   => $policyData,
            ]);

            $updateStatus = $this->PolicyModel->update($policyId, $policyData);

            if ($updateStatus) {
                $this->logRagStep('DB_UPDATE', 'success', 'Policy updated from RAG data', ['policy_id' => $policyId]);

                $this->logRagStep('COMMISSION', 'started', 'Calculating commission', ['policy_id' => $policyId]);
                $policyController = new PolicyController();
                $calculateCommission = $policyController->calculateCommission($policyId);

                $commissionStatus = $calculateCommission['status'] ?? 'failed';
                $this->logRagStep(
                    'COMMISSION',
                    $commissionStatus === 'success' ? 'success' : 'failed',
                    'Commission calculation finished',
                    ['policy_id' => $policyId, 'result' => $commissionStatus]
                );
            } else {
                $this->logRagStep('DB_UPDATE', 'failed', 'Failed to update policy from RAG data', ['policy_id' => $policyId]);
            }
        } else {
            $this->logRagStep('JOB_READ', 'failed', 'readPolicyDocViaRag failed', [
                'policy_id' => $policyId,
                'response'  => $readDoc,
            ]);
        }

        $this->logRagStep('JOB_END', 'completed', 'readFileAndCalculateCommission finished', [
            'policy_id'              => $policyId,
            'readPolicyDocViaRag'    => $readDoc['status'],
            'calculateCommission'    => $calculateCommission['status'] ?? 'failed',
        ]);

        return [
            'readPolicyDocViaRag' => $readDoc['status'],
            'calculateCommission' => $calculateCommission['status'] ?? 'failed',
        ];
    }

    /**
     * Test endpoint: ?type=read&policy_id=123
     */
    public function policyRagRead()
    {
        $type = $this->request->getGet('type');
        $policyId = $this->request->getGet('policy_id');

        if ($type === 'read') {
            $readDoc = $this->readPolicyDocViaRag($policyId);

            if ($readDoc['status'] === 'success') {
                $policyData = $this->mapExtractedDataToPolicyFields($readDoc['data']);
                $this->PolicyModel->update($policyId, $policyData);
            }

            return $this->respond(['status' => $readDoc['status'], 'data' => $readDoc], 200);
        }

        if ($type === 'commission') {
            $policyController = new PolicyController();
            return $policyController->calculateCommission($policyId, true);
        }

        return $this->respond(['status' => 'failed', 'message' => 'Invalid type. Use type=read or type=commission'], 200);
    }

    /**
     * Upload PDF to RAG, poll until indexed, extract JSON via chat, then delete RAG file.
     */
    public function readPolicyDocViaRag($policyId = null)
    {
        $fileId = null;
        $pdfFilePath = null;

        $this->logRagStep('READ_START', 'started', 'readPolicyDocViaRag', ['policy_id' => $policyId]);

        try {
            $record = $this->PolicyModel->find((int) $policyId);

            if (!$record) {
                $this->logRagStep('POLICY_LOOKUP', 'failed', 'Policy not found', ['policy_id' => $policyId]);
                return $this->formatReadResponse('failed', 'Policy not found');
            }

            $this->logRagStep('POLICY_LOOKUP', 'success', 'Policy found', [
                'policy_id' => $policyId,
                'pdf_file'  => $record['policy_pdf_file_name'] ?? null,
            ]);

            $pdfFileName = $record['policy_pdf_file_name'] ?? '';

            if ($pdfFileName === '' || !policy_exists_by_type($pdfFileName, 'policy_pdf')) {
                $missingPath = $pdfFileName !== ''
                    ? storage()->resolveKey('policy', 'policy_pdf', $pdfFileName)
                    : '';
                $this->logRagStep('PDF_CHECK', 'failed', 'PDF file not found', ['path' => $missingPath]);
                return $this->formatReadResponse('failed', "File not found at {$missingPath}");
            }

            $pdfFilePath = policy_local_pdf_path($pdfFileName);

            $this->logRagStep('PDF_CHECK', 'success', 'PDF file found', ['path' => $pdfFilePath]);

            $this->logRagStep('UPLOAD', 'started', 'Uploading PDF to RAG API', ['path' => $pdfFilePath]);
            $uploadResult = $this->uploadFileToRag($pdfFilePath, (int) $policyId);
            if ($uploadResult['status'] !== 'success') {
                return $this->formatReadResponse('failed', $uploadResult['message']);
            }

            $fileId = $uploadResult['data']['file_id'] ?? null;
            if (empty($fileId)) {
                $this->logRagStep('UPLOAD', 'failed', 'RAG upload did not return file_id', ['policy_id' => $policyId]);
                return $this->formatReadResponse('failed', 'RAG upload did not return file_id');
            }

            $this->logRagStep('SAVE_RAG_FILE_ID', 'started', 'Saving rag_file_id', [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
            ]);

            $updated = $this->PolicyModel->update((int) $policyId, ['rag_file_id' => $fileId]);
            if ($updated === false) {
                $this->logRagStep('SAVE_RAG_FILE_ID', 'failed', 'Failed to save rag_file_id', [
                    'policy_id' => $policyId,
                    'file_id'   => $fileId,
                ]);
            } else {
                $this->logRagStep('SAVE_RAG_FILE_ID', 'success', 'rag_file_id saved', [
                    'policy_id' => $policyId,
                    'file_id'   => $fileId,
                ]);
            }

            $this->logRagStep('INDEX_POLL', 'started', 'Waiting for RAG file to be indexed', [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
            ]);

            $indexResult = $this->waitForFileIndexed($fileId, (int) $policyId);
            if ($indexResult['status'] !== 'success') {
                return $this->formatReadResponse('failed', $indexResult['message']);
            }

            $this->logRagStep('CHAT', 'started', 'Sending extraction prompt to RAG API', [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
            ]);

            $chatResult = $this->chatWithRag($fileId, $this->getPolicyReadPrompt(), (int) $policyId);
            if ($chatResult['status'] !== 'success') {
                return $this->formatReadResponse('failed', $chatResult['message']);
            }

            $answer = $chatResult['data']['answer'] ?? '';
            $finalData = $this->extractJsonFromText($answer, (int) $policyId);

            if ($finalData === null) {
                $this->logRagStep('JSON_EXTRACT', 'failed', 'Could not extract valid JSON from RAG answer', [
                    'policy_id'    => $policyId,
                    'file_id'      => $fileId,
                    'answer_snippet' => substr($answer, 0, 500),
                ]);
                return $this->formatReadResponse('failed', 'Could not extract valid JSON from RAG answer');
            }

            $this->logRagStep('READ_COMPLETE', 'success', 'Policy document read successfully', [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
            ]);

            return $this->formatReadResponse('success', 'Doc read success', $finalData);
        } catch (\Throwable $e) {
            $this->logRagStep('READ_ERROR', 'failed', $e->getMessage(), [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
                'line'      => $e->getLine(),
            ]);
            return $this->formatReadResponse('failed', 'Error: ' . $e->getMessage());
        } finally {
            storage_cleanup_temp($pdfFilePath);

            if (!empty($fileId)) {
                $this->logRagStep('DELETE', 'started', 'Deleting RAG file', [
                    'policy_id' => $policyId,
                    'file_id'   => $fileId,
                ]);

                $deleteResult = $this->deleteRagFile($fileId, (int) $policyId);
                if ($deleteResult['status'] !== 'success') {
                    $this->logRagStep('DELETE', 'failed', $deleteResult['message'] ?? 'RAG file delete failed', [
                        'policy_id' => $policyId,
                        'file_id'   => $fileId,
                    ]);
                }
            }

            $this->logRagStep('READ_END', 'completed', 'readPolicyDocViaRag finished', [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
            ]);
        }
    }

    private function getPolicyReadPrompt(): string
    {
        $sampleJson = json_encode([
            'policy_number'        => '',
            'issued_date'          => '',
            'start_date'           => '',
            'end_date'             => '',
            'broker_name'          => '',
            'insured_name'         => '',
            'reg_no'               => '',
            'rto_state_code'       => '',
            'rto_city_code'        => '',
            'weight'               => '',
            'fuel_type'            => '',
            'date_of_registration' => '',
            'year_of_manufacture'  => '',
            'engine_no'            => '',
            'chassis_no'           => '',
            'make'                 => '',
            'model'                => '',
            'cubic_capacity'       => '',
            'vehicle_type'         => '',
            'tp'                   => 0,
            'od'                   => 0,
            'pa'                   => 0,
            'cgst'                 => 0,
            'sgst'                 => 0,
            'igst'                 => 0,
            'premium_amount'       => 0,
        ], JSON_UNESCAPED_SLASHES);

        return 'Read the motor policy document and return only valid JSON using this exact structure (no explanation): '
            . $sampleJson
            . '. Rules: vehicle_type must be one of Two Wheeler, Four Wheeler, Goods Vehicle, Bus, Tractor, Commercial Vehicle. '
            . 'rto_state_code is first 2 characters of reg_no. rto_city_code is 3rd and 4th characters of reg_no. '
            . 'All dates must be in YYYY-MM-DD format. TP premium must not include PA amount.';
    }

    private function mapExtractedDataToPolicyFields(array $data): array
    {
        $data = $this->normalizeExtractedData($data);

        $vehicleType = $data['vehicle_type'] ?? null;
        $fuelType = $data['fuel_type'] ?? null;

        if ($vehicleType !== null && $vehicleType !== '') {
            $vehicleType = $this->postProcessExtractedData((string) $vehicleType, $this->tier1VehicleTypeMap);
        } else {
            $vehicleType = 'UN_IDEN_DOC';
        }

        if ($fuelType !== null && $fuelType !== '') {
            $fuelType = $this->postProcessExtractedData((string) $fuelType, $this->fuelType);
        } else {
            $fuelType = 'UN_IDEN_DOC';
        }

        return [
            'policy_number'        => $this->nullIfEmpty($data['policy_number'] ?? null),
            'issued_date'          => $this->nullIfEmpty($data['issued_date'] ?? null),
            'start_date'           => $this->nullIfEmpty($data['start_date'] ?? null),
            'end_date'             => $this->nullIfEmpty($data['end_date'] ?? null),
            'broker_name'          => $this->nullIfEmpty($data['broker_name'] ?? null),
            'tp'                   => $this->nullIfEmpty($data['tp'] ?? null),
            'od'                   => $this->nullIfEmpty($data['od'] ?? null),
            'pa'                   => $this->nullIfEmpty($data['pa'] ?? null),
            'cgst'                 => $this->nullIfEmpty($data['cgst'] ?? 0),
            'sgst'                 => $this->nullIfEmpty($data['sgst'] ?? 0),
            'igst'                 => $this->nullIfEmpty($data['igst'] ?? 0),
            'premium_amount'       => $this->nullIfEmpty($data['premium_amount'] ?? null),
            'rto_state_code'       => $this->nullIfEmpty($data['rto_state_code'] ?? null),
            'rto_city_code'        => $this->nullIfEmpty($data['rto_city_code'] ?? null),
            'weight'               => $this->nullIfEmpty($data['weight'] ?? null),
            'fuel_type'            => $fuelType,
            'date_of_registration' => $this->nullIfEmpty($data['date_of_registration'] ?? null),
            'year_of_manufacture'  => $this->nullIfEmpty($data['year_of_manufacture'] ?? null),
            'engine_no'            => $this->nullIfEmpty($data['engine_no'] ?? null),
            'chassis_no'           => $this->nullIfEmpty($data['chassis_no'] ?? null),
            'make'                 => $this->nullIfEmpty($data['make'] ?? null),
            'model'                => $this->nullIfEmpty($data['model'] ?? null),
            'cubic_capacity'       => $this->nullIfEmpty($data['cubic_capacity'] ?? null),
            'product'              => $vehicleType,
            'rc_no'                => $this->nullIfEmpty($data['reg_no'] ?? null),
            'insured_name'         => $this->nullIfEmpty($data['insured_name'] ?? null),
        ];
    }

    private function normalizeExtractedData(array $data): array
    {
        if (!isset($data['policy']) && !isset($data['vehicle']) && !isset($data['premium'])) {
            return $data;
        }

        return [
            'policy_number'        => $data['policy']['policy_number'] ?? null,
            'issued_date'          => $data['policy']['issue_date'] ?? null,
            'start_date'           => $data['policy']['period']['start'] ?? null,
            'end_date'             => $data['policy']['period']['end'] ?? null,
            'broker_name'          => $data['policy']['intermediary_name'] ?? null,
            'insured_name'         => $data['insured']['name'] ?? null,
            'reg_no'               => $data['vehicle']['reg_no'] ?? null,
            'rto_state_code'       => $data['vehicle']['rto_state_code'] ?? null,
            'rto_city_code'        => $data['vehicle']['rto_city_code'] ?? null,
            'weight'               => $data['vehicle']['weight'] ?? null,
            'fuel_type'            => $data['vehicle']['fuel_type'] ?? null,
            'date_of_registration' => $data['vehicle']['date_of_registration'] ?? null,
            'year_of_manufacture'  => $data['vehicle']['year_of_manufacture'] ?? null,
            'engine_no'            => $data['vehicle']['engine_no'] ?? null,
            'chassis_no'           => $data['vehicle']['chassis_no'] ?? null,
            'make'                 => $data['vehicle']['make'] ?? null,
            'model'                => $data['vehicle']['model'] ?? null,
            'cubic_capacity'       => $data['vehicle']['cubic_capacity'] ?? null,
            'vehicle_type'         => $data['vehicle']['vehicle_type'] ?? null,
            'tp'                   => $data['premium']['tp'] ?? null,
            'od'                   => $data['premium']['od'] ?? null,
            'pa'                   => $data['premium']['pa'] ?? null,
            'cgst'                 => $data['premium']['taxes']['cgst'] ?? 0,
            'sgst'                 => $data['premium']['taxes']['sgst'] ?? 0,
            'igst'                 => $data['premium']['taxes']['igst'] ?? 0,
            'premium_amount'       => $data['premium']['total'] ?? null,
        ];
    }

    private function nullIfEmpty($value)
    {
        if ($value === '' || $value === null) {
            return null;
        }

        return $value;
    }

    private function postProcessExtractedData(string $incoming, array $masterArray): string
    {
        $value = strtolower(trim($incoming));
        $value = preg_replace('/\s+/', ' ', $value);

        return $masterArray[$value] ?? 'UN_IDEN_TYPE';
    }

    private function ragApiBaseUrl(): string
    {
        $baseUrl = rtrim((string) getenv('RAG_API_BASE_URL'), '/');
        if ($baseUrl === '') {
            throw new \RuntimeException('RAG_API_BASE_URL is not configured');
        }

        return $baseUrl;
    }

    private function uploadFileToRag(string $pdfFilePath, ?int $policyId = null): array
    {
        $url = $this->ragApiBaseUrl() . '/api/files/upload';
        $cfile = new \CURLFile($pdfFilePath, 'application/pdf', basename($pdfFilePath));

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, [
            'uploaded_file' => $cfile,
            'auto_detect'   => 'true',
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $curlError = curl_error($ch);
        $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($curlError) {
            $this->logRagStep('UPLOAD', 'failed', 'cURL error: ' . $curlError, [
                'policy_id' => $policyId,
                'url'       => $url,
            ]);
            return ['status' => 'failed', 'message' => 'RAG upload cURL error: ' . $curlError];
        }

        if ($httpCode < 200 || $httpCode >= 300) {
            $this->logRagStep('UPLOAD', 'failed', "HTTP {$httpCode}", [
                'policy_id' => $policyId,
                'response'  => substr((string) $response, 0, 500),
            ]);
            return ['status' => 'failed', 'message' => "RAG upload failed with HTTP {$httpCode}"];
        }

        $responseData = json_decode((string) $response, true);
        if (!is_array($responseData) || empty($responseData['success'])) {
            $this->logRagStep('UPLOAD', 'failed', 'Invalid API response', [
                'policy_id' => $policyId,
                'response'  => substr((string) $response, 0, 500),
            ]);
            return ['status' => 'failed', 'message' => 'RAG upload returned invalid response'];
        }

        $this->logRagStep('UPLOAD', 'success', 'PDF uploaded to RAG API', [
            'policy_id' => $policyId,
            'file_id'   => $responseData['file_id'] ?? null,
            'filename'  => $responseData['filename'] ?? null,
            'status'    => $responseData['status'] ?? null,
        ]);

        return ['status' => 'success', 'data' => $responseData];
    }

    private function getFileStatus(string $fileId, ?int $policyId = null): array
    {
        $url = $this->ragApiBaseUrl() . '/api/files/status?file_id=' . urlencode($fileId);

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $curlError = curl_error($ch);
        $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($curlError) {
            $this->logRagStep('INDEX_POLL', 'failed', 'Status check cURL error: ' . $curlError, [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
            ]);
            return ['status' => 'failed', 'message' => 'RAG status cURL error: ' . $curlError];
        }

        if ($httpCode < 200 || $httpCode >= 300) {
            $this->logRagStep('INDEX_POLL', 'failed', "Status check HTTP {$httpCode}", [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
            ]);
            return ['status' => 'failed', 'message' => "RAG status check failed with HTTP {$httpCode}"];
        }

        $responseData = json_decode((string) $response, true);
        if (!is_array($responseData) || empty($responseData['success'])) {
            $this->logRagStep('INDEX_POLL', 'failed', 'Status check invalid response', [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
            ]);
            return ['status' => 'failed', 'message' => 'RAG status returned invalid response'];
        }

        return ['status' => 'success', 'data' => $responseData];
    }

    private function waitForFileIndexed(string $fileId, ?int $policyId = null): array
    {
        $interval = (int) (getenv('RAG_API_STATUS_POLL_INTERVAL') ?: 10);
        $maxAttempts = (int) (getenv('RAG_API_STATUS_MAX_ATTEMPTS') ?: 60);

        for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
            $statusResult = $this->getFileStatus($fileId, $policyId);
            if ($statusResult['status'] !== 'success') {
                return $statusResult;
            }

            $fileStatus = strtolower((string) ($statusResult['data']['status'] ?? ''));
            $this->logRagStep('INDEX_POLL', 'progress', "Poll attempt {$attempt}/{$maxAttempts}", [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
                'status'    => $fileStatus,
            ]);

            if ($fileStatus === 'indexed') {
                $this->logRagStep('INDEX_POLL', 'success', 'File indexed', [
                    'policy_id'   => $policyId,
                    'file_id'     => $fileId,
                    'chunk_count' => $statusResult['data']['chunk_count'] ?? null,
                    'attempts'    => $attempt,
                ]);
                return $statusResult;
            }

            if (in_array($fileStatus, ['failed', 'error'], true)) {
                $this->logRagStep('INDEX_POLL', 'failed', 'Indexing failed', [
                    'policy_id' => $policyId,
                    'file_id'   => $fileId,
                    'status'    => $fileStatus,
                ]);
                return ['status' => 'failed', 'message' => 'RAG file indexing failed with status: ' . $fileStatus];
            }

            if ($attempt < $maxAttempts) {
                sleep($interval);
            }
        }

        $this->logRagStep('INDEX_POLL', 'failed', 'Indexing timed out', [
            'policy_id'    => $policyId,
            'file_id'      => $fileId,
            'max_attempts' => $maxAttempts,
            'interval_sec' => $interval,
        ]);

        return ['status' => 'failed', 'message' => 'RAG file indexing timed out'];
    }

    private function chatWithRag(string $fileId, string $question, ?int $policyId = null): array
    {
        $url = $this->ragApiBaseUrl() . '/api/chat';

        $payload = json_encode([
            'question' => $question,
            'file_id'  => $fileId,
        ]);

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $curlError = curl_error($ch);
        $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($curlError) {
            $this->logRagStep('CHAT', 'failed', 'cURL error: ' . $curlError, [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
            ]);
            return ['status' => 'failed', 'message' => 'RAG chat cURL error: ' . $curlError];
        }

        if ($httpCode < 200 || $httpCode >= 300) {
            $this->logRagStep('CHAT', 'failed', "HTTP {$httpCode}", [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
                'response'  => substr((string) $response, 0, 500),
            ]);
            return ['status' => 'failed', 'message' => "RAG chat failed with HTTP {$httpCode}"];
        }

        $responseData = json_decode((string) $response, true);
        if (!is_array($responseData) || empty($responseData['success'])) {
            $this->logRagStep('CHAT', 'failed', 'Invalid API response', [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
            ]);
            return ['status' => 'failed', 'message' => 'RAG chat returned invalid response'];
        }

        $this->logRagStep('CHAT', 'success', 'RAG chat response received', [
            'policy_id'      => $policyId,
            'file_id'        => $fileId,
            'answer_length'  => strlen((string) ($responseData['answer'] ?? '')),
        ]);

        return ['status' => 'success', 'data' => $responseData];
    }

    private function deleteRagFile(string $fileId, ?int $policyId = null): array
    {
        $url = $this->ragApiBaseUrl() . '/api/files/id/' . urlencode($fileId);

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $curlError = curl_error($ch);
        $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($curlError) {
            $this->logRagStep('DELETE', 'failed', 'cURL error: ' . $curlError, [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
            ]);
            return ['status' => 'failed', 'message' => 'RAG delete cURL error: ' . $curlError];
        }

        if ($httpCode < 200 || $httpCode >= 300) {
            $this->logRagStep('DELETE', 'failed', "HTTP {$httpCode}", [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
                'response'  => substr((string) $response, 0, 500),
            ]);
            return ['status' => 'failed', 'message' => "RAG delete failed with HTTP {$httpCode}"];
        }

        $responseData = json_decode((string) $response, true);
        if (!is_array($responseData) || empty($responseData['success'])) {
            $this->logRagStep('DELETE', 'failed', 'Invalid API response', [
                'policy_id' => $policyId,
                'file_id'   => $fileId,
            ]);
            return ['status' => 'failed', 'message' => 'RAG delete returned invalid response'];
        }

        $this->logRagStep('DELETE', 'success', 'RAG file deleted', [
            'policy_id' => $policyId,
            'file_id'   => $fileId,
        ]);

        return ['status' => 'success', 'data' => $responseData];
    }

    private function extractJsonFromText(string $text, ?int $policyId = null): ?array
    {
        $jsonString = '';

        if (preg_match('/```json\s*(.*?)\s*```/s', $text, $matches)) {
            $jsonString = trim($matches[1]);
        } else {
            $start = strpos($text, '{');
            $end = strrpos($text, '}');
            if ($start !== false && $end !== false && $end > $start) {
                $jsonString = substr($text, $start, $end - $start + 1);
            }
        }

        if ($jsonString === '') {
            return null;
        }

        $decoded = json_decode($jsonString, true);
        if (!is_array($decoded)) {
            $this->logRagStep('JSON_EXTRACT', 'failed', 'JSON decode failed', [
                'policy_id'    => $policyId,
                'json_snippet' => substr($jsonString, 0, 500),
            ]);
            return null;
        }

        $this->logRagStep('JSON_EXTRACT', 'success', 'JSON extracted from RAG answer', [
            'policy_id'  => $policyId,
            'field_keys' => array_keys($decoded),
        ]);

        return $decoded;
    }

    private function logRagStep(string $step, string $status, string $message, array $context = []): void
    {
        $level = in_array($status, ['failed', 'error'], true) ? 'error' : 'info';
        $contextJson = $context ? ' | ' . json_encode($context, JSON_UNESCAPED_SLASHES) : '';

        log_message($level, "[PolicyRag][{$step}][{$status}] {$message}{$contextJson}");
    }

    private function formatReadResponse(string $status, string $message, ?array $data = null): array
    {
        $result = ['status' => $status, 'message' => $message];
        if ($data !== null) {
            $result['data'] = $data;
        }

        return $result;
    }
}
