"""
Subscription management for broker billing
"""

import logging
from datetime import datetime, date, timedelta
from typing import Dict, Any, Optional, List
from decimal import Decimal as Dec

logger = logging.getLogger(__name__)


class SubscriptionManager:
    """
    Manages broker subscriptions including creation, upgrades, downgrades, and cancellation.
    
    Features:
    - Create new subscriptions with default plan settings
    - Upgrade/downgrade subscription plans
    - Handle subscription cancellation
    - Manage billing cycles
    - Track trial periods
    """
    
    # Subscription statuses
    STATUS_ACTIVE = 'active'
    STATUS_SUSPENDED = 'suspended'
    STATUS_CANCELLED = 'cancelled'
    STATUS_TRIAL = 'trial'
    
    # Plan types
    PLAN_FREE = 'free'
    PLAN_BASIC = 'basic'
    PLAN_PROFESSIONAL = 'professional'
    PLAN_ENTERPRISE = 'enterprise'
    
    # Trial duration in days
    TRIAL_DURATION_DAYS = 14
    
    def __init__(self, db_manager):
        """
        Initialize subscription manager.
        
        Args:
            db_manager: Database manager instance
        """
        self.db_manager = db_manager
        logger.info("SubscriptionManager initialized")
    
    def create_subscription(self, user_id: int, plan_type: str = 'free',
                            currency: str = 'USD', timezone: str = 'UTC',
                            start_trial: bool = False) -> Dict[str, Any]:
        """
        Create a new subscription for a broker.
        
        Args:
            user_id: User ID for the broker
            plan_type: Subscription plan type
            currency: Preferred currency
            timezone: Preferred timezone
            start_trial: Whether to start with a trial period
            
        Returns:
            Dict with subscription details or error
        """
        try:
            session = self.db_manager.get_session()
            try:
                from ..database.models import BrokerSubscriptionModel, SubscriptionPlanModel, UserModel
                
                # Check if user exists and has broker role
                user = session.query(UserModel).get(user_id)
                if not user:
                    return {
                        'success': False,
                        'error': 'User not found'
                    }
                
                # Check if subscription already exists
                existing = session.query(BrokerSubscriptionModel).filter_by(
                    user_id=user_id
                ).first()
                
                if existing:
                    return {
                        'success': False,
                        'error': 'Subscription already exists for this user',
                        'subscription_id': existing.id
                    }
                
                # Get plan details
                plan = session.query(SubscriptionPlanModel).filter_by(
                    plan_type=plan_type,
                    is_active=True
                ).first()
                
                if not plan:
                    # Use default values if plan not found
                    plan_defaults = self._get_plan_defaults(plan_type)
                else:
                    plan_defaults = {
                        'transaction_fee': plan.transaction_fee,
                        'transaction_fee_currency': plan.transaction_fee_currency,
                        'revenue_share_percentage': plan.revenue_share_percentage,
                        'monthly_fee': plan.monthly_fee,
                        'monthly_fee_currency': plan.monthly_fee_currency
                    }
                
                # Calculate billing cycle
                today = date.today()
                billing_cycle_start = today
                billing_cycle_end = self._calculate_cycle_end(today)
                next_billing_date = billing_cycle_end
                
                # Create subscription
                subscription = BrokerSubscriptionModel(
                    user_id=user_id,
                    plan_type=plan_type,
                    status=self.STATUS_TRIAL if start_trial else self.STATUS_ACTIVE,
                    currency=currency,
                    timezone=timezone,
                    
                    # Fee settings from plan
                    transaction_fee=plan_defaults['transaction_fee'],
                    transaction_fee_currency=plan_defaults['transaction_fee_currency'],
                    revenue_share_percentage=plan_defaults['revenue_share_percentage'],
                    monthly_fee=plan_defaults['monthly_fee'],
                    monthly_fee_currency=plan_defaults['monthly_fee_currency'],
                    
                    # Payment gateway settings
                    use_custom_gateway=False,
                    custom_gateway_config=None,
                    
                    # Billing cycle
                    billing_cycle_start=billing_cycle_start,
                    billing_cycle_end=billing_cycle_end,
                    next_billing_date=next_billing_date,
                    
                    # Trial settings
                    is_trial=start_trial,
                    trial_end_date=datetime.utcnow() + timedelta(days=self.TRIAL_DURATION_DAYS) if start_trial else None
                )
                
                session.add(subscription)
                session.commit()
                
                logger.info(f"Created subscription for user {user_id}: plan={plan_type}, trial={start_trial}")
                
                return {
                    'success': True,
                    'subscription_id': subscription.id,
                    'plan_type': plan_type,
                    'status': subscription.status,
                    'currency': currency,
                    'timezone': timezone,
                    'billing_cycle_start': str(billing_cycle_start),
                    'billing_cycle_end': str(billing_cycle_end),
                    'next_billing_date': str(next_billing_date),
                    'is_trial': start_trial,
                    'trial_end_date': subscription.trial_end_date.isoformat() if subscription.trial_end_date else None
                }
                
            finally:
                session.close()
                
        except Exception as e:
            logger.error(f"Failed to create subscription: {e}")
            return {
                'success': False,
                'error': str(e)
            }
    
    def get_subscription(self, user_id: int) -> Optional[Dict[str, Any]]:
        """
        Get subscription details for a broker.
        
        Args:
            user_id: User ID
            
        Returns:
            Dict with subscription details or None
        """
        try:
            session = self.db_manager.get_session()
            try:
                from ..database.models import BrokerSubscriptionModel
                
                subscription = session.query(BrokerSubscriptionModel).filter_by(
                    user_id=user_id
                ).first()
                
                if not subscription:
                    return None
                
                return {
                    'id': subscription.id,
                    'user_id': subscription.user_id,
                    'plan_type': subscription.plan_type,
                    'status': subscription.status,
                    'currency': subscription.currency,
                    'timezone': subscription.timezone,
                    'transaction_fee': float(subscription.transaction_fee),
                    'transaction_fee_currency': subscription.transaction_fee_currency,
                    'revenue_share_percentage': float(subscription.revenue_share_percentage),
                    'monthly_fee': float(subscription.monthly_fee),
                    'monthly_fee_currency': subscription.monthly_fee_currency,
                    'use_custom_gateway': subscription.use_custom_gateway,
                    'billing_cycle_start': str(subscription.billing_cycle_start),
                    'billing_cycle_end': str(subscription.billing_cycle_end),
                    'next_billing_date': str(subscription.next_billing_date),
                    'is_trial': subscription.is_trial,
                    'trial_end_date': subscription.trial_end_date.isoformat() if subscription.trial_end_date else None,
                    'created_at': subscription.created_at.isoformat() if subscription.created_at else None,
                    'updated_at': subscription.updated_at.isoformat() if subscription.updated_at else None
                }
                
            finally:
                session.close()
                
        except Exception as e:
            logger.error(f"Failed to get subscription: {e}")
            return None
    
    def upgrade_subscription(self, user_id: int, new_plan_type: str,
                              currency: Optional[str] = None,
                              timezone: Optional[str] = None) -> Dict[str, Any]:
        """
        Upgrade or downgrade a subscription.
        
        Args:
            user_id: User ID
            new_plan_type: New plan type
            currency: Optional new currency
            timezone: Optional new timezone
            
        Returns:
            Dict with result
        """
        try:
            session = self.db_manager.get_session()
            try:
                from ..database.models import BrokerSubscriptionModel, SubscriptionPlanModel
                
                subscription = session.query(BrokerSubscriptionModel).filter_by(
                    user_id=user_id
                ).first()
                
                if not subscription:
                    return {
                        'success': False,
                        'error': 'Subscription not found'
                    }
                
                old_plan = subscription.plan_type
                
                # Get new plan details
                plan = session.query(SubscriptionPlanModel).filter_by(
                    plan_type=new_plan_type,
                    is_active=True
                ).first()
                
                if plan:
                    subscription.plan_type = new_plan_type
                    subscription.transaction_fee = plan.transaction_fee
                    subscription.transaction_fee_currency = plan.transaction_fee_currency
                    subscription.revenue_share_percentage = plan.revenue_share_percentage
                    subscription.monthly_fee = plan.monthly_fee
                    subscription.monthly_fee_currency = plan.monthly_fee_currency
                else:
                    # Use defaults
                    defaults = self._get_plan_defaults(new_plan_type)
                    subscription.plan_type = new_plan_type
                    subscription.transaction_fee = defaults['transaction_fee']
                    subscription.transaction_fee_currency = defaults['transaction_fee_currency']
                    subscription.revenue_share_percentage = defaults['revenue_share_percentage']
                    subscription.monthly_fee = defaults['monthly_fee']
                    subscription.monthly_fee_currency = defaults['monthly_fee_currency']
                
                # Update currency and timezone if provided
                if currency:
                    subscription.currency = currency
                if timezone:
                    subscription.timezone = timezone
                
                # Reset custom gateway if downgrading to plan that doesn't allow it
                if new_plan_type in [self.PLAN_FREE, self.PLAN_BASIC]:
                    subscription.use_custom_gateway = False
                    subscription.custom_gateway_config = None
                
                subscription.updated_at = datetime.utcnow()
                session.commit()
                
                logger.info(f"Upgraded subscription for user {user_id}: {old_plan} -> {new_plan_type}")
                
                return {
                    'success': True,
                    'old_plan': old_plan,
                    'new_plan': new_plan_type,
                    'subscription_id': subscription.id
                }
                
            finally:
                session.close()
                
        except Exception as e:
            logger.error(f"Failed to upgrade subscription: {e}")
            return {
                'success': False,
                'error': str(e)
            }
    
    def cancel_subscription(self, user_id: int, 
                             immediate: bool = False) -> Dict[str, Any]:
        """
        Cancel a subscription.
        
        Args:
            user_id: User ID
            immediate: Cancel immediately or at end of billing cycle
            
        Returns:
            Dict with result
        """
        try:
            session = self.db_manager.get_session()
            try:
                from ..database.models import BrokerSubscriptionModel
                
                subscription = session.query(BrokerSubscriptionModel).filter_by(
                    user_id=user_id
                ).first()
                
                if not subscription:
                    return {
                        'success': False,
                        'error': 'Subscription not found'
                    }
                
                if immediate:
                    subscription.status = self.STATUS_CANCELLED
                    subscription.updated_at = datetime.utcnow()
                else:
                    # Mark for cancellation at end of billing cycle
                    # This would typically be handled by a scheduled job
                    subscription.status = self.STATUS_CANCELLED
                    subscription.updated_at = datetime.utcnow()
                
                session.commit()
                
                logger.info(f"Cancelled subscription for user {user_id}: immediate={immediate}")
                
                return {
                    'success': True,
                    'status': subscription.status,
                    'cancelled_at': datetime.utcnow().isoformat()
                }
                
            finally:
                session.close()
                
        except Exception as e:
            logger.error(f"Failed to cancel subscription: {e}")
            return {
                'success': False,
                'error': str(e)
            }
    
    def suspend_subscription(self, user_id: int, 
                              reason: Optional[str] = None) -> Dict[str, Any]:
        """
        Suspend a subscription.
        
        Args:
            user_id: User ID
            reason: Optional reason for suspension
            
        Returns:
            Dict with result
        """
        try:
            session = self.db_manager.get_session()
            try:
                from ..database.models import BrokerSubscriptionModel
                
                subscription = session.query(BrokerSubscriptionModel).filter_by(
                    user_id=user_id
                ).first()
                
                if not subscription:
                    return {
                        'success': False,
                        'error': 'Subscription not found'
                    }
                
                subscription.status = self.STATUS_SUSPENDED
                subscription.updated_at = datetime.utcnow()
                session.commit()
                
                logger.info(f"Suspended subscription for user {user_id}: {reason}")
                
                return {
                    'success': True,
                    'status': subscription.status,
                    'reason': reason
                }
                
            finally:
                session.close()
                
        except Exception as e:
            logger.error(f"Failed to suspend subscription: {e}")
            return {
                'success': False,
                'error': str(e)
            }
    
    def reactivate_subscription(self, user_id: int) -> Dict[str, Any]:
        """
        Reactivate a suspended subscription.
        
        Args:
            user_id: User ID
            
        Returns:
            Dict with result
        """
        try:
            session = self.db_manager.get_session()
            try:
                from ..database.models import BrokerSubscriptionModel
                
                subscription = session.query(BrokerSubscriptionModel).filter_by(
                    user_id=user_id
                ).first()
                
                if not subscription:
                    return {
                        'success': False,
                        'error': 'Subscription not found'
                    }
                
                if subscription.status != self.STATUS_SUSPENDED:
                    return {
                        'success': False,
                        'error': f'Cannot reactivate subscription with status {subscription.status}'
                    }
                
                subscription.status = self.STATUS_ACTIVE
                subscription.updated_at = datetime.utcnow()
                session.commit()
                
                logger.info(f"Reactivated subscription for user {user_id}")
                
                return {
                    'success': True,
                    'status': subscription.status
                }
                
            finally:
                session.close()
                
        except Exception as e:
            logger.error(f"Failed to reactivate subscription: {e}")
            return {
                'success': False,
                'error': str(e)
            }
    
    def renew_billing_cycle(self, user_id: int) -> Dict[str, Any]:
        """
        Renew billing cycle for a subscription.
        
        Args:
            user_id: User ID
            
        Returns:
            Dict with new billing cycle details
        """
        try:
            session = self.db_manager.get_session()
            try:
                from ..database.models import BrokerSubscriptionModel
                
                subscription = session.query(BrokerSubscriptionModel).filter_by(
                    user_id=user_id
                ).first()
                
                if not subscription:
                    return {
                        'success': False,
                        'error': 'Subscription not found'
                    }
                
                # Calculate new billing cycle
                old_cycle_end = subscription.billing_cycle_end
                new_cycle_start = old_cycle_end + timedelta(days=1)
                new_cycle_end = self._calculate_cycle_end(new_cycle_start)
                new_billing_date = new_cycle_end
                
                subscription.billing_cycle_start = new_cycle_start
                subscription.billing_cycle_end = new_cycle_end
                subscription.next_billing_date = new_billing_date
                subscription.updated_at = datetime.utcnow()
                
                session.commit()
                
                logger.info(f"Renewed billing cycle for user {user_id}")
                
                return {
                    'success': True,
                    'billing_cycle_start': str(new_cycle_start),
                    'billing_cycle_end': str(new_cycle_end),
                    'next_billing_date': str(new_billing_date)
                }
                
            finally:
                session.close()
                
        except Exception as e:
            logger.error(f"Failed to renew billing cycle: {e}")
            return {
                'success': False,
                'error': str(e)
            }
    
    def configure_custom_gateway(self, user_id: int, 
                                   gateway_config: Dict[str, Any]) -> Dict[str, Any]:
        """
        Configure custom payment gateway for a subscription.
        
        Args:
            user_id: User ID
            gateway_config: Gateway configuration dict
            
        Returns:
            Dict with result
        """
        try:
            session = self.db_manager.get_session()
            try:
                from ..database.models import BrokerSubscriptionModel
                import json
                
                subscription = session.query(BrokerSubscriptionModel).filter_by(
                    user_id=user_id
                ).first()
                
                if not subscription:
                    return {
                        'success': False,
                        'error': 'Subscription not found'
                    }
                
                # Check if plan allows custom gateway
                if subscription.plan_type in [self.PLAN_FREE, self.PLAN_BASIC]:
                    return {
                        'success': False,
                        'error': 'Custom gateway not allowed for this plan'
                    }
                
                subscription.use_custom_gateway = True
                subscription.custom_gateway_config = json.dumps(gateway_config)
                subscription.updated_at = datetime.utcnow()
                
                session.commit()
                
                logger.info(f"Configured custom gateway for user {user_id}")
                
                return {
                    'success': True,
                    'use_custom_gateway': True
                }
                
            finally:
                session.close()
                
        except Exception as e:
            logger.error(f"Failed to configure custom gateway: {e}")
            return {
                'success': False,
                'error': str(e)
            }
    
    def remove_custom_gateway(self, user_id: int) -> Dict[str, Any]:
        """
        Remove custom payment gateway configuration.
        
        Args:
            user_id: User ID
            
        Returns:
            Dict with result
        """
        try:
            session = self.db_manager.get_session()
            try:
                from ..database.models import BrokerSubscriptionModel
                
                subscription = session.query(BrokerSubscriptionModel).filter_by(
                    user_id=user_id
                ).first()
                
                if not subscription:
                    return {
                        'success': False,
                        'error': 'Subscription not found'
                    }
                
                subscription.use_custom_gateway = False
                subscription.custom_gateway_config = None
                subscription.updated_at = datetime.utcnow()
                
                session.commit()
                
                logger.info(f"Removed custom gateway for user {user_id}")
                
                return {
                    'success': True,
                    'use_custom_gateway': False
                }
                
            finally:
                session.close()
                
        except Exception as e:
            logger.error(f"Failed to remove custom gateway: {e}")
            return {
                'success': False,
                'error': str(e)
            }
    
    def get_all_subscriptions(self, status: Optional[str] = None,
                                plan_type: Optional[str] = None,
                                limit: int = 100, offset: int = 0) -> List[Dict[str, Any]]:
        """
        Get all subscriptions with optional filters.
        
        Args:
            status: Filter by status
            plan_type: Filter by plan type
            limit: Max results
            offset: Pagination offset
            
        Returns:
            List of subscription dicts
        """
        try:
            session = self.db_manager.get_session()
            try:
                from ..database.models import BrokerSubscriptionModel
                
                query = session.query(BrokerSubscriptionModel)
                
                if status:
                    query = query.filter_by(status=status)
                if plan_type:
                    query = query.filter_by(plan_type=plan_type)
                
                subscriptions = query.order_by(
                    BrokerSubscriptionModel.created_at.desc()
                ).limit(limit).offset(offset).all()
                
                return [
                    {
                        'id': sub.id,
                        'user_id': sub.user_id,
                        'plan_type': sub.plan_type,
                        'status': sub.status,
                        'currency': sub.currency,
                        'monthly_fee': float(sub.monthly_fee),
                        'next_billing_date': str(sub.next_billing_date),
                        'is_trial': sub.is_trial
                    }
                    for sub in subscriptions
                ]
                
            finally:
                session.close()
                
        except Exception as e:
            logger.error(f"Failed to get all subscriptions: {e}")
            return []
    
    def _calculate_cycle_end(self, start_date: date) -> date:
        """
        Calculate billing cycle end date.
        
        Args:
            start_date: Cycle start date
            
        Returns:
            Cycle end date (approximately 1 month later)
        """
        # Add one month
        if start_date.month == 12:
            end_date = date(start_date.year + 1, 1, start_date.day)
        else:
            # Handle months with fewer days
            next_month = start_date.month + 1
            year = start_date.year
            day = start_date.day
            
            # Get last day of next month
            if next_month in [1, 3, 5, 7, 8, 10, 12]:
                max_day = 31
            elif next_month in [4, 6, 9, 11]:
                max_day = 30
            else:
                # February
                import calendar
                max_day = 29 if calendar.isleap(year) else 28
            
            day = min(day, max_day)
            end_date = date(year, next_month, day)
        
        # Subtract one day to get the last day of the cycle
        return end_date - timedelta(days=1)
    
    def _get_plan_defaults(self, plan_type: str) -> Dict[str, Any]:
        """Get default values for a plan type."""
        defaults = {
            self.PLAN_FREE: {
                'transaction_fee': Dec('0.05'),
                'transaction_fee_currency': 'USD',
                'revenue_share_percentage': Dec('10.00'),
                'monthly_fee': Dec('0.00'),
                'monthly_fee_currency': 'USD'
            },
            self.PLAN_BASIC: {
                'transaction_fee': Dec('0.03'),
                'transaction_fee_currency': 'USD',
                'revenue_share_percentage': Dec('5.00'),
                'monthly_fee': Dec('29.99'),
                'monthly_fee_currency': 'USD'
            },
            self.PLAN_PROFESSIONAL: {
                'transaction_fee': Dec('0.01'),
                'transaction_fee_currency': 'USD',
                'revenue_share_percentage': Dec('0.00'),
                'monthly_fee': Dec('99.99'),
                'monthly_fee_currency': 'USD'
            },
            self.PLAN_ENTERPRISE: {
                'transaction_fee': Dec('0.00'),
                'transaction_fee_currency': 'USD',
                'revenue_share_percentage': Dec('0.00'),
                'monthly_fee': Dec('0.00'),  # Negotiated
                'monthly_fee_currency': 'USD'
            }
        }
        return defaults.get(plan_type, defaults[self.PLAN_FREE])

    def get_all_plans(self) -> List[Any]:
        """
        Get all available subscription plans from the database.
        
        Returns:
            List of subscription plan objects
        """
        try:
            from ..database.models import SubscriptionPlanModel
            session = self.db_manager.get_session()
            try:
                plans = session.query(SubscriptionPlanModel).filter_by(is_active=True).all()
                return plans
            finally:
                session.close()
        except Exception as e:
            logger.error(f"Failed to get all plans: {e}")
            return []