<?php

namespace App\Core\Flows;

use App\Services\GeminiService;
use App\Services\SiroService;
use App\Models\DriverOcrData;
use App\Models\ChatbotRule;
use App\Models\DriverReminder;
use App\Core\Database;

/**
 * DriverRegistrationFlow
 * Handles step-by-step driver and vehicle registration using Gemini OCR.
 * Integrates with Siro platform for driver registration across Syria, Jordan, and Egypt.
 */
class DriverRegistrationFlow extends BaseFlow
{
    private array $prompts = [];
    private string $country = 'syria';

    private array $stepToDocType = [
        'id_front' => 'id_front',
        'id_back' => 'id_back',
        'driving_license_front' => 'driver_license_front',
        'driving_license_back' => 'driver_license_back',
        'vehicle_license_front' => 'car_license_front',
        'vehicle_license_back' => 'car_license_back',
        'criminal_record' => 'criminal_record',
    ];

    public function __construct()
    {
        $this->prompts = SiroService::getDocumentPrompts($this->country);
    }

    public function handleStep(string $step, array $messageData, array &$context): FlowResult
    {
        $text = isset($messageData['body']) ? trim($messageData['body']) : '';
        $phone = $messageData['phone'];
        $companyId = $context['company_id'] ?? 1;

        // Detect country from phone number
        $this->country = SiroService::detectCountry($phone);
        $context['country'] = $this->country;

        // Set country-specific prompts
        $this->prompts = SiroService::getDocumentPrompts($this->country);

        // Country name in Arabic for messages
        $countryNames = [
            'syria' => 'سوريا',
            'jordan' => 'الأردن',
            'egypt' => 'مصر',
        ];
        $countryName = $countryNames[$this->country] ?? 'سوريا';

        // App name based on country
        $appNames = [
            'syria' => 'سيرو',
            'jordan' => 'سيرو',
            'egypt' => 'سيرو',
        ];
        $appName = $appNames[$this->country] ?? 'سيرو';

        // If currently postponed and user sends a message, resume the flow
        if ($step === 'postponed') {
            $activeReminder = DriverReminder::findActive($companyId, $phone);
            if ($activeReminder) {
                DriverReminder::update($activeReminder['id'], ['status' => 'cancelled']);
            }
            $step = $context['previous_step'] ?? 'ask_name';
        }

        // Check if user requests postponement/delay (only if already started and not finished)
        if ($step !== 'start' && $step !== 'finished' && !empty($text)) {
            $rule = ChatbotRule::findActiveForRule($companyId);
            $configuredGeminiKey = ($rule && !empty($rule['gemini_api_key'])) ? $rule['gemini_api_key'] : null;
            $apiKey = GeminiService::getGeminiApiKey($configuredGeminiKey);
            if (!empty($apiKey)) {
                $postponeData = $this->detectPostponement($text, $apiKey, $companyId);
                if ($postponeData !== null) {
                    $hours = $postponeData['hours'];
                    $postponeCount = ($context['postpone_count'] ?? 0) + 1;
                    
                    if ($postponeCount > 3) {
                        return new FlowResult(
                            "عذراً كابتن، لقد تجاوزت الحد الأقصى لمرات التأجيل (3 مرات). تم إلغاء طلب التسجيل الحالي. يمكنك البدء من جديد عندما تكون جاهزاً بكتابة 'تسجيل'.",
                            "finished",
                            true
                        );
                    }

                    $context['postpone_count'] = $postponeCount;
                    $context['previous_step'] = $step;

                    $scheduledAt = date('Y-m-d H:i:s', strtotime("+{$hours} hours"));
                    DriverReminder::saveReminder([
                        'company_id' => $companyId,
                        'phone' => $phone,
                        'scheduled_at' => $scheduledAt,
                        'postpone_count' => $postponeCount,
                        'status' => 'pending'
                    ]);

                    return new FlowResult(
                        "حاضر كابتن، قمت بتأجيل التسجيل. سأقوم بتذكيرك بعد {$hours} ساعة لإكمال خطوات التسجيل. بالتوفيق!",
                        "postponed"
                    );
                }
            }
        }

        switch ($step) {
            case 'start':
                return new FlowResult(
                    "أهلاً بك كابتن في خدمة تسجيل كباتن تطبيق {$appName} في {$countryName} 🚖.\nيرجى إرسال اسمك الثلاثي الكامل للبدء:",
                    "ask_name"
                );

            case 'ask_name':
                if (empty($text)) {
                    return new FlowResult("يرجى إدخال اسمك الثلاثي الكامل للاستمرار:", "ask_name");
                }
                $context['name'] = $text;
                return new FlowResult(
                    "شكراً كابتن {$text}.\nالآن يرجى إرسال صورة **الوجه الأمامي للهوية الشخصية** (تأكد من أن الصورة واضحة والإضاءة جيدة):",
                    "id_front"
                );

            case 'id_front':
                return $this->processOcrStep(
                    $step,
                    $messageData,
                    $context,
                    "id_front_sy",
                    "national_number",
                    "عذراً كابتن، لم أتمكن من قراءة الرقم الوطني من الهوية بوضوح. يرجى إرسال صورة أخرى للوجه الأمامي للهوية الشخصية تكون أكثر وضوحاً:",
                    "تم استخراج الرقم الوطني بنجاح ✅.\nالآن، يرجى إرسال صورة **الوجه الخلفي للهوية الشخصية**:",
                    "id_back"
                );

            case 'id_back':
                return $this->processOcrStep(
                    $step,
                    $messageData,
                    $context,
                    "id_back_sy",
                    "gender",
                    "عذراً كابتن، لم أتمكن من قراءة بيانات الوجه الخلفي للهوية بوضوح. يرجى إرسال صورة أخرى للوجه الخلفي للهوية الشخصية:",
                    "تم استخراج البيانات بنجاح ✅.\nيرجى إرسال صورة **الوجه الأمامي لرخصة القيادة**:",
                    "driving_license_front"
                );

            case 'driving_license_front':
                return $this->processOcrStep(
                    $step,
                    $messageData,
                    $context,
                    "driving_license_sy_front",
                    "national_number",
                    "عذراً كابتن، لم أتمكن من قراءة رخصة القيادة بوضوح. يرجى إرسال صورة أخرى واضحة للوجه الأمامي لرخصة القيادة:",
                    "تم استخراج بيانات رخصة القيادة بنجاح ✅.\nيرجى إرسال صورة **الوجه الخلفي لرخصة القيادة**:",
                    "driving_license_back"
                );

            case 'driving_license_back':
                return $this->processOcrStep(
                    $step,
                    $messageData,
                    $context,
                    "driving_license_sy_back",
                    "license_number",
                    "عذراً كابتن، لم أتمكن من قراءة الوجه الخلفي لرخصة القيادة بوضوح. يرجى إعادة إرسال الصورة بشكل أكثر وضوحاً:",
                    "تم استخراج البيانات بنجاح ✅.\nيرجى إرسال صورة **الوجه الأمامي لرخصة السيارة (الرخصة البرتقالية)**:",
                    "vehicle_license_front"
                );

            case 'vehicle_license_front':
                return $this->processOcrStep(
                    $step,
                    $messageData,
                    $context,
                    "vehicle_license_sy_front",
                    "car_plate",
                    "عذراً كابتن، لم أتمكن من قراءة رقم لوحة السيارة بوضوح. يرجى إرسال صورة واضحة للوجه الأمامي لرخصة السيارة:",
                    "تم استخراج رقم اللوحة بنجاح ✅.\nيرجى إرسال صورة **الوجه الخلفي لرخصة السيارة (الرخصة البرتقالية)**:",
                    "vehicle_license_back"
                );

            case 'vehicle_license_back':
                return $this->processOcrStep(
                    $step,
                    $messageData,
                    $context,
                    "vehicle_license_sy_back",
                    "chassis",
                    "عذراً كابتن، لم أتمكن من قراءة مواصفات السيارة بوضوح. يرجى إرسال صورة واضحة للوجه الخلفي لرخصة السيارة:",
                    "تم استخراج مواصفات السيارة بنجاح ✅.\nيرجى إرسال صورة **وثيقة غير محكوم (لا حكم عليه)**:",
                    "criminal_record"
                );

            case 'criminal_record':
                if (empty($messageData['image']) || empty($messageData['imageMimeType'])) {
                    return new FlowResult("الرجاء إرسال صورة وثيقة غير محكوم (لا حكم عليه) للاستمرار:", "criminal_record");
                }

                // Save non-OCR criminal record image
                $imageUrl = $this->saveIncomingImage($step, $phone, $messageData);
                if (!$imageUrl) {
                    return new FlowResult("عذراً، فشل حفظ الصورة. الرجاء إعادة إرسال صورة الوثيقة:", "criminal_record");
                }

                // Upload criminal record to Siro
                $fullPath = __DIR__ . '/../../../../public' . $imageUrl;
                $criminalSiroUrl = SiroService::uploadDocument(
                    $this->country,
                    SiroService::formatPhone($phone, $this->country),
                    'criminal_record',
                    $fullPath,
                    $messageData['imageMimeType']
                );
                $context['criminal_record_siro_url'] = $criminalSiroUrl;

                // Securely save registration data to local database
                try {
                    DriverOcrData::saveSecure([
                        'company_id' => $companyId,
                        'phone' => $phone,
                        'name' => $context['name'],
                        'id_front_url' => $context['id_front_url'] ?? null,
                        'id_front_ocr' => $context['id_front_ocr'] ?? null,
                        'id_back_url' => $context['id_back_url'] ?? null,
                        'id_back_ocr' => $context['id_back_ocr'] ?? null,
                        'driving_license_front_url' => $context['driving_license_front_url'] ?? null,
                        'driving_license_front_ocr' => $context['driving_license_front_ocr'] ?? null,
                        'driving_license_back_url' => $context['driving_license_back_url'] ?? null,
                        'driving_license_back_ocr' => $context['driving_license_back_ocr'] ?? null,
                        'vehicle_license_front_url' => $context['vehicle_license_front_url'] ?? null,
                        'vehicle_license_front_ocr' => $context['vehicle_license_front_ocr'] ?? null,
                        'vehicle_license_back_url' => $context['vehicle_license_back_url'] ?? null,
                        'vehicle_license_back_ocr' => $context['vehicle_license_back_ocr'] ?? null,
                        'criminal_record_url' => $imageUrl,
                        'status' => 'ocr_completed'
                    ]);
                } catch (\Exception $dbEx) {
                    error_log("[Registration Flow Error] DB Write Failed: " . $dbEx->getMessage());
                    return new FlowResult("عذراً، حدث خطأ أثناء حفظ طلبك في قاعدة البيانات. يرجى المحاولة مرة أخرى لاحقاً.", "criminal_record");
                }

                // Register driver in Siro with Siro-hosted URLs
                $docUrls = [
                    'id_front' => $context['id_front_siro_url'] ?? '',
                    'id_back' => $context['id_back_siro_url'] ?? '',
                    'driving_license_front' => $context['driving_license_front_siro_url'] ?? '',
                    'driving_license_back' => $context['driving_license_back_siro_url'] ?? '',
                    'vehicle_license_front' => $context['vehicle_license_front_siro_url'] ?? '',
                    'vehicle_license_back' => $context['vehicle_license_back_siro_url'] ?? '',
                    'criminal_record' => $criminalSiroUrl ?? '',
                ];

                $idOcr = $context['id_front_ocr'] ?? [];
                $vlOcr = $context['vehicle_license_front_ocr'] ?? [];
                $vlbOcr = $context['vehicle_license_back_ocr'] ?? [];
                $dlOcr = $context['driving_license_front_ocr'] ?? [];

                $formattedPhone = SiroService::formatPhone($phone, $this->country);
                $driverId = 'DRV' . date('YmdHis') . rand(100, 999);

                $driverData = [
                    'phone' => $formattedPhone,
                    'password' => substr(md5($formattedPhone . time()), 0, 12),
                    'first_name' => explode(' ', $context['name'] ?? '')[0] ?? $context['name'],
                    'last_name' => implode(' ', array_slice(explode(' ', $context['name'] ?? ''), 1)) ?: $context['name'],
                    'name_arabic' => $context['name'] ?? '',
                    'national_number' => $idOcr['national_number'] ?? '',
                    'birthdate' => $idOcr['dob'] ?? '',
                    'address' => $idOcr['address'] ?? '',
                    'id' => $driverId,
                ];

                $carData = [
                    'vin' => $vlbOcr['chassis'] ?? $vlOcr['vin'] ?? '',
                    'car_plate' => $vlOcr['car_plate'] ?? '',
                    'make' => $vlbOcr['make'] ?? '',
                    'model' => $vlbOcr['model'] ?? '',
                    'year' => $vlbOcr['year'] ?? '',
                    'color' => $vlOcr['color'] ?? '',
                    'color_hex' => $vlOcr['color_hex'] ?? '#000000',
                    'owner' => $vlOcr['owner'] ?? '',
                    'fuel' => $vlbOcr['fuel'] ?? '',
                    'expiration_date' => $vlOcr['issue_date'] ?? '',
                    'vehicle_category_id' => 1,
                ];

                $syroResult = SiroService::registerDriver($driverData, $carData, $docUrls, $this->country);

                $appNames = [
                    'syria' => 'سيرو',
                    'jordan' => 'سيرو',
                    'egypt' => 'سيرو',
                ];
                $appName = $appNames[$this->country] ?? 'سيرو';

                if ($syroResult && ($syroResult['status'] ?? '') === 'success') {
                    DriverOcrData::saveSecure([
                        'company_id' => $companyId,
                        'phone' => $phone,
                        'name' => $context['name'],
                        'status' => 'registered'
                    ]);

                    return new FlowResult(
                        "شكراً لك كابتن، تم تسجيلك بنجاح في تطبيق {$appName} 🚖✅\nسيتم مراجعة طلبك من قبل فريق الخدمة وتفعيل حسابك قريباً. يومك سعيد!",
                        "finished",
                        true
                    );
                } else {
                    $errMsg = $syroResult['message'] ?? 'خطأ غير معروف';
                    error_log("[Registration Flow] Siro registration failed: " . json_encode($syroResult));

                    return new FlowResult(
                        "تم حفظ مستنداتك بنجاح في نظامنا. لكن حدث تأخير في تسجيلك على تطبيق {$appName}. سيقوم فريقنا بمراجعة بياناتك وتفعيل حسابك في أقرب وقت. شكراً لصبرك! 🙏",
                        "finished",
                        true
                    );
                }

            default:
                return new FlowResult("خطأ في تحديد خطوة المسار.", "finished", true);
        }
    }

    /**
     * Process document upload and OCR extraction step
     */
    private function processOcrStep(
        string $step,
        array $messageData,
        array &$context,
        string $promptKey,
        string $requiredJsonKey,
        string $failMessage,
        string $successMessage,
        string $nextStep
    ): FlowResult {
        if (empty($messageData['image']) || empty($messageData['imageMimeType'])) {
            return new FlowResult("الرجاء إرسال الصورة المطلوبة للمتابعة:", $step);
        }

        $imageUrl = $this->saveIncomingImage($step, $messageData['phone'], $messageData);
        if (!$imageUrl) {
            return new FlowResult("عذراً، فشل حفظ الصورة. الرجاء إعادة المحاولة وإرسال الصورة:", $step);
        }

        // Upload to Siro and store the signed URL
        $fullPath = __DIR__ . '/../../../../public' . $imageUrl;
        $siroUrl = SiroService::uploadDocument(
            $this->country,
            SiroService::formatPhone($messageData['phone'], $this->country),
            $this->stepToDocType[$step] ?? $step,
            $fullPath,
            $messageData['imageMimeType']
        );
        if ($siroUrl) {
            $context[$step . '_siro_url'] = $siroUrl;
            error_log("[DriverRegistrationFlow] Uploaded {$step} to Siro: {$siroUrl}");
        } else {
            $context[$step . '_siro_url'] = null;
            error_log("[DriverRegistrationFlow] Warning: Failed to upload {$step} to Siro, using local URL");
        }

        $companyId = $context['company_id'] ?? 1;

        // Check subscription limit for OCR
        if ($companyId !== 1) {
            if (!\App\Models\CompanySubscriptionUsage::hasRemainingLimit($companyId, 'ocr')) {
                error_log("[DriverRegistrationFlow] Company {$companyId} has exceeded its OCR limit.");
                return new FlowResult("⚠️ عذراً، لقد استهلك هذا المتجر الحد المسموح له من تحليل الصور والوصولات لهذا الشهر. يرجى إرسال استفسارك نصياً.", $step);
            }
        }

        $rule = ChatbotRule::findActiveForRule($companyId);
        $configuredGeminiKey = ($rule && !empty($rule['gemini_api_key'])) ? $rule['gemini_api_key'] : null;
        $apiKey = GeminiService::getGeminiApiKey($configuredGeminiKey);

        if (empty($apiKey)) {
            error_log("[DriverRegistrationFlow] Gemini API key not configured.");
            return new FlowResult("عذراً، عطل فني في خادم معالجة الصور بالذكاء الاصطناعي. يرجى المحاولة لاحقاً.", $step);
        }

        $prompt = $this->prompts[$step] ?? '';
        $rawOcr = GeminiService::generateOcrFromImage($apiKey, $prompt, $messageData['image'], $messageData['imageMimeType']);

        if (!$rawOcr) {
            error_log("[DriverRegistrationFlow] OCR response empty or model request failed.");
            return new FlowResult($failMessage, $step);
        }

        $ocrData = json_decode($rawOcr, true);
        if (!$ocrData || (empty($ocrData[$requiredJsonKey]) && !array_key_exists($requiredJsonKey, $ocrData))) {
            error_log("[DriverRegistrationFlow] Missing required key '$requiredJsonKey' in OCR response: " . $rawOcr);
            return new FlowResult($failMessage, $step);
        }

        // Increment stats on successful OCR processing
        if ($companyId !== 1) {
            \App\Models\CompanySubscriptionUsage::incrementUsage($companyId, 'ocr');
            \App\Models\CompanySubscriptionUsage::incrementUsage($companyId, 'request');
        }

        // Save URL and OCR JSON string in the conversation context
        $context[$step . '_url'] = $imageUrl;
        $context[$step . '_ocr'] = $ocrData;

        return new FlowResult($successMessage, $nextStep);
    }

    /**
     * Decode base64 image data and save it to the public directory
     */
    private function saveIncomingImage(string $step, string $phone, array $messageData): ?string
    {
        try {
            $extension = 'jpg';
            if (strpos($messageData['imageMimeType'], 'png') !== false) {
                $extension = 'png';
            }

            $uniqueName = 'driver_' . $step . '_' . md5($phone . time()) . '.' . $extension;
            $uploadDir = __DIR__ . '/../../../../public/uploads/documents/';

            if (!is_dir($uploadDir)) {
                mkdir($uploadDir, 0755, true);
            }

            $uploadPath = $uploadDir . $uniqueName;
            $imgData = base64_decode($messageData['image']);
            
            if (file_put_contents($uploadPath, $imgData) === false) {
                error_log("[DriverRegistrationFlow] Failed to write image file: " . $uploadPath);
                return null;
            }

            return '/uploads/documents/' . $uniqueName;
        } catch (\Exception $e) {
            error_log("[DriverRegistrationFlow Exception] Failed to save image: " . $e->getMessage());
            return null;
        }
    }

    /**
     * Detect if user wants to postpone, and return hours_delay if so.
     */
    private function detectPostponement(string $text, string $apiKey, int $companyId): ?array
    {
        if (empty($text)) {
            return null;
        }

        // Quick heuristic check: if the message is too long, or clearly doesn't contain postponement keywords, skip to save API costs
        $keywords = ['بعدين', 'بكرا', 'بكرة', 'بعد', 'شوي', 'ثانية', 'مشغول', 'المسا', 'الليل', 'تأجيل', 'وقت ثاني', 'تعبان', 'بعدين برسل', 'بعدين ببعت', 'ببعثهم بعدين', 'ببعتهم بعدين', 'بعدين بكمل'];
        $hasKeyword = false;
        foreach ($keywords as $kw) {
            if (mb_strpos($text, $kw) !== false) {
                $hasKeyword = true;
                break;
            }
        }

        if (!$hasKeyword && mb_strlen($text) > 100) {
            return null;
        }

        $systemPrompt = "You are an assistant that detects if a user wants to postpone, delay, or complete a registration flow later. Analyze the Arabic message.";
        $userMessage = <<<EOT
Analyze the following Arabic message (often in Syrian dialect) to determine if the user wants to postpone/delay sending documents or complete the registration later.

Message: "{$text}"

Respond with ONLY a valid JSON object matching this schema:
{
  "wants_postpone": true/false,
  "hours_delay": 12
}
Do not include any markdown, code blocks, or explanations.
EOT;

        $response = GeminiService::generateResponse($apiKey, $systemPrompt, $userMessage);
        if (empty($response)) {
            return null;
        }

        // Increment request limit on successful postponement check API call
        if ($companyId !== 1) {
            \App\Models\CompanySubscriptionUsage::incrementUsage($companyId, 'request');
        }

        // Clean markdown block if present
        $response = trim(preg_replace('/```json|```/', '', $response));
        $data = json_decode($response, true);
        if (isset($data['wants_postpone']) && $data['wants_postpone'] === true) {
            return [
                'hours' => isset($data['hours_delay']) ? (int)$data['hours_delay'] : 12
            ];
        }

        return null;
    }
}
