"""
Token Manager Service
Handles AI token balance, deductions, and transaction logging
"""

import json
from datetime import datetime
from typing import Optional, Dict, Any
from database import get_db_connection
import logging

logger = logging.getLogger(__name__)

# Input tokens are charged at 50%, output tokens at 100%.
# This reflects that output tokens represent the AI's actual work (generation),
# while input tokens are often large context/notes the user didn't create.
INPUT_TOKEN_WEIGHT = 0.5


class TokenManager:
    """Centralized AI token management service"""
    
    @staticmethod
    def get_user_balance(user_id: int) ->Dict[str, Any]:
        """
        Get current user token balance and subscription info
        
        Returns:
            {
                'balance': float,
                'purchased': float,
                'consumed': float,
                'subscriptions': list
            }
        """
        conn = None
        try:
            conn = get_db_connection()
            cursor = conn.cursor(dictionary=True)
            
            try:
                # Try to query token columns (may not exist in all databases)
                cursor.execute("""
                    SELECT ai_tokens_balance, ai_tokens_purchased, ai_tokens_consumed
                    FROM user WHERE id = %s
                """, (user_id,))
                
                user = cursor.fetchone()
                
                if not user:
                    return {'balance': 0, 'purchased': 0, 'consumed': 0, 'subscriptions': []}
                
                balance = float(user.get('ai_tokens_balance') or 0)
                purchased = float(user.get('ai_tokens_purchased') or 0)
                consumed = float(user.get('ai_tokens_consumed') or 0)
                
            except Exception as column_error:
                # Columns don't exist - return defaults
                logger.warning(f"Token columns not found in database: {column_error}")
                return {'balance': 0, 'purchased': 0, 'consumed': 0, 'subscriptions': []}
            
            # Get AI token subscriptions (skip if tables don't exist)
            try:
                cursor.execute("""
                    SELECT us.*, sp.plan_name, sp.benefits
                    FROM user_subscriptions us
                    JOIN subscription_plans sp ON us.plan_id = sp.id
                    JOIN subscription_types st ON us.subscription_type_id = st.id
                    WHERE us.user_id = %s 
                    AND st.type_key = 'ai_tokens'
                    AND us.status IN ('active', 'trial')
                """, (user_id,))
                
                subscriptions = cursor.fetchall()
                
                for sub in subscriptions:
                    if sub.get('benefits'):
                        sub['benefits'] = json.loads(sub['benefits']) if isinstance(sub['benefits'], str) else sub['benefits']
                    if sub.get('usage_data'):
                        sub['usage_data'] = json.loads(sub['usage_data']) if isinstance(sub['usage_data'], str) else sub['usage_data']
            except:
                subscriptions = []
            
            return {
                'balance': balance,
                'purchased': purchased,
                'consumed': consumed,
                'subscriptions': subscriptions
            }
            
        except Exception as e:
            logger.error(f"Error getting user balance: {e}")
            return {'balance': 0, 'purchased': 0, 'consumed': 0, 'subscriptions': []}
        finally:
            if conn:
                conn.close()
    
    @staticmethod
    def check_balance(user_id: int, required_tokens: float) -> Dict[str, Any]:
        """
        Check if user has sufficient tokens
        
        Returns:
            {
                'has_sufficient': bool,
                'balance': float,
                'required': float,
                'shortfall': float (if insufficient)
            }
        """
        balance_info = TokenManager.get_user_balance(user_id)
        balance = balance_info['balance']
        
        if balance >= required_tokens:
            return {
                'has_sufficient': True,
                'balance': balance,
                'required': required_tokens
            }
        else:
            return {
                'has_sufficient': False,
                'balance': balance,
                'required': required_tokens,
                'shortfall': required_tokens - balance
            }
    
    @staticmethod
    def deduct_tokens(
        user_id: int,
        amount: float,
        endpoint: str,
        ai_model: Optional[str] = None,
        provider: Optional[str] = None,
        session_id: Optional[int] = None,
        message_id: Optional[int] = None,
        request_tokens: int = 0,
        response_tokens: int = 0,
        metadata: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """
        Deduct tokens from user balance and log transaction
        
        Returns:
            {
                'success': bool,
                'balance_before': float,
                'balance_after': float,
                'transaction_id': int
            }
        """
        conn = None
        try:
            conn = get_db_connection()
            cursor = conn.cursor(dictionary=True)
            
            # Get current balance
            cursor.execute("""
                SELECT ai_tokens_balance, ai_tokens_consumed 
                FROM user WHERE id = %s FOR UPDATE
            """, (user_id,))
            
            user = cursor.fetchone()
            
            if not user:
                return {'success': False, 'message': 'User not found'}
            
            balance_before = float(user['ai_tokens_balance'] or 0)
            
            if balance_before < amount:
                conn.rollback()
                return {
                    'success': False,
                    'message': 'Insufficient tokens',
                    'balance': balance_before,
                    'required': amount
                }
            
            balance_after = balance_before - amount
            
            # Update user balance
            cursor.execute("""
                UPDATE user
                SET ai_tokens_balance = %s,
                    ai_tokens_consumed = ai_tokens_consumed + %s
                WHERE id = %s
            """, (balance_after, amount, user_id))
            
            # Get model pricing if available
            cost_per_1k = 0.0
            if ai_model:
                cursor.execute("""
                    SELECT cost_per_1k_input_tokens, cost_per_1k_output_tokens
                    FROM ai_models WHERE name = %s
                """, (ai_model,))
                
                model_pricing = cursor.fetchone()
                if model_pricing:
                    # Calculate weighted average cost
                    total_tokens = request_tokens + response_tokens
                    if total_tokens > 0:
                        cost_per_1k = (
                            (request_tokens * float(model_pricing['cost_per_1k_input_tokens']) +
                             response_tokens * float(model_pricing['cost_per_1k_output_tokens']))
                            / total_tokens
                        )
            
            # Log transaction
            cursor.execute("""
                INSERT INTO ai_token_transactions (
                    user_id, transaction_type, amount, balance_before, balance_after,
                    endpoint, ai_model, provider, session_id, message_id,
                    request_tokens, response_tokens, total_tokens,
                    cost_per_1k_tokens, metadata
                ) VALUES (
                    %s, 'consumption', %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s
                )
            """, (
                user_id, -amount, balance_before, balance_after,
                endpoint, ai_model, provider, session_id, message_id,
                request_tokens, response_tokens, request_tokens + response_tokens,
                cost_per_1k, json.dumps(metadata) if metadata else None
            ))
            
            transaction_id = cursor.lastrowid
            
            conn.commit()
            
            logger.info(f"Deducted {amount} tokens from user {user_id}, new balance: {balance_after}")
            
            return {
                'success': True,
                'balance_before': balance_before,
                'balance_after': balance_after,
                'transaction_id': transaction_id
            }
            
        except Exception as e:
            if conn:
                conn.rollback()
            logger.error(f"Error deducting tokens: {e}")
            return {'success': False, 'message': str(e)}
        finally:
            if conn:
                conn.close()
    
    @staticmethod
    def add_tokens(
        user_id: int,
        amount: float,
        transaction_type: str = 'purchase',
        purchase_package_id: Optional[int] = None,
        payment_id: Optional[str] = None,
        payment_method: Optional[str] = None,
        payment_amount: Optional[float] = None,
        notes: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Add tokens to user balance (purchase, bonus, admin credit, etc.)
        
        Returns:
            {'success': bool, 'balance_after': float, 'transaction_id': int}
        """
        conn = None
        try:
            conn = get_db_connection()
            cursor = conn.cursor(dictionary=True)
            
            # Get current balance
            cursor.execute("""
                SELECT ai_tokens_balance, ai_tokens_purchased 
                FROM user WHERE id = %s FOR UPDATE
            """, (user_id,))
            
            user = cursor.fetchone()
            
            if not user:
                return {'success': False, 'message': 'User not found'}
            
            balance_before = float(user['ai_tokens_balance'] or 0)
            balance_after = balance_before + amount
            
            # Update user balance
            update_query = """
                UPDATE user
                SET ai_tokens_balance = %s
            """
            params = [balance_after]
            
            # Update purchased total if this is a purchase
            if transaction_type == 'purchase':
                update_query += ", ai_tokens_purchased = ai_tokens_purchased + %s"
                params.append(amount)
            
            update_query += " WHERE id = %s"
            params.append(user_id)
            
            cursor.execute(update_query, params)
            
            # Log transaction
            cursor.execute("""
                INSERT INTO ai_token_transactions (
                    user_id, transaction_type, amount, balance_before, balance_after,
                    purchase_package_id, payment_id, payment_method, payment_amount, notes
                ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
            """, (
                user_id, transaction_type, amount, balance_before, balance_after,
                purchase_package_id, payment_id, payment_method, payment_amount, notes
            ))
            
            transaction_id = cursor.lastrowid
            
            conn.commit()
            
            logger.info(f"Added {amount} tokens to user {user_id} ({transaction_type}), new balance: {balance_after}")
            
            return {
                'success': True,
                'balance_before': balance_before,
                'balance_after': balance_after,
                'transaction_id': transaction_id
            }
            
        except Exception as e:
            if conn:
                conn.rollback()
            logger.error(f"Error adding tokens: {e}")
            return {'success': False, 'message': str(e)}
        finally:
            if conn:
                conn.close()
    
    @staticmethod
    def calculate_token_cost(
        input_tokens: int,
        output_tokens: int,
        model_name: str
    ) -> float:
        """
        Calculate token cost based on model pricing from ai_models table.
        Charges: input_tokens * 50% + output_tokens * 100%
        
        Returns:
            Total cost in tokens
        """
        conn = None
        try:
            conn = get_db_connection()
            cursor = conn.cursor(dictionary=True)
            
            # Query by both 'name' and 'model' columns to find the model
            cursor.execute("""
                SELECT cost_per_1k_input_tokens, cost_per_1k_output_tokens
                FROM ai_models 
                WHERE name = %s OR model = %s
                LIMIT 1
            """, (model_name, model_name))
            
            model = cursor.fetchone()
            
            if not model:
                # Fallback: if model not found, use simplified calculation
                # Default: assume $0.0001 per 1k input tokens (at 50%), $0.0001 per 1k output (at 100%)
                logger.warning(f"Model '{model_name}' not found in ai_models table, using fallback pricing")
                input_cost = (input_tokens * INPUT_TOKEN_WEIGHT * 0.0001) / 1000
                output_cost = (output_tokens * 0.0001) / 1000
                total_cost = input_cost + output_cost
                logger.info(f"Fallback cost: input={input_tokens}*{INPUT_TOKEN_WEIGHT} -> {input_cost:.4f}, output={output_tokens} -> {output_cost:.4f}, total={total_cost:.4f}")
                return total_cost
            
            # Calculate cost using database pricing
            input_cost = input_tokens * INPUT_TOKEN_WEIGHT * float(model['cost_per_1k_input_tokens']) / 1000
            output_cost = output_tokens * float(model['cost_per_1k_output_tokens']) / 1000
            total_cost = input_cost + output_cost
            
            logger.info(f"Model '{model_name}' pricing: input_rate={model['cost_per_1k_input_tokens']}/1k, output_rate={model['cost_per_1k_output_tokens']}/1k, input_cost={input_cost:.4f}, output_cost={output_cost:.4f}, total={total_cost:.4f}")
            
            return total_cost
            
        except Exception as e:
            logger.error(f"Error calculating token cost for model '{model_name}': {e}")
            # Safe fallback
            fallback_cost = (input_tokens * INPUT_TOKEN_WEIGHT * 0.0001 + output_tokens * 0.0001) / 1000
            logger.info(f"Using exception fallback cost: {fallback_cost:.4f}")
            return fallback_cost
        finally:
            if conn:
                conn.close()
    
    @staticmethod
    def get_transaction_history(
        user_id: int,
        limit: int = 50,
        offset: int = 0,
        transaction_type: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Get user's token transaction history
        
        Returns:
            {
                'transactions': list,
                'total_count': int
            }
        """
        conn = None
        try:
            conn = get_db_connection()
            cursor = conn.cursor(dictionary=True)
            
            # Build query
            where_clause = "WHERE user_id = %s"
            params = [user_id]
            
            if transaction_type:
                where_clause += " AND transaction_type = %s"
                params.append(transaction_type)
            
            # Get total count
            cursor.execute(f"""
                SELECT COUNT(*) as total
                FROM ai_token_transactions
                {where_clause}
            """, params)
            
            total_count = cursor.fetchone()['total']
            
            # Get transactions
            cursor.execute(f"""
                SELECT *
                FROM ai_token_transactions
                {where_clause}
                ORDER BY created_at DESC
                LIMIT %s OFFSET %s
            """, params + [limit, offset])
            
            transactions = cursor.fetchall()
            
            # Parse JSON fields
            for txn in transactions:
                if txn.get('metadata'):
                    txn['metadata'] = json.loads(txn['metadata']) if isinstance(txn['metadata'], str) else txn['metadata']
            
            return {
                'transactions': transactions,
                'total_count': total_count
            }
            
        except Exception as e:
            logger.error(f"Error getting transaction history: {e}")
            return {'transactions': [], 'total_count': 0}
        finally:
            if conn:
                conn.close()
    
    @staticmethod
    def get_usage_analytics(user_id: int, days: int = 30) -> Dict[str, Any]:
        """
        Get AI usage analytics for user
        
        Returns:
            {
                'total_consumed': float,
                'by_endpoint': dict,
                'by_model': dict,
                'daily_usage': list
            }
        """
        conn = None
        try:
            conn = get_db_connection()
            cursor = conn.cursor(dictionary=True)
            
            # Total consumed in period
            cursor.execute("""
                SELECT 
                    COALESCE(SUM(ABS(amount)), 0) as total_consumed,
                    COALESCE(SUM(total_tokens), 0) as total_tokens
                FROM ai_token_transactions
                WHERE user_id = %s
                AND transaction_type = 'consumption'
                AND created_at >= DATE_SUB(NOW(), INTERVAL %s DAY)
            """, (user_id, days))
            
            totals = cursor.fetchone()
            
            # By endpoint
            cursor.execute("""
                SELECT 
                    endpoint,
                    COALESCE(SUM(ABS(amount)), 0) as tokens_consumed,
                    COUNT(*) as request_count
                FROM ai_token_transactions
                WHERE user_id = %s
                AND transaction_type = 'consumption'
                AND created_at >= DATE_SUB(NOW(), INTERVAL %s DAY)
                AND endpoint IS NOT NULL
                GROUP BY endpoint
                ORDER BY tokens_consumed DESC
            """, (user_id, days))
            
            by_endpoint = cursor.fetchall()
            
            # By model
            cursor.execute("""
                SELECT 
                    ai_model,
                    COALESCE(SUM(ABS(amount)), 0) as tokens_consumed,
                    COUNT(*) as request_count
                FROM ai_token_transactions
                WHERE user_id = %s
                AND transaction_type = 'consumption'
                AND created_at >= DATE_SUB(NOW(), INTERVAL %s DAY)
                AND ai_model IS NOT NULL
                GROUP BY ai_model
                ORDER BY tokens_consumed DESC
            """, (user_id, days))
            
            by_model = cursor.fetchall()
            
            # Daily usage
            cursor.execute("""
                SELECT 
                    DATE(created_at) as date,
                    COALESCE(SUM(ABS(amount)), 0) as tokens_consumed,
                    COUNT(*) as requests
                FROM ai_token_transactions
                WHERE user_id = %s
                AND transaction_type = 'consumption'
                AND created_at >= DATE_SUB(NOW(), INTERVAL %s DAY)
                GROUP BY DATE(created_at)
                ORDER BY date ASC
            """, (user_id, days))
            
            daily_usage = cursor.fetchall()
            
            return {
                'total_consumed': float(totals['total_consumed']),
                'total_tokens': int(totals['total_tokens']),
                'by_endpoint': by_endpoint,
                'by_model': by_model,
                'daily_usage': daily_usage
            }
            
        except Exception as e:
            logger.error(f"Error getting usage analytics: {e}")
            return {
                'total_consumed': 0,
                'total_tokens': 0,
                'by_endpoint': [],
                'by_model': [],
                'daily_usage': []
            }
        finally:
            if conn:
                conn.close()
