# /opt/docker/dev/service_finder/backend/app/services/analytics_service.py
"""
TCO (Total Cost of Ownership) Analytics Service.
Számítások a vehicle.costs tábla alapján, árfolyam-átváltással a system_service segítségével.
"""

import logging
from typing import Optional, Dict, Any, List
from sqlalchemy import select, func, and_
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.models.vehicle import VehicleCost, CostCategory
from app.models import VehicleModelDefinition
from app.models.marketplace.organization import Organization
from app.services.system_service import SystemService

logger = logging.getLogger(__name__)


class TCOAnalytics:
    """
    TCO Analytics osztály 3 fő metódussal:
    1. get_user_tco: Egy adott organization_id költségeinek összesítése
    2. get_vehicle_lifetime_tco: Egy jármű összes tulajdonos költségének összesítése (anonimizálva)
    3. get_global_benchmark: Egy modell (vehicle_model_id) átlagos költségeinek számítása
    """

    def __init__(self):
        self.system_service = SystemService()

    async def get_user_tco(
        self,
        db: AsyncSession,
        organization_id: int,
        currency_target: str = "HUF",
        include_categories: Optional[List[str]] = None,
        start_date: Optional[str] = None,
        end_date: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Egy adott szervezet (organization_id) összes költségének összesítése.
        Átváltja a különböző valutákban lévő költségeket a célvalutára (currency_target).
        
        :param db: Adatbázis munkamenet
        :param organization_id: A szervezet azonosítója
        :param currency_target: Célvaluta (pl. "HUF", "EUR")
        :param include_categories: Szűrés költségkategóriákra (opcionális)
        :param start_date: Kezdő dátum (ISO formátum, opcionális)
        :param end_date: Végdátum (ISO formátum, opcionális)
        :return: Szótár a következőkkel:
            - total_amount: Összesített összeg a célvalutában
            - total_transactions: Tranzakciók száma
            - by_category: Kategóriánkénti bontás
            - currency: A célvaluta
        """
        # Alap lekérdezés: organization_id szűrés
        stmt = select(
            VehicleCost.amount,
            VehicleCost.currency,
            VehicleCost.category_id,
            CostCategory.code,
            CostCategory.name
        ).join(
            CostCategory, VehicleCost.category_id == CostCategory.id
        ).where(
            VehicleCost.organization_id == organization_id
        )
        
        # Dátum szűrés
        if start_date:
            stmt = stmt.where(VehicleCost.date >= start_date)
        if end_date:
            stmt = stmt.where(VehicleCost.date <= end_date)
        
        # Kategória szűrés
        if include_categories:
            stmt = stmt.where(CostCategory.code.in_(include_categories))
        
        result = await db.execute(stmt)
        rows = result.all()
        
        # Árfolyamok lekérése a system_service-ből
        exchange_rates = await self._get_exchange_rates(db, currency_target)
        
        total_amount = 0.0
        category_totals = {}
        
        for row in rows:
            amount = float(row.amount)
            source_currency = row.currency
            
            # Átváltás célvalutára
            converted_amount = await self._convert_currency(
                db, amount, source_currency, currency_target, exchange_rates
            )
            
            total_amount += converted_amount
            
            # Kategória összesítés
            category_code = row.code
            if category_code not in category_totals:
                category_totals[category_code] = {
                    "name": row.name,
                    "total": 0.0,
                    "count": 0
                }
            category_totals[category_code]["total"] += converted_amount
            category_totals[category_code]["count"] += 1
        
        return {
            "organization_id": organization_id,
            "total_amount": round(total_amount, 2),
            "total_transactions": len(rows),
            "currency": currency_target,
            "by_category": category_totals,
            "date_range": {
                "start": start_date,
                "end": end_date
            }
        }

    async def get_vehicle_lifetime_tco(
        self,
        db: AsyncSession,
        vehicle_model_id: int,
        currency_target: str = "HUF",
        anonymize: bool = True,
    ) -> Dict[str, Any]:
        """
        Egy jármű (vehicle_model_id) összes tulajdonos általi költségének összesítése.
        Alapértelmezetten anonimizálva (organization_id-k elrejtve).
        
        :param db: Adatbázis munkamenet
        :param vehicle_model_id: A járműmodell azonosítója
        :param currency_target: Célvaluta (pl. "HUF", "EUR")
        :param anonymize: Ha True, nem tartalmazza az organization_id-kat
        :return: Szótár a következőkkel:
            - vehicle_model_id: A járműmodell azonosítója
            - total_lifetime_cost: Teljes élettartam költség a célvalutában
            - total_owners: Különböző tulajdonosok száma
            - average_cost_per_owner: Tulajdonosonkénti átlag
            - by_owner: Tulajdonosonkénti bontás (ha anonymize=False)
            - currency: A célvaluta
        """
        # Összes költség lekérdezése a járműhöz
        stmt = select(
            VehicleCost.amount,
            VehicleCost.currency,
            VehicleCost.organization_id,
            Organization.name.label("org_name")
        ).outerjoin(
            Organization, VehicleCost.organization_id == Organization.id
        ).where(
            VehicleCost.vehicle_id == vehicle_model_id
        )
        
        result = await db.execute(stmt)
        rows = result.all()
        
        # Árfolyamok lekérése
        exchange_rates = await self._get_exchange_rates(db, currency_target)
        
        total_lifetime_cost = 0.0
        owners = set()
        owner_totals = {}
        
        for row in rows:
            amount = float(row.amount)
            source_currency = row.currency
            
            # Átváltás célvalutára
            converted_amount = await self._convert_currency(
                db, amount, source_currency, currency_target, exchange_rates
            )
            
            total_lifetime_cost += converted_amount
            
            # Tulajdonos adatok
            org_id = row.organization_id
            if org_id:
                owners.add(org_id)
                
                if not anonymize:
                    if org_id not in owner_totals:
                        owner_totals[org_id] = {
                            "name": row.org_name,
                            "total": 0.0,
                            "count": 0
                        }
                    owner_totals[org_id]["total"] += converted_amount
                    owner_totals[org_id]["count"] += 1
        
        total_owners = len(owners)
        average_cost_per_owner = round(total_lifetime_cost / max(total_owners, 1), 2)
        
        result_data = {
            "vehicle_model_id": vehicle_model_id,
            "total_lifetime_cost": round(total_lifetime_cost, 2),
            "total_owners": total_owners,
            "average_cost_per_owner": average_cost_per_owner,
            "currency": currency_target,
            "anonymized": anonymize,
        }
        
        if not anonymize:
            result_data["by_owner"] = owner_totals
        
        return result_data

    async def get_global_benchmark(
        self,
        db: AsyncSession,
        vehicle_model_id: Optional[int] = None,
        make: Optional[str] = None,
        model: Optional[str] = None,
        fuel_type: Optional[str] = None,
        currency_target: str = "HUF",
    ) -> Dict[str, Any]:
        """
        Egy modell (vehicle_model_id) vagy modellcsoport átlagos költségeinek számítása.
        Ha vehicle_model_id nincs megadva, akkor make/model/fuel_type alapján csoportosít.
        
        :param db: Adatbázis munkamenet
        :param vehicle_model_id: Konkrét járműmodell azonosítója (opcionális)
        :param make: Gyártó (opcionális)
        :param model: Modell (opcionális)
        :param fuel_type: Üzemanyag típus (opcionális)
        :param currency_target: Célvaluta (pl. "HUF", "EUR")
        :return: Szótár a következőkkel:
            - benchmark_type: "specific_model" vagy "grouped"
            - vehicle_count: Járművek száma a mintában
            - total_cost_sum: Összes költség a célvalutában
            - average_cost_per_vehicle: Járművenkénti átlag
            - average_cost_per_km: Kilométerenkénti átlag (ha elérhető odometer adat)
            - by_category: Kategóriánkénti átlagok
            - currency: A célvaluta
        """
        # Alap lekérdezés: vehicle és cost összekapcsolása
        stmt = select(
            VehicleCost.amount,
            VehicleCost.currency,
            VehicleCost.vehicle_id,
            VehicleCost.odometer,
            CostCategory.code,
            VehicleModelDefinition.make,
            VehicleModelDefinition.model,
            VehicleModelDefinition.fuel_type
        ).join(
            VehicleModelDefinition, VehicleCost.vehicle_id == VehicleModelDefinition.id
        ).join(
            CostCategory, VehicleCost.category_id == CostCategory.id
        )
        
        # Szűrés
        if vehicle_model_id:
            stmt = stmt.where(VehicleCost.vehicle_id == vehicle_model_id)
            benchmark_type = "specific_model"
        else:
            conditions = []
            if make:
                conditions.append(VehicleModelDefinition.make == make)
            if model:
                conditions.append(VehicleModelDefinition.model == model)
            if fuel_type:
                conditions.append(VehicleModelDefinition.fuel_type == fuel_type)
            
            if conditions:
                stmt = stmt.where(and_(*conditions))
            
            benchmark_type = "grouped"
        
        result = await db.execute(stmt)
        rows = result.all()
        
        if not rows:
            return {
                "benchmark_type": benchmark_type,
                "vehicle_count": 0,
                "total_cost_sum": 0.0,
                "average_cost_per_vehicle": 0.0,
                "average_cost_per_km": None,
                "by_category": {},
                "currency": currency_target,
                "message": "No data found for the specified criteria"
            }
        
        # Árfolyamok
        exchange_rates = await self._get_exchange_rates(db, currency_target)
        
        total_cost_sum = 0.0
        total_odometer_sum = 0
        vehicle_ids = set()
        category_totals = {}
        category_counts = {}
        
        for row in rows:
            amount = float(row.amount)
            source_currency = row.currency
            
            # Átváltás
            converted_amount = await self._convert_currency(
                db, amount, source_currency, currency_target, exchange_rates
            )
            
            total_cost_sum += converted_amount
            vehicle_ids.add(row.vehicle_id)
            
            # Odometer összegzés (ha van)
            if row.odometer:
                total_odometer_sum += row.odometer
            
            # Kategória összesítés
            category_code = row.code
            if category_code not in category_totals:
                category_totals[category_code] = 0.0
                category_counts[category_code] = 0
            
            category_totals[category_code] += converted_amount
            category_counts[category_code] += 1
        
        vehicle_count = len(vehicle_ids)
        average_cost_per_vehicle = round(total_cost_sum / vehicle_count, 2)
        
        # Kilométerenkénti átlag számítása
        average_cost_per_km = None
        if total_odometer_sum > 0:
            average_cost_per_km = round(total_cost_sum / total_odometer_sum, 4)
        
        # Kategóriánkénti átlagok
        category_averages = {}
        for code, total in category_totals.items():
            count = category_counts[code]
            category_averages[code] = {
                "total": round(total, 2),
                "count": count,
                "average": round(total / count, 2)
            }
        
        return {
            "benchmark_type": benchmark_type,
            "vehicle_count": vehicle_count,
            "total_cost_sum": round(total_cost_sum, 2),
            "average_cost_per_vehicle": average_cost_per_vehicle,
            "average_cost_per_km": average_cost_per_km,
            "by_category": category_averages,
            "currency": currency_target,
            "criteria": {
                "vehicle_model_id": vehicle_model_id,
                "make": make,
                "model": model,
                "fuel_type": fuel_type
            }
        }

    async def _get_exchange_rates(
        self,
        db: AsyncSession,
        target_currency: str
    ) -> Dict[str, float]:
        """
        Árfolyamok lekérése a system_service-ből.
        A rendszerparaméterekben az "exchange_rates" kulcs alatt tároljuk.
        
        :param db: Adatbázis munkamenet
        :param target_currency: Célvaluta
        :return: Szótár forrásvaluta -> célvaluta árfolyammal
        """
        exchange_rates = await self.system_service.get_scoped_parameter(
            db, 
            key="exchange_rates",
            default={}
        )
        
        # Ha nincs adat, alapértelmezett árfolyamok
        if not exchange_rates:
            logger.warning("No exchange rates found in system parameters, using defaults")
            # Alapértelmezett árfolyamok (1 EUR = 400 HUF, 1 USD = 350 HUF stb.)
            exchange_rates = {
                "EUR": {"HUF": 400.0, "EUR": 1.0, "USD": 1.1},
                "USD": {"HUF": 350.0, "EUR": 0.9, "USD": 1.0},
                "HUF": {"HUF": 1.0, "EUR": 0.0025, "USD": 0.0029},
                "GBP": {"HUF": 460.0, "EUR": 1.15, "USD": 1.26},
            }
        
        # Ellenőrizzük, hogy a célvaluta szerepel-e az árfolyamokban
        if target_currency not in exchange_rates.get("EUR", {}):
            logger.warning(f"Target currency {target_currency} not found in exchange rates, using 1:1 conversion")
        
        return exchange_rates

    async def _convert_currency(
        self,
        db: AsyncSession,
        amount: float,
        source_currency: str,
        target_currency: str,
        exchange_rates: Dict[str, Any]
    ) -> float:
        """
        Pénznem átváltása a megadott árfolyamok alapján.
        
        :param amount: Összeg a forrásvalutában
        :param source_currency: Forrásvaluta (pl. "EUR")
        :param target_currency: Célvaluta (pl. "HUF")
        :param exchange_rates: Árfolyam szótár
        :return: Átváltott összeg a célvalutában
        """
        if source_currency == target_currency:
            return amount
        
        # Keresés az árfolyamokban
        try:
            # Próbáljuk meg a forrásvaluta -> célvaluta árfolyamot
            if source_currency in exchange_rates:
                rates = exchange_rates[source_currency]
                if target_currency in rates:
                    rate = rates[target_currency]
                    return amount * rate
            
            # Ha nem találjuk, próbáljuk meg fordítva (inverz)
            if target_currency in exchange_rates:
                rates = exchange_rates[target_currency]
                if source_currency in rates:
                    rate = 1.0 / rates[source_currency]
                    return amount * rate
            
            # Ha még mindig nem találjuk, használjunk EUR-t közvetítőként
            if "EUR" in exchange_rates:
                eur_rates = exchange_rates["EUR"]
                if source_currency in eur_rates and target_currency in eur_rates:
                    # Forrás -> EUR -> Cél
                    to_eur = amount / eur_rates[source_currency]
                    return to_eur * eur_rates[target_currency]
        
        except (KeyError, ZeroDivisionError, TypeError) as e:
            logger.error(f"Currency conversion error: {e}, using 1:1 conversion")
        
        # Visszaesés: 1:1 árfolyam
        logger.warning(f"Could not convert {source_currency} to {target_currency}, using 1:1 conversion")
        return amount