"""
Smart Odometer Service - Adminisztrátor által paraméterezhető kilométeróra becslés.

A szolgáltatás a járművek kilométeróra állását becsüli a költségbejegyzések alapján,
figyelembe véve a rendszerparamétereket (ODOMETER_MIN_DAYS_FOR_AVG, ODOMETER_CONFIDENCE_THRESHOLD).
Ha az admin beállított manuális átlagot (manual_override_avg), akkor azt használja.
"""

from datetime import datetime, timedelta
from typing import Optional, Tuple
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, and_
from sqlalchemy.orm import selectinload

from app.models.vehicle import VehicleOdometerState, VehicleCost
from app.models.system import SystemParameter
from app.models import VehicleModelDefinition


class OdometerService:
    """Kilométeróra becslési szolgáltatás adminisztrációs kontrollal."""
    
    @staticmethod
    async def get_system_param(db: AsyncSession, key: str, default_value):
        """Rendszerparaméter lekérése a system.system_parameters táblából."""
        stmt = select(SystemParameter).where(
            SystemParameter.key == key,
            SystemParameter.scope_level == 'global',
            SystemParameter.is_active == True
        )
        result = await db.execute(stmt)
        param = result.scalars().first()
        if param and 'value' in param.value:
            return param.value['value']
        return default_value
    
    @staticmethod
    async def update_vehicle_stats(db: AsyncSession, vehicle_id: int) -> Optional[VehicleOdometerState]:
        """
        Frissíti a jármű kilométeróra statisztikáit.
        
        Algoritmus:
        1. Ha van manual_override_avg, használja azt.
        2. Különben számol átlagot a vehicle.costs bejegyzésekből.
        3. Figyelembe veszi az ODOMETER_MIN_DAYS_FOR_AVG paramétert.
        4. Kiszámolja a confidence_score-t a minták száma alapján.
        5. Frissíti vagy létrehozza a VehicleOdometerState rekordot.
        """
        # Rendszerparaméterek lekérése
        min_days = await OdometerService.get_system_param(db, 'ODOMETER_MIN_DAYS_FOR_AVG', 7)
        confidence_threshold = await OdometerService.get_system_param(db, 'ODOMETER_CONFIDENCE_THRESHOLD', 0.5)
        
        # Meglévő állapot lekérése
        stmt = select(VehicleOdometerState).where(VehicleOdometerState.vehicle_id == vehicle_id)
        result = await db.execute(stmt)
        odometer_state = result.scalars().first()
        
        # Költségbejegyzések lekérése dátum és odometer szerint rendezve
        cost_stmt = select(VehicleCost).where(
            VehicleCost.vehicle_id == vehicle_id,
            VehicleCost.odometer.isnot(None)
        ).order_by(VehicleCost.date.asc())
        
        cost_result = await db.execute(cost_stmt)
        costs = cost_result.scalars().all()
        
        if not costs:
            # Nincs adat, alapértelmezett értékek
            if odometer_state:
                odometer_state.daily_avg_distance = 0
                odometer_state.confidence_score = 0
                odometer_state.estimated_current_odometer = odometer_state.last_recorded_odometer
            else:
                # Jármű alapadatok lekérése
                vehicle_stmt = select(VehicleModelDefinition).where(VehicleModelDefinition.id == vehicle_id)
                vehicle_result = await db.execute(vehicle_stmt)
                vehicle = vehicle_result.scalars().first()
                
                if not vehicle:
                    return None
                
                odometer_state = VehicleOdometerState(
                    vehicle_id=vehicle_id,
                    last_recorded_odometer=0,
                    last_recorded_date=datetime.now(),
                    daily_avg_distance=0,
                    estimated_current_odometer=0,
                    confidence_score=0,
                    manual_override_avg=None
                )
                db.add(odometer_state)
            
            await db.commit()
            await db.refresh(odometer_state)
            return odometer_state
        
        # Utolsó rögzített adatok
        last_cost = costs[-1]
        last_recorded_odometer = last_cost.odometer
        last_recorded_date = last_cost.date
        
        # Manuális átlag ellenőrzése
        if odometer_state and odometer_state.manual_override_avg is not None:
            daily_avg = float(odometer_state.manual_override_avg)
            confidence = 1.0  # Manuális beállítás esetén teljes bizalom
        else:
            # Átlag számítása a költségbejegyzésekből
            valid_pairs = []
            for i in range(1, len(costs)):
                prev = costs[i-1]
                curr = costs[i]
                
                days_diff = (curr.date - prev.date).days
                km_diff = curr.odometer - prev.odometer
                
                if days_diff >= min_days and km_diff > 0:
                    daily_avg = km_diff / days_diff
                    valid_pairs.append((daily_avg, days_diff))
            
            if valid_pairs:
                # Súlyozott átlag (hosszabb időszakok nagyobb súllyal)
                total_weighted = sum(avg * weight for avg, weight in valid_pairs)
                total_days = sum(weight for _, weight in valid_pairs)
                daily_avg = total_weighted / total_days if total_days > 0 else 0
                
                # Confidence score: érvényes párok száma / összes lehetséges párok
                confidence = min(len(valid_pairs) / max(len(costs) - 1, 1), 1.0)
            else:
                daily_avg = 0
                confidence = 0
        
        # Becsült jelenlegi kilométer
        days_since_last = (datetime.now(last_recorded_date.tzinfo) - last_recorded_date).days
        estimated_odometer = last_recorded_odometer + (daily_avg * max(days_since_last, 0))
        
        # Állapot frissítése vagy létrehozása
        if odometer_state:
            odometer_state.last_recorded_odometer = last_recorded_odometer
            odometer_state.last_recorded_date = last_recorded_date
            odometer_state.daily_avg_distance = daily_avg
            odometer_state.estimated_current_odometer = estimated_odometer
            odometer_state.confidence_score = confidence
        else:
            odometer_state = VehicleOdometerState(
                vehicle_id=vehicle_id,
                last_recorded_odometer=last_recorded_odometer,
                last_recorded_date=last_recorded_date,
                daily_avg_distance=daily_avg,
                estimated_current_odometer=estimated_odometer,
                confidence_score=confidence,
                manual_override_avg=None
            )
            db.add(odometer_state)
        
        await db.commit()
        await db.refresh(odometer_state)
        return odometer_state
    
    @staticmethod
    async def get_estimated_odometer(db: AsyncSession, vehicle_id: int) -> Tuple[Optional[float], float]:
        """
        Visszaadja a jármű becsült jelenlegi kilométeróra állását és a bizalom pontszámot.
        
        Returns:
            Tuple[estimated_odometer, confidence_score]
        """
        stmt = select(VehicleOdometerState).where(VehicleOdometerState.vehicle_id == vehicle_id)
        result = await db.execute(stmt)
        odometer_state = result.scalars().first()
        
        if not odometer_state:
            # Ha nincs állapot, frissítsük
            odometer_state = await OdometerService.update_vehicle_stats(db, vehicle_id)
            if not odometer_state:
                return None, 0.0
        
        return odometer_state.estimated_current_odometer, odometer_state.confidence_score
    
    @staticmethod
    async def set_manual_override(db: AsyncSession, vehicle_id: int, daily_avg: Optional[float]) -> Optional[VehicleOdometerState]:
        """
        Adminisztrátori manuális átlag beállítása.
        
        Args:
            daily_avg: Napi átlagos kilométer (km/nap). Ha None, törli a manuális beállítást.
        """
        stmt = select(VehicleOdometerState).where(VehicleOdometerState.vehicle_id == vehicle_id)
        result = await db.execute(stmt)
        odometer_state = result.scalars().first()
        
        if not odometer_state:
            # Ha nincs állapot, hozzuk létre
            odometer_state = VehicleOdometerState(
                vehicle_id=vehicle_id,
                last_recorded_odometer=0,
                last_recorded_date=datetime.now(),
                daily_avg_distance=0,
                estimated_current_odometer=0,
                confidence_score=0,
                manual_override_avg=daily_avg
            )
            db.add(odometer_state)
        else:
            odometer_state.manual_override_avg = daily_avg
            # Frissítsük a becslést a manuális átlaggal
            if daily_avg is not None:
                days_since_last = (datetime.now(odometer_state.last_recorded_date.tzinfo) - odometer_state.last_recorded_date).days
                odometer_state.estimated_current_odometer = odometer_state.last_recorded_odometer + (daily_avg * max(days_since_last, 0))
                odometer_state.confidence_score = 1.0
        
        await db.commit()
        await db.refresh(odometer_state)
        return odometer_state