<?php

namespace App\Core\Flows;

use App\Services\GeminiService;
use App\Models\DriverOcrData;
use App\Models\ChatbotRule;
use App\Models\DriverReminder;

/**
 * DriverRegistrationFlow
 * Handles step-by-step driver and vehicle registration using Gemini OCR.
 */
class DriverRegistrationFlow extends BaseFlow
{
    private array $prompts = [
        "id_front" => <<<EOT
You are an OCR expert for Syrian national ID cards (green card).

### TASK
Analyse the **front side** of the ID and return **raw JSON only** with exactly these keys:

{
  "full_name": "",          // الاسم الثلاثي أو الرباعي
  "national_number": "",    // الرقم الوطني (LATIN digits only)
  "dob": "YYYY-MM-DD",      // تاريخ الميلاد
  "address": ""             // العنوان
}

### RULES
* Read the red number on the bottom of the card.
* Convert any Eastern-Arabic digits (٠١٢٣٤٥٦٧٨٩) to Western-Arabic digits (0-9).
* `national_number` must contain **Latin digits only, no spaces or other characters**.
* If a field is missing, set it to **null**.
* Convert the birth date to ISO `YYYY-MM-DD`.
* Return valid JSON only — no extra keys, no markdown.
EOT,
        "id_back" => <<<EOT
أنت خبير OCR مختص ببطاقات الهوية السورية (الوجه الخلفي).

### المطلوب
حلّل صورة الوجه الخلفي للهوية السورية وأعد **JSON صِرف** يحتوي المفاتيح التالية فقط:

{
  "governorate": "",        // المحافظة (مثال: دمشق)
  "address": "",            // العنوان التفصيلي (حيّ، بلدة …)
  "gender": "",         //Male or Female
  "issue_date": "YYYY-MM-DD"// تاريخ الإصدار بصيغة ISO
}

### القواعد
1. حوّل أي أرقام عربية شرقية (٠١٢٣٤٥٦٧٨٩) إلى أرقام لاتينية (0-9).
2. أعدّ تاريخ الإصدار بالتقويم الميلادي بصيغة `YYYY-MM-DD`.
3. استخدم أحرف لاتينية كبيرة لزمرة الدم مع رمز ‎`+`‎ أو ‎`-`‎ فقط.
4. إذا كان أحد الحقول غير موجود مطلقًا، أعد قيمته **null**.
5. لا تُرجع أي مفاتيح إضافية أو شروح أو Markdown — JSON صالح فقط.
EOT,
        "driving_license_front" => <<<EOT
You are an OCR expert for Syrian documents.

### TASK
Analyse the **front side of a Syrian driving licence** and return **clean JSON only** with the following keys (no extra keys, no markdown):

{
  "name_arabic": "",        // الاسم الثلاثي أو الرباعي بالعربية
  "birth_place": "",        // المحافظة أو المنطقة المكتوبة بعد كلمة الولادة
  "birth_year": "",         // سنة الميلاد فقط (أربعة أرقام)
  "national_number": "",
  "civil_registry": "",     // سطر "القيد" (مثال: سهوة 3)
  "blood_type": ""          // زمرة الدم بالشكل: A+ , A- , B+ , B- , AB+ , AB- , O+ , O-
}

### RULES
* إذا كانت القيمة مفقودة تمامًا اكتب **null**.
* لا تُغيّر ترتيب المفاتيح.
* لا تُرسل أى شرح أو أسطر إضافية – JSON خالص فقط.
EOT,
        "driving_license_back" => <<<EOT
You are an OCR expert for Syrian driving licences.

### TASK
Analyse the **back side** of a Syrian driving licence and return **raw JSON only** with exactly these keys:

{
  "issue_date": "YYYY-MM-DD",    // تاريخ المنح
  "expiry_date": "YYYY-MM-DD",   // صالحة لغاية
  "license_number": "",          // رقم الإجازة
  "license_category": ""         // D1, D2, D3 … (as printed after "UNIVERSAL DRIVING LICENCE")
}

### RULES
* If a value is totally absent, set it to **null**.
* Convert all dates to ISO `YYYY-MM-DD` (Gregorian).
* Do **NOT** add extra keys, comments, or markdown — return valid JSON only.
EOT,
        "vehicle_license_front" => <<<EOT
You are an OCR expert specialized in analyzing Syrian vehicle registration cards (الرخصة البرتقالية).

Your task is to extract structured data from the **front side** of the Syrian orange vehicle card and return **raw JSON only** with the following exact fields:

{
  "car_plate": "",          // رقم المركبة الكامل مع اسم المحافظة، مأخوذ من الجهة اليسرى في السطر الأول (مثال: "155186 درعا")
  "owner": "",              // اسم المالك الكامل
  "vin": "",                // رقم الهيكل
  "color": "",              // اللون بالعربية أو الإنجليزية (مثال: "أبيض" أو "White")
  "color_hex": "",          // كود اللون بصيغة Hex (مثال: "#FFFFFF") أو #27332F إن تعذّر
  "issue_date": "YYYY-MM-DD",        // تاريخ المنح بصيغة ISO
  "inspection_date": "YYYY-MM-DD"    // تاريخ الفحص القادم بصيغة ISO
}

### Instructions & Rules:

1. Do **not** extract the "رمز المركبة" (on the right side of the first line) — use only the **left side** of the first line for `car_plate`.
2. Convert any Arabic dates (like `2024/05/13`) into ISO format `YYYY-MM-DD`.
3. If any value is missing or unreadable, return `null` for it.
4. Maintain Arabic encoding (e.g., owner name, city name, color).
5. Never guess — extract only what's visually found on the card.
6. Never include any explanation or extra output — return the JSON only.

Example of valid `car_plate`:
- "155186 درعا"
- "45291 دمشق"
- "122334 حمص"
EOT,
        "vehicle_license_back" => <<<EOT
You are an OCR expert for Syrian vehicle registration cards (orange card).

### TASK
Analyse the **back side** of the card and return **raw JSON only** with exactly these keys (no more, no less):

{
  "make": "",         // الصانع (Hyundai …)
  "model": "",        // الطراز (H1 …)
  "year": "",         // سنة الصنع بالأرقام اللاتينية (e.g. "2019")
  "fuel": "",         // نوع الوقود (بنزين، ديزل …) أو بالإنجليزية (Petrol, Diesel,electric)
  "chassis": ""       // رقم الهيكل (VIN)
}

### RULES
* Convert any Eastern-Arabic digits (٠١٢٣٤٥٦٧٨٩) to Western digits (0-9).
* Normalise color names to standard English if possible, then map to a common Hex code  
  • "أبيض / White" → **#FFFFFF**  
  • "أسود / Black" → **#000000**  
  • "أحمر / Red"   → **#FF0000**  
  • "أزرق / Blue"  → **#0000FF**  
  • … (use the closest basic colour); if no match, set **color_hex = null**.
* If any field is unreadable or absent, set its value to **null**.
* Do **NOT** include extra keys, comments, or markdown — output valid JSON only.
EOT
    ];

    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;

        // If currently postponed and user sends a message, resume the flow
        if ($step === 'postponed') {
            // Cancel active reminder
            $activeReminder = DriverReminder::findActive($companyId, $phone);
            if ($activeReminder) {
                DriverReminder::update($activeReminder['id'], ['status' => 'cancelled']);
            }
            // Restore previous step
            $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;

                    // Schedule reminder
                    $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(
                    "أهلاً بك كابتن في خدمة تسجيل كباتن تطبيق انطلق 🚖.\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");
                }

                // 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");
                }

                return new FlowResult(
                    "شكراً لك كابتن، لقد تم استلام كافة المستندات والتحقق منها بنجاح. سيقوم فريق خدمة عملاء انطلق بمراجعة طلبك وتفعيل حسابك بأسرع وقت ممكن. يومك سعيد! 🚖",
                    "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);
        }

        $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;
    }
}
