# /opt/docker/dev/service_finder/backend/app/services/asset_service.py
from __future__ import annotations
import logging
import uuid
from typing import List, Optional, Dict, Any, TYPE_CHECKING
from datetime import datetime
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, and_, distinct
from sqlalchemy.orm import selectinload
from fastapi import HTTPException

from app.models import Asset, AssetAssignment, AssetTelemetry, AssetFinancials, VehicleModelDefinition
from app.models.identity import User
from app.models.vehicle.history import LogSeverity
from app.schemas.asset import AssetCreate
from app.services.config_service import config
from app.services.gamification_service import GamificationService
from app.services.security_service import security_service

if TYPE_CHECKING:
    from .identity import User, Person
    from .organization import Organization
    from .vehicle_definitions import VehicleModelDefinition

logger = logging.getLogger(__name__)

class AssetService:
    """
    Asset Service 2.0 - A Járművek Életciklus-menedzsere.
    Kezeli a regisztrációt, a tulajdonosváltást és a flotta-korlátokat.
    """

    @staticmethod
    async def create_or_claim_vehicle(
        db: AsyncSession,
        user_id: int,
        org_id: int,
        asset_data: AssetCreate,
        draft: bool = False
    ):
        """
        Intelligens Jármű Rögzítés - Thick Digital Twin támogatással:
        Ha új: létrehozza a teljes technikai adatokkal.
        Ha már létezik: Transzfer folyamatot indít.
        Automatikus státusz meghatározás az adatkomplettség alapján.
        Catalog Snapshot Sync: Ha catalog_id van, betölti a hiányzó technikai adatokat.
        """
        try:
            # Clean input data
            vin_clean = asset_data.vin.strip().upper() if asset_data.vin else None
            license_plate_clean = asset_data.license_plate.strip().upper()
            
            # Use organization_id from asset_data if provided, otherwise use the passed org_id
            target_org_id = asset_data.organization_id or org_id
            
            # 1. ADMIN LIMIT ELLENŐRZÉS (csak aktív járművek számítanak)
            user_stmt = select(User).where(User.id == user_id)
            user = (await db.execute(user_stmt)).scalar_one()
            
            # Get vehicle limit using the new function that checks both user AND organization limits
            # Returns the HIGHER value of user-specific and organization-specific limits
            allowed_limit = await AssetService.get_user_vehicle_limit(db, user_id, target_org_id)

            # Csak aktív járművek számítanak a limitbe (draft-ok nem)
            count_stmt = select(func.count(Asset.id)).where(
                Asset.current_organization_id == target_org_id,
                Asset.status == "active"
            )
            current_count = (await db.execute(count_stmt)).scalar()

            # Determine status based on data completeness (use Pydantic validator's logic)
            # Check the 5 core fields: license_plate, brand, model, vehicle_class, fuel_type
            core_fields_complete = all([
                asset_data.license_plate and asset_data.license_plate.strip(),
                asset_data.brand and asset_data.brand.strip(),
                asset_data.model and asset_data.model.strip(),
                asset_data.vehicle_class and asset_data.vehicle_class.strip(),
                asset_data.fuel_type and asset_data.fuel_type.strip()
            ])
            
            # Determine final status
            if draft:
                status = "draft"
            elif not core_fields_complete:
                status = "draft"
            else:
                status = "active"

            if current_count >= allowed_limit and status == "active":
                raise ValueError(f"Limit túllépés! A csomagod {allowed_limit} aktív autót engedélyez.")

            # 2. LÉTEZIK-E MÁR A JÁRMŰ? (csak ha van VIN)
            existing_asset = None
            if vin_clean:
                stmt = select(Asset).where(Asset.vin == vin_clean)
                existing_asset = (await db.execute(stmt)).scalar_one_or_none()

            if existing_asset:
                # HA MÁR A JELENLEGI SZERVEZETNÉL VAN
                if existing_asset.current_organization_id == target_org_id:
                    raise ValueError("Ez a jármű már a te garázsodban van.")
                
                # TRANSZFER FOLYAMAT INDÍTÁSA
                return await AssetService.initiate_ownership_transfer(
                    db, existing_asset, user_id, target_org_id, license_plate_clean or ""
                )

            # 3. CATALOG SNAPSHOT SYNC - Ha catalog_id van, betöltjük a hiányzó technikai adatokat
            catalog_data = {}
            if asset_data.catalog_id:
                catalog_stmt = select(VehicleModelDefinition).where(
                    VehicleModelDefinition.id == asset_data.catalog_id
                )
                catalog = (await db.execute(catalog_stmt)).scalar_one_or_none()
                if catalog:
                    # Map catalog fields to asset fields (only if not already provided by user)
                    catalog_data = {
                        'brand': catalog.make if not asset_data.brand else None,
                        'model': catalog.marketing_name if not asset_data.model else None,
                        'vehicle_class': catalog.vehicle_class if not asset_data.vehicle_class else None,
                        'fuel_type': catalog.fuel_type if not asset_data.fuel_type else None,
                        'power_kw': catalog.power_kw if not asset_data.power_kw else None,
                        'engine_capacity': catalog.engine_capacity if not asset_data.engine_capacity else None,
                        'euro_classification': catalog.euro_class if not asset_data.euro_classification else None,
                        'body_type': catalog.body_type if not asset_data.trim_level else None,
                    }
                    # Remove None values
                    catalog_data = {k: v for k, v in catalog_data.items() if v is not None}

            # 4. ÚJ JÁRMŰ LÉTREHOZÁSA - Thick Digital Twin
            # Először összeállítjuk az összes adatot (user input + catalog snapshot)
            # Get default vehicle class from config if not provided
            default_vehicle_class = await config.get_setting(db, "DEFAULT_VEHICLE_CLASS", default="car")
            
            asset_fields = {
                'vin': vin_clean,
                'license_plate': license_plate_clean,
                'catalog_id': asset_data.catalog_id,
                'current_organization_id': target_org_id,
                'owner_person_id': user.person_id,
                'owner_org_id': asset_data.owner_org_id or target_org_id,
                'operator_org_id': asset_data.operator_org_id,
                'status': status,
                'individual_equipment': asset_data.individual_equipment or {},
                'created_at': datetime.utcnow(),
                
                # Classification
                'brand': asset_data.brand or catalog_data.get('brand'),
                'model': asset_data.model or catalog_data.get('model'),
                'vehicle_class': asset_data.vehicle_class or catalog_data.get('vehicle_class') or default_vehicle_class,
                'trim_level': asset_data.trim_level,
                
                # Technical Specs
                'fuel_type': asset_data.fuel_type or catalog_data.get('fuel_type'),
                'engine_capacity': asset_data.engine_capacity or catalog_data.get('engine_capacity'),
                'power_kw': asset_data.power_kw or catalog_data.get('power_kw'),
                'torque_nm': asset_data.torque_nm,
                'cylinder_layout': asset_data.cylinder_layout,
                'transmission_type': asset_data.transmission_type,
                'drive_type': asset_data.drive_type,
                'euro_classification': asset_data.euro_classification or catalog_data.get('euro_classification'),
                
                # Physical Dimensions
                'curb_weight': asset_data.curb_weight,
                'max_weight': asset_data.max_weight,
                'cargo_volume_x': asset_data.cargo_volume_x,
                'cargo_volume_y': asset_data.cargo_volume_y,
                'door_count': asset_data.door_count,
                'seat_count': asset_data.seat_count,
                
                # Equipment
                'roof_type': asset_data.roof_type,
                'audio_system_type': asset_data.audio_system_type,
                
                # Timeline
                'year_of_manufacture': asset_data.year_of_manufacture,
                'first_registration_date': asset_data.first_registration_date,
            }
            
            # Remove None values from the dictionary
            asset_fields = {k: v for k, v in asset_fields.items() if v is not None}
            
            new_asset = Asset(**asset_fields)
            db.add(new_asset)
            await db.flush()

            # Digitális Iker Alapmodulok
            db.add(AssetAssignment(asset_id=new_asset.id, organization_id=target_org_id, status="active"))
            db.add(AssetTelemetry(asset_id=new_asset.id))
            db.add(AssetFinancials(
                asset_id=new_asset.id,
                purchase_price_net=0.0,
                purchase_price_gross=0.0,
                financing_type="unknown"
            ))

            # Gamification
            reward = await config.get_setting(db, "xp_reward_asset_register", default=250)
            await GamificationService.award_points(db, user_id, int(reward), "NEW_ASSET_REG")
            
            # Check if this is user's first vehicle and award "First Car" badge
            await AssetService._award_first_car_badge(db, user_id, target_org_id)

            await db.commit()
            return new_asset

        except Exception as e:
            await db.rollback()
            logger.error(f"Asset Creation Error: {e}")
            raise e

    @staticmethod
    async def initiate_ownership_transfer(db: AsyncSession, asset: Asset, user_id: int, org_id: int, new_plate: str):
        """
        Adásvétel kezelése: Az autót 'Transfer Pending' állapotba teszi.
        """
        # Admin paraméter: Automatikus transzfer engedélyezése?
        auto_transfer = await config.get_setting(db, "asset_auto_transfer_enabled", default=False)
        
        # Logoljuk a kísérletet a biztonsági szolgálatnál (Sentinel)
        await security_service.log_event(
            db, user_id=user_id, action="VEHICLE_CLAIM_INITIATED",
            severity=LogSeverity.warning, target_type="Asset", target_id=str(asset.id),
            new_data={"vin": asset.vin, "new_org": org_id}
        )

        if auto_transfer:
            # Csak akkor, ha a régi tulajdonos 'sold' állapotba tette
            if asset.status == "sold":
                return await AssetService.execute_final_transfer(db, asset, org_id, new_plate, user_id)
        
        # Függőben lévő állapot: Dokumentum feltöltésre vár
        asset.status = "transfer_pending"
        asset.temp_claim_org_id = org_id # Átmeneti tároló a validálásig
        
        await db.commit()
        # Itt egy speciális hibaüzenetet dobunk, amit a Frontend tud kezelni (Dokumentum feltöltő ablak)
        raise HTTPException(
            status_code=202, 
            detail="A jármű már szerepel a rendszerben. Kérjük, töltsd fel az adásvételi szerződést a tulajdonjog igazolásához."
        )

    @staticmethod
    async def execute_final_transfer(db: AsyncSession, asset: Asset, new_org_id: int, new_plate: str, user_id: int = None):
        """ A tulajdonjog tényleges átírása az adatbázisban. """
        # 1. Régi hozzárendelés lezárása
        await db.execute(
            update(AssetAssignment)
            .where(and_(AssetAssignment.asset_id == asset.id, AssetAssignment.status == "active"))
            .values(status="archived", end_date=datetime.now())
        )

        # 2. Új hozzárendelés és adatok frissítése
        asset.current_organization_id = new_org_id
        asset.license_plate = new_plate.upper()
        asset.status = "active"
        asset.is_verified = False # Az új tulajdonos papírjait is ellenőrizni kell!
        
        # 3. Update ownership fields if user_id is provided
        if user_id is not None:
            from app.models.identity import User
            user_stmt = select(User).where(User.id == user_id)
            user = (await db.execute(user_stmt)).scalar_one_or_none()
            if user and user.person_id:
                asset.owner_person_id = user.person_id
                asset.owner_org_id = new_org_id
            else:
                logger.warning(f"User {user_id} has no person_id, cannot set owner_person_id")
        else:
            logger.warning("execute_final_transfer called without user_id, ownership fields not updated")
        
        db.add(AssetAssignment(asset_id=asset.id, organization_id=new_org_id, status="active"))
        
        await db.commit()
        return asset

    # --- CATALOG METHODS ---
    @staticmethod
    async def get_makes(db: AsyncSession) -> List[str]:
        """Get all distinct makes from vehicle model definitions."""
        stmt = select(distinct(VehicleModelDefinition.make)).order_by(VehicleModelDefinition.make)
        result = await db.execute(stmt)
        makes = result.scalars().all()
        return [make for make in makes if make]  # Filter out None/empty

    @staticmethod
    async def get_models(db: AsyncSession, make: str, vehicle_class: str = None) -> List[str]:
        """Get all distinct models for a given make, optionally filtered by vehicle_class."""
        stmt = select(distinct(VehicleModelDefinition.marketing_name)).where(
            VehicleModelDefinition.make == make
        )
        if vehicle_class:
            stmt = stmt.where(VehicleModelDefinition.vehicle_class == vehicle_class)
        stmt = stmt.order_by(VehicleModelDefinition.marketing_name)
        result = await db.execute(stmt)
        models = result.scalars().all()
        return [model for model in models if model]

    @staticmethod
    async def get_generations(db: AsyncSession, make: str, model: str) -> List[str]:
        """Get all distinct generations/variants for a given make and model.
        For now, we'll use engine_code as generation placeholder."""
        stmt = select(distinct(VehicleModelDefinition.engine_code)).where(
            VehicleModelDefinition.make == make,
            VehicleModelDefinition.marketing_name == model,
            VehicleModelDefinition.engine_code.isnot(None)
        ).order_by(VehicleModelDefinition.engine_code)
        result = await db.execute(stmt)
        generations = result.scalars().all()
        return [gen for gen in generations if gen]

    @staticmethod
    async def get_engines(db: AsyncSession, make: str, model: str, gen: str) -> List[VehicleModelDefinition]:
        """Get all engine variants for a given make, model, and generation."""
        stmt = select(VehicleModelDefinition).where(
            VehicleModelDefinition.make == make,
            VehicleModelDefinition.marketing_name == model,
            VehicleModelDefinition.engine_code == gen
        ).order_by(VehicleModelDefinition.id)
        result = await db.execute(stmt)
        engines = result.scalars().all()
        return engines

    @staticmethod
    async def get_user_vehicle_limit(db: AsyncSession, user_id: int, org_id: int) -> int:
        """
        Get the vehicle limit for a user, checking both user-specific AND organization limits.
        Returns the HIGHER value of the two as per requirements.
        
        Args:
            db: AsyncSession
            user_id: User ID
            org_id: Organization ID
            
        Returns:
            Maximum allowed vehicles (higher of user limit and organization limit)
        """
        from app.models.identity import User
        from app.services.config_service import config
        
        try:
            # Get user info
            user_stmt = select(User).where(User.id == user_id)
            user = (await db.execute(user_stmt)).scalar_one()
            
            # Get global vehicle limits configuration
            limits = await config.get_setting(db, "VEHICLE_LIMIT")
            if limits is None:
                logger.error(f"VEHICLE_LIMIT configuration not found in database for user {user_id}")
                # Fallback to very high limit instead of restricting users
                limits = {"admin": 9999, "superadmin": 9999, "user": 100, "free": 100, "premium": 100, "vip": 100, "service_pro": 100}
            
            user_role = user.role.value if hasattr(user.role, 'value') else str(user.role)
            subscription_plan = user.subscription_plan or "free"
            
            # Get user-specific limit (based on role or subscription plan)
            user_limit = limits.get(user_role)
            if user_limit is None:
                user_limit = limits.get(subscription_plan.lower(), 100)
            
            # Get organization-specific limit (if configured)
            org_limit = None
            try:
                org_limits = await config.get_setting(db, "VEHICLE_LIMIT", org_id=org_id)
                if org_limits and isinstance(org_limits, dict):
                    # Organization might have different limit structure
                    # Try to get limit for user's role or use a default org limit
                    org_limit = org_limits.get(user_role) or org_limits.get(subscription_plan.lower())
                    if org_limit is None and "default" in org_limits:
                        org_limit = org_limits["default"]
            except Exception as e:
                logger.debug(f"No organization-specific VEHICLE_LIMIT found for org {org_id}: {e}")
                org_limit = None
            
            # Log the calculated limit for debugging
            final_limit = user_limit
            if org_limit is not None:
                final_limit = max(user_limit, org_limit)
                logger.info(f"Calculated limit for user {user_id} (role: {user_role}, plan: {subscription_plan}): user_limit={user_limit}, org_limit={org_limit}, final={final_limit}")
            else:
                logger.info(f"Calculated limit for user {user_id} (role: {user_role}, plan: {subscription_plan}): user_limit={user_limit}, org_limit=None, final={final_limit}")
            
            return final_limit
            
        except Exception as e:
            logger.error(f"Error getting vehicle limit for user {user_id}, org {org_id}: {e}")
            # Fallback to a reasonable default
            return 100

    @staticmethod
    async def _award_first_car_badge(db: AsyncSession, user_id: int, org_id: int):
        """
        Award 'First Car' badge to user if this is their first vehicle.
        
        Checks if the user already has any vehicles in the organization.
        If not, awards the 'First Car' badge.
        """
        try:
            from sqlalchemy import select, func
            from app.models.gamification import Badge, UserBadge
            
            # Check if user already has vehicles in this organization
            from app.models.vehicle import Asset
            vehicle_count_stmt = select(func.count(Asset.id)).where(
                Asset.current_organization_id == org_id,
                Asset.status == "active"
            )
            vehicle_count = (await db.execute(vehicle_count_stmt)).scalar()
            
            # If this is the first vehicle (count should be 1 after the new one is added)
            if vehicle_count == 1:
                # Get or create the "First Car" badge
                badge_stmt = select(Badge).where(Badge.name == "First Car")
                badge_result = await db.execute(badge_stmt)
                badge = badge_result.scalar_one_or_none()
                
                if not badge:
                    # Create the badge if it doesn't exist
                    badge = Badge(
                        name="First Car",
                        description="Awarded for adding your first vehicle to the fleet",
                        icon_url="/badges/first-car.svg"
                    )
                    db.add(badge)
                    await db.flush()
                
                # Check if user already has this badge
                user_badge_stmt = select(UserBadge).where(
                    UserBadge.user_id == user_id,
                    UserBadge.badge_id == badge.id
                )
                user_badge_result = await db.execute(user_badge_stmt)
                existing_user_badge = user_badge_result.scalar_one_or_none()
                
                if not existing_user_badge:
                    # Award the badge to the user
                    user_badge = UserBadge(
                        user_id=user_id,
                        badge_id=badge.id,
                        awarded_at=datetime.utcnow()
                    )
                    db.add(user_badge)
                    await db.flush()
                    
                    logger = logging.getLogger(__name__)
                    logger.info(f"Awarded 'First Car' badge to user {user_id}")
        
        except Exception as e:
            logger = logging.getLogger(__name__)
            logger.error(f"Error awarding first car badge: {e}")
            # Don't raise the error - badge awarding shouldn't break vehicle creation