import time
import re
import cv2
import numpy as np
import requests
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import uvicorn
import os

# Initialization
app = FastAPI(title="Fast OCR API")

# إيقاف فحص مصدر الموديل لتسريع التشغيل وإلغاء التحذير
os.environ['PADDLE_PDX_DISABLE_MODEL_SOURCE_CHECK'] = 'True'

try:
    from paddleocr import PaddleOCR
    import logging
    # كتم سجلات PaddleOCR لتجنب الإزعاج
    logging.getLogger("ppocr").setLevel(logging.ERROR) 
    
    # تحميل الموديل مرة واحدة فقط في الذاكرة
    ocr_model = PaddleOCR(use_textline_orientation=False, lang='ar')
except Exception as e:
    print(f"Error loading PaddleOCR: {e}")
    ocr_model = None

class ImageRequest(BaseModel):
    url: str

@app.post("/extract")
async def extract_receipt(req: ImageRequest):
    if ocr_model is None:
        raise HTTPException(status_code=500, detail="OCR Model not loaded.")
        
    start_time = time.time()
    
    try:
        # تحميل الصورة سريعاً
        resp = requests.get(req.url, timeout=5)
        resp.raise_for_status()
        
        # معالجة وتصغير الصورة للحفاظ على سرعة (أقل من ثانية)
        image_array = np.asarray(bytearray(resp.content), dtype=np.uint8)
        image = cv2.imdecode(image_array, cv2.IMREAD_COLOR)
        
        max_dim = 1200
        h, w = image.shape[:2]
        if max(h, w) > max_dim:
            scale = max_dim / max(h, w)
            image = cv2.resize(image, (int(w * scale), int(h * scale)))
            
        # 3. استخراج النص
        try:
            result = ocr_model.ocr(image)
        except Exception:
            # طريقة بديلة في حال فشلت الطريقة الأولى
            result = list(ocr_model.predict(input=image))
            
        if hasattr(result, '__iter__') and not isinstance(result, list):
            result = list(result)
            
        # تحويل كل المخرجات أياً كانت لكلمة واحدة مفرودة (String)
        raw_output = str(result)
        
        # استخراج النصوص فقط (تجاهل الإحداثيات ونسب الثقة)
        texts = re.findall(r"\('([^']+)',\s*\d+\.\d+\)", raw_output)
        clean_text = " ".join(texts)
        
        # 4. التقاط المبلغ والعملة
        amount = None
        currency = None
        
        # الكلمات العربية قد تستخرج معكوسة
        currencies = r"(دولار|يورو|ليرة|دينار|ريال|شيكل|رالود|وروي|ةريل|رانيد|لاير|لكيش)"
        
        # نستخدم [^\d] للسماح بوجود كلمات بين الرقم والعملة
        pattern1 = r'(\d+[\d\.,]*)([^\d]{0,20}?)' + currencies
        pattern2 = currencies + r'([^\d]{0,20}?)(\d+[\d\.,]*)'
        
        matches = []
        for m in re.finditer(pattern1, clean_text):
            matches.append({
                'amount': m.group(1),
                'currency': m.group(3),
                'dist': len(m.group(2))
            })
            
        for m in re.finditer(pattern2, clean_text):
            matches.append({
                'amount': m.group(3),
                'currency': m.group(1),
                'dist': len(m.group(2))
            })
            
        raw_currency = None
        if matches:
            # تقييم ذكي لكل نتيجة: النتيجة الأفضل هي التي المسافة فيها بين الرقم والعملة أقصر، 
            # مع إعطاء أولوية قوية للأرقام التي تحتوي على فاصلة (مثل 100,000)
            def score_match(m):
                dist_score = m['dist']
                comma_bonus = -5 if ',' in m['amount'] else 0
                length_bonus = -len(m['amount']) * 0.1
                return dist_score + comma_bonus + length_bonus
                
            matches.sort(key=score_match)
            best_match = matches[0]
            amount = best_match['amount']
            raw_currency = best_match['currency']
            
        # إصلاح مشكلة الأرقام المعكوسة بسبب الفواصل (مثل "000,100" بدلاً من "100,000")
        if amount:
            if ',' in amount:
                parts = amount.split(',')
                # إذا كان الجزء الأول أصفاراً فهذا يعني أن الرقم انقرأ بالعكس
                if parts[0] == '000' or len(parts[0]) == 3:
                    parts.reverse()
                amount = "".join(parts)
            else:
                amount = amount.replace(',', '').replace('.', '')
                
        # تعديل الكلمة المعكوسة وإرجاعها لأصلها
        currency_map = {
            'رالود': 'دولار', 'وروي': 'يورو', 'ةريل': 'ليرة', 'رانيد': 'دينار', 'لاير': 'ريال', 'لكيش': 'شيكل',
            'دولار': 'دولار', 'يورو': 'يورو', 'ليرة': 'ليرة', 'دينار': 'دينار', 'ريال': 'ريال', 'شيكل': 'شيكل'
        }
        if raw_currency:
            currency = currency_map.get(raw_currency, raw_currency)
            
        end_time = time.time()
        exec_time = round(end_time - start_time, 3)
        
        return {
            "success": True,
            "status": "success",
            "extracted_data": {
                "amount": amount,
                "currency": currency
            },
            "execution_time_seconds": exec_time,
            "full_text": clean_text
        }
        
    except Exception as e:
        end_time = time.time()
        return {
            "success": False,
            "error": str(e),
            "execution_time_seconds": round(end_time - start_time, 3)
        }

if __name__ == "__main__":
    import sys
    # عند تشغيل السكربت عبر سطر الأوامر (للاختبار)
    if len(sys.argv) > 1 and sys.argv[1].startswith("http"):
        url = sys.argv[1]
        print(f"Processing URL: {url}...")
        
        class MockReq:
            pass
        req = MockReq()
        req.url = url
        
        import asyncio
        res = asyncio.run(extract_receipt(req))
        print("\n--- Results ---")
        if not res.get("success"):
            print("❌ Error:", res.get("error"))
        else:
            print(f"Amount: {res.get('extracted_data', {}).get('amount')}")
            print(f"Currency: {res.get('extracted_data', {}).get('currency')}")
            
        print(f"Execution Time: {res.get('execution_time_seconds')} seconds")
    else:
        print("Starting FastAPI Server to keep OCR Model in memory for sub-second execution...")
        uvicorn.run(app, host="0.0.0.0", port=8000)
