import asyncio
import httpx
import logging
import os
import re
import sys
from sqlalchemy import text
from sqlalchemy.dialects.postgresql import insert
from app.database import AsyncSessionLocal
from app.models.vehicle_definitions import VehicleModelDefinition

# Naplózás beállítása a standard kimenetre
logging.basicConfig(
    level=logging.INFO, 
    format='%(asctime)s [%(levelname)s] Robot-1-Hunter: %(message)s', 
    stream=sys.stdout
)
logger = logging.getLogger("Robot-1-Hunter")

class CatalogHunter:
    """
    Vehicle Robot 1.9.3: The Truly Invincible Hunter (SAVEPOINT PATCH)
    Kezeli az ALL_VARIANTS utasítást és row-level tranzakcióvédelmet használ.
    """
    RDW_MAIN = "https://opendata.rdw.nl/resource/m9d7-ebf2.json"
    RDW_FUEL = "https://opendata.rdw.nl/resource/8ys7-d773.json"
    RDW_ENGINE = "https://opendata.rdw.nl/resource/jh96-v4pq.json"
    
    RDW_TOKEN = os.getenv("RDW_APP_TOKEN")
    HEADERS = {"X-App-Token": RDW_TOKEN} if RDW_TOKEN else {}
    BATCH_SIZE = 50

    @classmethod
    def normalize(cls, text_val: str) -> str:
        if not text_val: return ""
        return re.sub(r'[^a-zA-Z0-9]', '', text_val).lower()

    @classmethod
    def parse_int(cls, value) -> int:
        try:
            if value is None or str(value).strip() == "": return 0
            return int(float(value))
        except (ValueError, TypeError): return 0

    @classmethod
    def parse_float(cls, value) -> float:
        try:
            if value is None or str(value).strip() == "": return 0.0
            return float(value)
        except (ValueError, TypeError): return 0.0

    @classmethod
    async def fetch_with_retry(cls, client: httpx.AsyncClient, url: str, retries: int = 3):
        """ Hibatűrő HTTP lekérdezés exponenciális várakozással. """
        for attempt in range(retries):
            try:
                resp = await client.get(url, headers=cls.HEADERS)
                if resp.status_code == 200: 
                    return resp
                elif resp.status_code == 429: # Rate limit
                    await asyncio.sleep(2 ** attempt)
                else: 
                    return resp
            except httpx.RequestError as e:
                if attempt == retries - 1:
                    logger.debug(f"Hálózati hiba: {e}")
                    raise
                await asyncio.sleep(2 ** attempt)
        return None

    @classmethod
    async def fetch_tech_details(cls, client, plate):
        """ Technikai adatok (üzemanyag, teljesítmény, motorkód) begyűjtése. """
        results = {
            "power_kw": 0, "engine_code": None, "euro_class": None, 
            "fuel_desc": "Unknown", "co2": 0, "consumption": 0.0
        }
        try:
            # Üzemanyag adatok
            f_resp = await cls.fetch_with_retry(client, f"{cls.RDW_FUEL}?kenteken={plate}")
            if f_resp and f_resp.status_code == 200 and f_resp.json():
                f = f_resp.json()[0]
                p1 = cls.parse_int(f.get("netto_maximum_vermogen") or f.get("nettomaximumvermogen"))
                p2 = cls.parse_int(f.get("nominaal_continu_maximum_vermogen") or f.get("nominaalcontinuvermogen"))
                results.update({
                    "power_kw": max(p1, p2),
                    "fuel_desc": f.get("brandstof_omschrijving") or "Unknown",
                    "euro_class": f.get("euro_klasse") or f.get("uitlaatemissieniveau"),
                    "co2": cls.parse_int(f.get("co2_uitstoot_gecombineerd")),
                    "consumption": cls.parse_float(f.get("brandstofverbruik_gecombineerd"))
                })
            
            # Motorkód adatok
            e_resp = await cls.fetch_with_retry(client, f"{cls.RDW_ENGINE}?kenteken={plate}")
            if e_resp and e_resp.status_code == 200 and e_resp.json():
                results["engine_code"] = e_resp.json()[0].get("motorcode")
        except Exception: 
            pass
        return results

    @classmethod
    async def process_make_model(cls, db, task_id, make_name, model_name, v_class, priority):
        """ Egy adott márka/modell (vagy wildcard) feldolgozása. """
        clean_make = make_name.strip().upper()
        clean_model = model_name.strip().upper()
        logger.info(f"🎯 ADATGYŰJTÉS INDUL: {clean_make} {clean_model}")
        
        offset = 0
        async with httpx.AsyncClient(timeout=30.0) as client:
            while True:
                # Dinamikus paraméterezés: ALL_VARIANTS esetén nem szűrünk modellre
                if clean_model == 'ALL_VARIANTS':
                    params = f"merk={clean_make}&$limit={cls.BATCH_SIZE}&$offset={offset}&$order=kenteken DESC"
                else:
                    params = f"merk={clean_make}&handelsbenaming={clean_model}&$limit={cls.BATCH_SIZE}&$offset={offset}&$order=kenteken DESC"
                
                try:
                    r = await cls.fetch_with_retry(client, f"{cls.RDW_MAIN}?{params}")
                    batch = r.json() if r and r.status_code == 200 else []
                except Exception as e:
                    logger.error(f"❌ API hiba: {e}")
                    break
                
                if not batch: 
                    break 

                for item in batch:
                    plate = item.get("kenteken", "UNKNOWN")
                    try:
                        # SAVEPOINT: Ha egy rekord mentése hibás, a tranzakció blokk nem sérül
                        async with db.begin_nested():
                            tech = await cls.fetch_tech_details(client, plate)
                            
                            # Valódi modellnév kinyerése (Wildcard esetén fontos)
                            actual_model = (item.get("handelsbenaming") or clean_model).upper()
                            norm_name = cls.normalize(actual_model.replace(clean_make, "").strip() or actual_model)

                            stmt = insert(VehicleModelDefinition).values(
                                make=clean_make,
                                marketing_name=actual_model,
                                normalized_name=norm_name,
                                variant_code=item.get("variant", "UNKNOWN"),
                                version_code=item.get("uitvoering", "UNKNOWN"),
                                type_approval_number=item.get("typegoedkeuringsnummer"),
                                technical_code=plate,
                                engine_capacity=cls.parse_int(item.get("cilinderinhoud")),
                                power_kw=tech["power_kw"],
                                fuel_type=tech["fuel_desc"],
                                engine_code=tech["engine_code"],
                                seats=cls.parse_int(item.get("aantal_zitplaatsen")),
                                doors=cls.parse_int(item.get("aantal_deuren")),
                                width=cls.parse_int(item.get("breedte")),
                                wheelbase=cls.parse_int(item.get("wielbasis")),
                                list_price=cls.parse_int(item.get("catalogusprijs")),
                                max_speed=cls.parse_int(item.get("maximale_constructiesnelheid")),
                                curb_weight=cls.parse_int(item.get("massa_ledig_voertuig")),
                                max_weight=cls.parse_int(item.get("technische_max_massa_voertuig")),
                                body_type=item.get("inrichting"),
                                co2_emissions_combined=tech["co2"],
                                fuel_consumption_combined=tech["consumption"],
                                euro_classification=tech["euro_class"],
                                cylinders=cls.parse_int(item.get("aantal_cilinders")),
                                vehicle_class=v_class,
                                priority_score=priority,
                                status="unverified", # R2 Researcher számára előkészítve
                                source="MEGA-HUNTER-v1.9.3"
                            ).on_conflict_do_nothing(
                                index_elements=['make', 'normalized_name', 'variant_code', 'version_code', 'fuel_type']
                            )
                            await db.execute(stmt)
                            
                    except Exception as e:
                        logger.warning(f"⚠️ Sor eldobva ({plate}): {e}")
                
                # Batch commit a sikeres sorok után
                await db.commit()
                
                offset += len(batch)
                if offset >= 500: # Biztonsági korlát egy-egy márkánál
                    break 
                await asyncio.sleep(0.5)
            
            # Discovery feladat lezárása
            await db.execute(
                text("UPDATE vehicle.catalog_discovery SET status = 'processed' WHERE id = :id"), 
                {"id": task_id}
            )
            await db.commit()

    @classmethod
    async def run(cls):
        logger.info("🤖 Mega-Hunter v1.9.3 ONLINE (SAVEPOINT ENABLED)")
        while True:
            try:
                async with AsyncSessionLocal() as db:
                    # ATOMI ZÁROLÁS: Keresés, Zárolás és Állapotváltás egy lépésben
                    query = text("""
                        UPDATE vehicle.catalog_discovery
                        SET status = 'processing'
                        WHERE id = (
                            SELECT id FROM vehicle.catalog_discovery
                            WHERE status = 'pending'
                            ORDER BY priority_score DESC
                            FOR UPDATE SKIP LOCKED
                            LIMIT 1
                        )
                        RETURNING id, make, model, vehicle_class, priority_score;
                    """)
                    
                    result = await db.execute(query)
                    task = result.fetchone()
                    await db.commit()
                    
                    if task: 
                        await cls.process_make_model(db, task[0], task[1], task[2], task[3], task[4])
                    else: 
                        # Ha nincs munka, 30 másodperc pihenő
                        await asyncio.sleep(30)
            except Exception as e:
                logger.error(f"💀 Főciklus hiba: {e}")
                await asyncio.sleep(10)

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