"""
Subscription Manager Service
Handles all subscription-related operations for the modular subscription system
"""

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

logger = logging.getLogger(__name__)


class SubscriptionManager:
    """Unified subscription management service for all subscription types"""
    
    @staticmethod
    def get_user_subscriptions(
        user_id: int, 
        subscription_type: Optional[str] = None,
        active_only: bool = True
    ) -> List[Dict[str, Any]]:
        """
        Get user's subscriptions, optionally filtered by type
        
        Args:
            user_id: User ID
            subscription_type: Filter by type_key (e.g., 'ai_tokens', 'course_access')
            active_only: Only return active/trial subscriptions
            
        Returns:
            List of subscription dictionaries
        """
        conn = None
        try:
            conn = get_db_connection()
            cursor = conn.cursor(dictionary=True)
            
            query = """
                SELECT 
                    us.*,
                    sp.plan_name,
                    sp.tier,
                    sp.benefits,
                    sp.features_list,
                    st.type_key,
                    st.type_name,
                    st.color,
                    st.icon
                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
            """
            
            params = [user_id]
            
            if subscription_type:
                query += " AND st.type_key = %s"
                params.append(subscription_type)
            
            if active_only:
                query += " AND us.status IN ('active', 'trial')"
            
            query += " ORDER BY us.created_at DESC"
            
            cursor.execute(query, params)
            subscriptions = cursor.fetchall()
            
            # Parse JSON fields
            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('features_list'):
                    sub['features_list'] = json.loads(sub['features_list']) if isinstance(sub['features_list'], str) else sub['features_list']
                if sub.get('usage_data'):
                    sub['usage_data'] = json.loads(sub['usage_data']) if isinstance(sub['usage_data'], str) else sub['usage_data']
                if sub.get('metadata'):
                    sub['metadata'] = json.loads(sub['metadata']) if isinstance(sub['metadata'], str) else sub['metadata']
            
            return subscriptions
            
        except Exception as e:
            logger.error(f"Error getting user subscriptions: {e}")
            return []
        finally:
            if conn:
                conn.close()
    
    @staticmethod
    def get_subscription_benefits(user_id: int, subscription_type: str) -> Dict[str, Any]:
        """
        Get aggregated benefits from all active subscriptions of a specific type
        If user has multiple subscriptions of same type, benefits are merged
        
        Args:
            user_id: User ID
            subscription_type: Type key (e.g., 'ai_tokens')
            
        Returns:
            Aggregated benefits dictionary
        """
        subscriptions = SubscriptionManager.get_user_subscriptions(
            user_id, 
            subscription_type=subscription_type,
            active_only=True
        )
        
        if not subscriptions:
            return {}
        
        # Merge benefits from all subscriptions
        aggregated_benefits = {}
        
        for sub in subscriptions:
            benefits = sub.get('benefits', {})
            
            # For AI tokens, sum allowances and merge model access
            if subscription_type == 'ai_tokens' and 'ai_tokens' in benefits:
                if 'ai_tokens' not in aggregated_benefits:
                    aggregated_benefits['ai_tokens'] = {
                        'monthly_allowance': 0,
                        'model_access': [],
                        'priority_processing': False,
                        'rollover_enabled': False
                    }
                
                ai_benefits = benefits['ai_tokens']
                aggregated_benefits['ai_tokens']['monthly_allowance'] += ai_benefits.get('monthly_allowance', 0)
                
                # Merge model access lists
                current_models = aggregated_benefits['ai_tokens']['model_access']
                new_models = ai_benefits.get('model_access', [])
                aggregated_benefits['ai_tokens']['model_access'] = list(set(current_models + new_models))
                
                # Enable if any subscription has it
                if ai_benefits.get('priority_processing'):
                    aggregated_benefits['ai_tokens']['priority_processing'] = True
                if ai_benefits.get('rollover_enabled'):
                    aggregated_benefits['ai_tokens']['rollover_enabled'] = True
            
            # Merge other benefit types
            for benefit_key, benefit_value in benefits.items():
                if benefit_key not in aggregated_benefits:
                    aggregated_benefits[benefit_key] = benefit_value
        
        return aggregated_benefits
    
    @staticmethod
    def check_subscription_access(user_id: int, required_benefit: Dict[str, Any]) -> Dict[str, Any]:
        """
        Check if user has required subscription benefit
        
        Args:
            user_id: User ID
            required_benefit: Dictionary specifying required benefit
                Example: {
                    'type': 'ai_tokens',
                    'model': 'gpt-4o'
                }
                
        Returns:
            {
                'has_access': bool,
                'reason': str (if no access),
                'suggested_plan': str (if no access)
            }
        """
        benefit_type = required_benefit.get('type')
        
        if not benefit_type:
            return {'has_access': False, 'reason': 'Invalid benefit check'}
        
        # Check AI token model access
        if benefit_type == 'ai_tokens':
            required_model = required_benefit.get('model')
            
            # Free models that don't require any subscription
            FREE_MODELS = [
                'geminisearch', 'geminisearch-nano', 'openai-fast', 'qwen-coder',
                'gemini', 'llama', 'mistral', 'claude-free'
            ]
            
            # Allow free models without checking subscription
            if required_model and any(free in required_model.lower() for free in ['gemini', 'geminisearch', 'qwen', 'openai-fast', 'llama', 'mistral']):
                logger.info(f"User {user_id} granted free access to model {required_model}")
                return {'has_access': True}
            
            if required_model in FREE_MODELS:
                logger.info(f"User {user_id} granted free access to model {required_model}")
                return {'has_access': True}
            
            # For non-free models, check subscription benefits
            benefits = SubscriptionManager.get_subscription_benefits(user_id, benefit_type)
            
            if not benefits:
                return {
                    'has_access': False,
                    'reason': f'Model {required_model} requires an AI subscription',
                    'suggested_plan': 'ai-starter'
                }
            
            ai_benefits = benefits.get('ai_tokens', {})
            model_access = ai_benefits.get('model_access', [])
            
            # Check if "all" or specific model is in access list
            if 'all' in model_access or required_model in model_access:
                return {'has_access': True}
            else:
                return {
                    'has_access': False,
                    'reason': f'Model {required_model} not available in your plan',
                    'suggested_plan': 'ai-pro'
                }
        
        # Check course access
        elif benefit_type == 'course_access':
            course_id = required_benefit.get('course_id')
            category_id = required_benefit.get('category_id')
            
            if not benefits:
                return {
                    'has_access': False,
                    'reason': 'No active course subscription',
                    'suggested_plan': 'course-explorer'
                }
            
            course_benefits = benefits.get('courses', {})
            access_type = course_benefits.get('access_type')
            
            if access_type == 'full':
                return {'has_access': True}
            elif access_type == 'category':
                allowed_categories = course_benefits.get('category_ids', [])
                if category_id in allowed_categories:
                    return {'has_access': True}
                else:
                    return {
                        'has_access': False,
                        'reason': 'This course category not included in your plan',
                        'suggested_plan': 'course-full'
                    }
        
        return {'has_access': False, 'reason': 'Benefit type not supported'}
    
    @staticmethod
    def create_subscription(
        user_id: int,
        plan_id: int,
        billing_cycle: str = 'monthly',
        payment_data: Optional[Dict[str, Any]] = None,
        trial_days: int = 0
    ) -> Dict[str, Any]:
        """
        Create new subscription for user
        
        Args:
            user_id: User ID
            plan_id: Subscription plan ID
            billing_cycle: 'monthly', 'yearly', or 'lifetime'
            payment_data: Payment information dictionary
            trial_days: Number of trial days
            
        Returns:
            {
                'success': bool,
                'subscription_id': int,
                'message': str
            }
        """
        conn = None
        try:
            conn = get_db_connection()
            cursor = conn.cursor(dictionary=True)
            
            # Get plan details
            cursor.execute("""
                SELECT sp.*, st.type_key 
                FROM subscription_plans sp
                JOIN subscription_types st ON sp.subscription_type_id = st.id
                WHERE sp.id = %s AND sp.is_active = TRUE
            """, (plan_id,))
            
            plan = cursor.fetchone()
            
            if not plan:
                return {'success': False, 'message': 'Invalid or inactive plan'}
            
            # Calculate subscription period
            now = datetime.now()
            trial_ends_at = None
            
            if trial_days > 0:
                status = 'trial'
                trial_ends_at = now + timedelta(days=trial_days)
                current_period_end = trial_ends_at
            else:
                status = 'active'
                if billing_cycle == 'monthly':
                    current_period_end = now + timedelta(days=30)
                elif billing_cycle == 'yearly':
                    current_period_end = now + timedelta(days=365)
                else:  # lifetime
                    current_period_end = now + timedelta(days=365 * 100)  # Far future
            
            # Insert subscription
            insert_query = """
                INSERT INTO user_subscriptions (
                    user_id, plan_id, subscription_type_id, status, billing_cycle,
                    started_at, trial_ends_at, current_period_start, current_period_end,
                    expires_at, next_billing_date, payment_method, last_payment_id,
                    last_payment_amount, last_payment_date, usage_data
                ) VALUES (
                    %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s
                )
            """
            
            payment_method = payment_data.get('method') if payment_data else None
            payment_id = payment_data.get('payment_id') if payment_data else None
            payment_amount = payment_data.get('amount') if payment_data else None
            
            # Initialize usage data based on subscription type
            usage_data = {}
            benefits = json.loads(plan['benefits']) if isinstance(plan['benefits'], str) else plan['benefits']
            
            if plan['type_key'] == 'ai_tokens' and 'ai_tokens' in benefits:
                usage_data = {
                    'ai_tokens_allocated': benefits['ai_tokens'].get('monthly_allowance', 0),
                    'ai_tokens_used': 0,
                    'last_reset_date': now.isoformat()
                }
            
            cursor.execute(insert_query, (
                user_id, plan_id, plan['subscription_type_id'], status, billing_cycle,
                now, trial_ends_at, now, current_period_end, current_period_end,
                current_period_end if billing_cycle != 'lifetime' else None,
                payment_method, payment_id, payment_amount,
                now if payment_amount else None,
                json.dumps(usage_data) if usage_data else None
            ))
            
            subscription_id = cursor.lastrowid
            
            # If AI token subscription, add tokens to user balance
            if plan['type_key'] == 'ai_tokens' and 'ai_tokens' in benefits:
                monthly_tokens = benefits['ai_tokens'].get('monthly_allowance', 0)
                
                cursor.execute("""
                    UPDATE user 
                    SET ai_tokens_balance = ai_tokens_balance + %s
                    WHERE id = %s
                """, (monthly_tokens, user_id))
                
                # Log token transaction
                cursor.execute("""
                    SELECT ai_tokens_balance FROM user WHERE id = %s
                """, (user_id,))
                new_balance = cursor.fetchone()['ai_tokens_balance']
                
                cursor.execute("""
                    INSERT INTO ai_token_transactions (
                        user_id, transaction_type, amount, balance_before, balance_after,
                        notes, metadata
                    ) VALUES (%s, 'subscription', %s, %s, %s, %s, %s)
                """, (
                    user_id, monthly_tokens, new_balance - monthly_tokens, new_balance,
                    f'Tokens from subscription: {plan["plan_name"]}',
                    json.dumps({'subscription_id': subscription_id, 'plan_id': plan_id})
                ))
            
            conn.commit()
            
            logger.info(f"Created subscription {subscription_id} for user {user_id}, plan {plan['plan_name']}")
            
            return {
                'success': True,
                'subscription_id': subscription_id,
                'message': f'Successfully subscribed to {plan["plan_name"]}',
                'subscription': {
                    'id': subscription_id,
                    'plan_name': plan['plan_name'],
                    'status': status,
                    'expires_at': current_period_end.isoformat()
                }
            }
            
        except Exception as e:
            if conn:
                conn.rollback()
            logger.error(f"Error creating subscription: {e}")
            return {'success': False, 'message': str(e)}
        finally:
            if conn:
                conn.close()
    
    @staticmethod
    def cancel_subscription(
        user_id: int,
        subscription_id: int,
        reason: Optional[str] = None,
        immediate: bool = False
    ) -> Dict[str, Any]:
        """
        Cancel user subscription
        
        Args:
            user_id: User ID
            subscription_id: Subscription ID to cancel
            reason: Cancellation reason
            immediate: If True, cancel immediately. If False, cancel at period end
            
        Returns:
            {'success': bool, 'message': str}
        """
        conn = None
        try:
            conn = get_db_connection()
            cursor = conn.cursor(dictionary=True)
            
            # Verify subscription belongs to user
            cursor.execute("""
                SELECT * FROM user_subscriptions
                WHERE id = %s AND user_id = %s
            """, (subscription_id, user_id))
            
            subscription = cursor.fetchone()
            
            if not subscription:
                return {'success': False, 'message': 'Subscription not found'}
            
            if subscription['status'] in ['cancelled', 'expired']:
                return {'success': False, 'message': 'Subscription already cancelled or expired'}
            
            now = datetime.now()
            
            if immediate:
                # Cancel immediately
                cursor.execute("""
                    UPDATE user_subscriptions
                    SET status = 'cancelled',
                        cancelled_at = %s,
                        cancellation_reason = %s,
                        cancelled_by = 'user',
                        auto_renew = FALSE
                    WHERE id = %s
                """, (now, reason, subscription_id))
                
                message = 'Subscription cancelled immediately'
            else:
                # Cancel at period end
                cursor.execute("""
                    UPDATE user_subscriptions
                    SET cancel_at_period_end = TRUE,
                        cancellation_reason = %s,
                        cancelled_by = 'user',
                        auto_renew = FALSE
                    WHERE id = %s
                """, (reason, subscription_id))
                
                message = f'Subscription will be cancelled on {subscription["current_period_end"]}'
            
            conn.commit()
            
            logger.info(f"Cancelled subscription {subscription_id} for user {user_id}")
            
            return {'success': True, 'message': message}
            
        except Exception as e:
            if conn:
                conn.rollback()
            logger.error(f"Error cancelling subscription: {e}")
            return {'success': False, 'message': str(e)}
        finally:
            if conn:
                conn.close()
    
    @staticmethod
    def reset_monthly_allowances() -> Dict[str, Any]:
        """
        Reset usage-based allowances (AI tokens, etc.) for subscriptions
        Run this as a cron job monthly
        
        Returns:
            {'success': bool, 'reset_count': int}
        """
        conn = None
        try:
            conn = get_db_connection()
            cursor = conn.cursor(dictionary=True)
            
            now = datetime.now()
            reset_count = 0
            
            # Get all active AI token subscriptions that need reset
            cursor.execute("""
                SELECT us.*, 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.status = 'active'
                AND st.type_key = 'ai_tokens'
                AND us.current_period_end <= %s
            """, (now,))
            
            subscriptions_to_reset = cursor.fetchall()
            
            for sub in subscriptions_to_reset:
                benefits = json.loads(sub['benefits']) if isinstance(sub['benefits'], str) else sub['benefits']
                
                if 'ai_tokens' in benefits:
                    monthly_allowance = benefits['ai_tokens'].get('monthly_allowance', 0)
                    
                    # Add tokens to user balance
                    cursor.execute("""
                        UPDATE users
                        SET ai_tokens_balance = ai_tokens_balance + %s
                        WHERE id = %s
                    """, (monthly_allowance, sub['user_id']))
                    
                    # Update subscription period
                    new_period_start = sub['current_period_end']
                    new_period_end = new_period_start + timedelta(days=30)
                    
                    cursor.execute("""
                        UPDATE user_subscriptions
                        SET current_period_start = %s,
                            current_period_end = %s,
                            usage_data = JSON_SET(
                                COALESCE(usage_data, '{}'),
                                '$.last_reset_date', %s,
                                '$.ai_tokens_used', 0
                            )
                        WHERE id = %s
                    """, (new_period_start, new_period_end, now.isoformat(), sub['id']))
                    
                    # Log transaction
                    cursor.execute("""
                        INSERT INTO ai_token_transactions (
                            user_id, transaction_type, amount, balance_before, balance_after,
                            notes, metadata
                        )
                        SELECT 
                            %s, 'subscription', %s, 
                            ai_tokens_balance - %s, ai_tokens_balance,
                            'Monthly token reset', 
                            %s
                        FROM user WHERE id = %s
                    """, (
                        sub['user_id'], monthly_allowance, monthly_allowance,
                        json.dumps({'subscription_id': sub['id'], 'reset_date': now.isoformat()}),
                        sub['user_id']
                    ))
                    
                    reset_count += 1
            
            conn.commit()
            
            logger.info(f"Reset monthly allowances for {reset_count} subscriptions")
            
            return {'success': True, 'reset_count': reset_count}
            
        except Exception as e:
            if conn:
                conn.rollback()
            logger.error(f"Error resetting monthly allowances: {e}")
            return {'success': False, 'message': str(e), 'reset_count': 0}
        finally:
            if conn:
                conn.close()
