# /opt/docker/dev/service_finder/backend/app/workers/vehicle/ultimatespecs/vehicle_ultimate_r2_enricher.py
#!/usr/bin/env python3
"""
Worker: vehicle_ultimate_r2_enricher
Producer-Consumer lánc harmadik eleme (Az Elemző). Offline adattisztítást és strukturálást végez.
Kivesz egy feldolgozandó sort a vehicle.external_reference_library táblából (pipeline_status='pending_enrich'),
hozzácsatolja a vehicle.auto_data_crawler_queue adatait, kinyeri a standard értékeket a nyers JSON-ből,
és strukturált JSON-be csomagolja (standardized + _raw).
"""

import asyncio
import logging
import random
import sys
import signal
import json
import re
from datetime import datetime
from typing import Optional, Dict, Any, List, Tuple

from sqlalchemy import text, select, and_, or_
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession

from app.database import AsyncSessionLocal

# Logging konfiguráció
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [R2-ENRICHER] %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger("R2-ENRICHER")

# Konfiguráció
SLEEP_INTERVAL = random.uniform(1, 3)  # 1-3 mp között várakozás

# Fuzzy mapping a metrikákhoz
FUZZY_MAPPING = {
    "power_kw": ["horsepower", "total electric power", "engine power", "maximum power", "power"],
    "engine_capacity": ["engine displacement", "displacement", "capacity", "cm3", "cu-in"],
    "torque_nm": ["maximum torque", "total electric torque", "torque"],
    "max_speed": ["top speed", "maximum speed"],
    "curb_weight": ["curb weight", "weight"],
    "wheelbase": ["wheelbase"],
    "seats": ["num. of seats", "seats"]
}

# Szöveges mezők keresési kulcsszavai
TEXT_FIELD_KEYWORDS = {
    "fuel_type": ["fuel type", "fuel", "engine fuel", "fuel system"],
    "transmission_type": ["transmission", "gear", "gearbox"],
    "drive_type": ["drive type", "drive", "drivetrain"],
    "body_type": ["body type", "body", "car body"]
}


class UltimateSpecsEnricher:
    def __init__(self):
        self.running = True

    async def fetch_next_library_item(self, session: AsyncSession) -> Optional[Dict[str, Any]]:
        """
        Kivesz egy feldolgozandó sort a Library-ből.
        """
        query = text("""
            SELECT id, specifications, make, model, year_from
            FROM vehicle.external_reference_library
            WHERE pipeline_status = 'pending_enrich'
            FOR UPDATE SKIP LOCKED LIMIT 1
        """)
        
        try:
            result = await session.execute(query)
            row = result.fetchone()
            if row:
                return {
                    "id": row[0],
                    "specifications": row[1] if isinstance(row[1], dict) else {},
                    "make": row[2],
                    "model": row[3],
                    "year_from": row[4]
                }
            return None
        except SQLAlchemyError as e:
            logger.error(f"SQL hiba a lekérdezés során: {e}")
            return None

    def extract_fuzzy_metric(self, specifications: Dict[str, Any], target_key: str, keywords: List[str]) -> Optional[float]:
        """
        Keres a specifications szótárban a megadott kulcsszavak alapján, és számot próbál kinyerni.
        """
        if not specifications:
            return None
        
        # Először próbáljuk meg a kulcsokat (case-insensitive)
        spec_lower = {k.lower(): v for k, v in specifications.items()}
        
        for keyword in keywords:
            for key, value in spec_lower.items():
                if keyword.lower() in key:
                    # Ha a érték szám vagy string, próbáljuk kinyerni a számot
                    num = self.clean_number(value)
                    if num is not None:
                        # Ha a kulcs tartalmazza a "hp" vagy "horsepower" és a cél kW, konvertáljuk
                        if target_key == "power_kw" and ("hp" in key or "horsepower" in key):
                            # hp -> kW konverzió (1 hp = 0.7457 kW)
                            num = num * 0.7457
                        return num
        return None

    def clean_number(self, value: Any) -> Optional[float]:
        """
        Kinyeri a számot egy stringből vagy más típusból.
        """
        if value is None:
            return None
        if isinstance(value, (int, float)):
            return float(value)
        if isinstance(value, str):
            # Távolítsuk el a nem szám karaktereket, kivéve pont és mínusz
            # Keresünk mintákat mint "120 kW" vagy "120kW"
            match = re.search(r'([-+]?\d*\.?\d+)\s*(?:kW|hp|cc|Nm|kg|km/h|mph)?', value, re.IGNORECASE)
            if match:
                try:
                    return float(match.group(1))
                except ValueError:
                    pass
            # Ha nincs specifikus egység, próbáljunk meg bármilyen számot kinyerni
            matches = re.findall(r'[-+]?\d*\.?\d+', value)
            if matches:
                try:
                    return float(matches[0])
                except ValueError:
                    pass
        return None

    def extract_text_field(self, specifications: Dict[str, Any], keywords: List[str]) -> Optional[str]:
        """
        Kinyer egy szöveges mezőt a specifications-ből a kulcsszavak alapján.
        """
        if not specifications:
            return None
        
        spec_lower = {k.lower(): v for k, v in specifications.items()}
        
        for keyword in keywords:
            for key, value in spec_lower.items():
                if keyword.lower() in key:
                    if isinstance(value, str):
                        return value.strip()
                    elif isinstance(value, (int, float)):
                        return str(value)
        return None

    def enrich_specifications(self, raw_specs: Dict[str, Any], make: str, model: str, year_from: int) -> Dict[str, Any]:
        """
        Fő strukturáló függvény: kinyeri a standard értékeket és létrehozza az új JSON struktúrát.
        """
        standardized = {}
        
        # Metrikák kinyerése
        for target_key, keywords in FUZZY_MAPPING.items():
            value = self.extract_fuzzy_metric(raw_specs, target_key, keywords)
            standardized[target_key] = value
        
        # Szöveges mezők kinyerése
        for field, keywords in TEXT_FIELD_KEYWORDS.items():
            value = self.extract_text_field(raw_specs, keywords)
            standardized[field] = value
        
        # Készítsük az új JSON struktúrát
        updated_specifications = {
            "standardized": standardized,
            "_raw": raw_specs  # Az eredeti R1 adat érintetlenül megmarad!
        }
        
        return updated_specifications

    async def process_item(self, session: AsyncSession, item: Dict[str, Any]) -> bool:
        """
        Feldolgoz egy elemet: kinyeri az adatokat, frissíti az adatbázist.
        """
        try:
            logger.info(f"Feldolgozás: ID={item['id']}, {item['make']} {item['model']} ({item['year_from']})")
            
            # Adatok kinyerése és strukturálása
            updated_specs = self.enrich_specifications(
                item['specifications'],
                item['make'],
                item['model'],
                item['year_from']
            )
            
            # Kinyert értékek a fizikai oszlopokhoz
            power_kw = updated_specs['standardized'].get('power_kw')
            engine_cc = updated_specs['standardized'].get('engine_capacity')
            
            # UPDATE végrehajtása
            update_query = text("""
                UPDATE vehicle.external_reference_library
                SET power_kw = :power_kw,
                    engine_cc = :engine_cc,
                    make = :make,
                    model = :model,
                    year_from = :year_from,
                    specifications = :updated_specifications,
                    pipeline_status = 'pending_match'
                WHERE id = :id
            """)
            
            params = {
                "power_kw": int(power_kw) if power_kw is not None else None,
                "engine_cc": int(engine_cc) if engine_cc is not None else None,
                "make": item['make'],
                "model": item['model'],
                "year_from": item['year_from'],
                "updated_specifications": json.dumps(updated_specs),
                "id": item['id']
            }
            
            await session.execute(update_query, params)
            await session.commit()
            
            logger.info(f"Sikeres frissítés: ID={item['id']}, power_kw={power_kw}, engine_cc={engine_cc}")
            return True
            
        except Exception as e:
            logger.error(f"Hiba a feldolgozás során ID={item['id']}: {e}")
            await session.rollback()
            return False

    async def run_once(self):
        """
        Egyetlen feldolgozási ciklus.
        """
        async with AsyncSessionLocal() as session:
            try:
                # Tranzakció indítása
                async with session.begin():
                    item = await self.fetch_next_library_item(session)
                    if not item:
                        logger.debug("Nincs feldolgozandó elem")
                        return False
                    
                    success = await self.process_item(session, item)
                    return success
            except SQLAlchemyError as e:
                logger.error(f"Adatbázis hiba: {e}")
                return False

    async def run_loop(self):
        """
        Fő végtelen ciklus.
        """
        logger.info("R2 Enricher indítva...")
        
        while self.running:
            try:
                success = await self.run_once()
                if not success:
                    # Ha nincs feldolgozandó elem, várjunk egy kicsit
                    await asyncio.sleep(SLEEP_INTERVAL)
            except KeyboardInterrupt:
                logger.info("Keyboard interrupt, leállítás...")
                self.running = False
                break
            except Exception as e:
                logger.error(f"Váratlan hiba a ciklusban: {e}")
                await asyncio.sleep(SLEEP_INTERVAL)
        
        logger.info("R2 Enricher leállt")

    def stop(self):
        """Leállítási jelzés."""
        self.running = False


async def main():
    """Fő függvény."""
    enricher = UltimateSpecsEnricher()
    
    # Signal kezelés
    def signal_handler(signum, frame):
        logger.info(f"Signal {signum} fogadva, leállítás...")
        enricher.stop()
    
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)
    
    try:
        await enricher.run_loop()
    except asyncio.CancelledError:
        logger.info("Task cancelled")
    finally:
        logger.info("R2 Enricher befejezte a munkát.")


if __name__ == "__main__":
    asyncio.run(main())