from flask import Blueprint, request, jsonify, current_app
from flask_login import login_required, current_user
from app import db
from app.models import Income
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
import json

bp = Blueprint('income', __name__, url_prefix='/api/income')


def calculate_income_next_due_date(frequency, custom_days=None, from_date=None):
    """Calculate next due date for recurring income based on frequency
    Args:
        frequency: 'once', 'weekly', 'biweekly', 'every4weeks', 'monthly', 'custom'
        custom_days: Number of days for custom frequency
        from_date: Starting date (default: today)
    Returns:
        Next due date or None for one-time income
    """
    if frequency == 'once':
        return None
    
    if from_date is None:
        from_date = datetime.utcnow()
    
    if frequency == 'weekly':
        return from_date + timedelta(days=7)
    elif frequency == 'biweekly':
        return from_date + timedelta(days=14)
    elif frequency == 'every4weeks':
        return from_date + timedelta(days=28)
    elif frequency == 'monthly':
        return from_date + relativedelta(months=1)
    elif frequency == 'custom' and custom_days:
        return from_date + timedelta(days=custom_days)
    
    return None


@bp.route('/', methods=['GET'])
@login_required
def get_income():
    """Get income entries with filtering and pagination
    Security: Only returns income for current_user
    """
    current_app.logger.info(f"Getting income for user {current_user.id}")
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('per_page', 20, type=int)
    start_date = request.args.get('start_date')
    end_date = request.args.get('end_date')
    source = request.args.get('source')
    search = request.args.get('search', '')
    
    # Security: Filter by current user
    query = Income.query.filter_by(user_id=current_user.id)
    
    if source:
        query = query.filter_by(source=source)
    
    if start_date:
        query = query.filter(Income.date >= datetime.fromisoformat(start_date))
    
    if end_date:
        query = query.filter(Income.date <= datetime.fromisoformat(end_date))
    
    if search:
        query = query.filter(Income.description.ilike(f'%{search}%'))
    
    pagination = query.order_by(Income.date.desc()).paginate(
        page=page, per_page=per_page, error_out=False
    )
    
    current_app.logger.info(f"Found {pagination.total} income entries for user {current_user.id}")
    
    return jsonify({
        'income': [inc.to_dict() for inc in pagination.items],
        'total': pagination.total,
        'pages': pagination.pages,
        'current_page': page
    })


@bp.route('/', methods=['POST'])
@login_required
def create_income():
    """Create new income entry
    Security: Only creates income for current_user
    """
    data = request.get_json()
    current_app.logger.info(f"Creating income for user {current_user.id}, data: {data}")
    
    # Validate required fields
    if not data or not data.get('amount') or not data.get('source') or not data.get('description'):
        current_app.logger.warning(f"Missing required fields: {data}")
        return jsonify({'success': False, 'message': 'Missing required fields'}), 400
    
    try:
        income_date = datetime.fromisoformat(data.get('date')) if data.get('date') else datetime.utcnow()
        frequency = data.get('frequency', 'once')
        custom_days = data.get('custom_days')
        auto_create = data.get('auto_create', False)
        
        # Calculate next due date for recurring income
        next_due_date = None
        if frequency != 'once' and auto_create:
            next_due_date = calculate_income_next_due_date(frequency, custom_days, income_date)
        
        # Create income entry
        income = Income(
            amount=float(data.get('amount')),
            currency=data.get('currency', current_user.currency),
            description=data.get('description'),
            source=data.get('source'),
            user_id=current_user.id,
            tags=json.dumps(data.get('tags', [])) if isinstance(data.get('tags'), list) else data.get('tags', '[]'),
            frequency=frequency,
            custom_days=custom_days,
            next_due_date=next_due_date,
            is_active=True,
            auto_create=auto_create,
            date=income_date
        )
        
        current_app.logger.info(f"Adding income to session: {income.description}")
        db.session.add(income)
        db.session.commit()
        current_app.logger.info(f"Income committed with ID: {income.id}")
        
        # Verify it was saved
        saved_income = Income.query.filter_by(id=income.id).first()
        current_app.logger.info(f"Verification - Income exists: {saved_income is not None}")
        
        return jsonify({
            'success': True,
            'message': 'Income added successfully',
            'income': income.to_dict()
        }), 201
        
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Error creating income: {str(e)}", exc_info=True)
        return jsonify({'success': False, 'message': 'Failed to create income'}), 500


@bp.route('/<int:income_id>', methods=['PUT'])
@login_required
def update_income(income_id):
    """Update income entry
    Security: Only allows updating user's own income
    """
    # Security check: verify income belongs to current user
    income = Income.query.filter_by(id=income_id, user_id=current_user.id).first()
    if not income:
        return jsonify({'success': False, 'message': 'Income not found'}), 404
    
    data = request.get_json()
    
    try:
        # Update fields
        if 'amount' in data:
            income.amount = float(data['amount'])
        if 'currency' in data:
            income.currency = data['currency']
        if 'description' in data:
            income.description = data['description']
        if 'source' in data:
            income.source = data['source']
        if 'tags' in data:
            income.tags = json.dumps(data['tags']) if isinstance(data['tags'], list) else data['tags']
        if 'date' in data:
            income.date = datetime.fromisoformat(data['date'])
        
        # Handle frequency changes
        frequency_changed = False
        if 'frequency' in data and data['frequency'] != income.frequency:
            income.frequency = data['frequency']
            frequency_changed = True
        
        if 'custom_days' in data:
            income.custom_days = data['custom_days']
            frequency_changed = True
        
        if 'auto_create' in data:
            income.auto_create = data['auto_create']
        
        if 'is_active' in data:
            income.is_active = data['is_active']
        
        # Recalculate next_due_date if frequency changed or auto_create enabled
        if (frequency_changed or 'auto_create' in data) and income.auto_create and income.is_active:
            if income.frequency != 'once':
                from_date = income.last_created_date if income.last_created_date else income.date
                income.next_due_date = calculate_income_next_due_date(
                    income.frequency, 
                    income.custom_days, 
                    from_date
                )
            else:
                income.next_due_date = None
        elif not income.auto_create or not income.is_active:
            income.next_due_date = None
        
        income.updated_at = datetime.utcnow()
        
        db.session.commit()
        
        return jsonify({
            'success': True,
            'message': 'Income updated successfully',
            'income': income.to_dict()
        })
        
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Error updating income: {str(e)}")
        return jsonify({'success': False, 'message': 'Failed to update income'}), 500


@bp.route('/<int:income_id>', methods=['DELETE'])
@login_required
def delete_income(income_id):
    """Delete income entry
    Security: Only allows deleting user's own income
    """
    # Security check: verify income belongs to current user
    income = Income.query.filter_by(id=income_id, user_id=current_user.id).first()
    if not income:
        return jsonify({'success': False, 'message': 'Income not found'}), 404
    
    try:
        db.session.delete(income)
        db.session.commit()
        
        return jsonify({
            'success': True,
            'message': 'Income deleted successfully'
        })
        
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Error deleting income: {str(e)}")
        return jsonify({'success': False, 'message': 'Failed to delete income'}), 500


@bp.route('/<int:income_id>/toggle', methods=['PUT'])
@login_required
def toggle_recurring_income(income_id):
    """Toggle recurring income active status
    Security: Only allows toggling user's own income
    """
    # Security check: verify income belongs to current user
    income = Income.query.filter_by(id=income_id, user_id=current_user.id).first()
    if not income:
        return jsonify({'success': False, 'message': 'Income not found'}), 404
    
    try:
        income.is_active = not income.is_active
        
        # Clear next_due_date if deactivated
        if not income.is_active:
            income.next_due_date = None
        elif income.auto_create and income.frequency != 'once':
            # Recalculate next_due_date when reactivated
            from_date = income.last_created_date if income.last_created_date else income.date
            income.next_due_date = calculate_income_next_due_date(
                income.frequency, 
                income.custom_days, 
                from_date
            )
        
        income.updated_at = datetime.utcnow()
        db.session.commit()
        
        return jsonify({
            'success': True,
            'message': f'Income {"activated" if income.is_active else "deactivated"}',
            'income': income.to_dict()
        })
        
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Error toggling income: {str(e)}")
        return jsonify({'success': False, 'message': 'Failed to toggle income'}), 500


@bp.route('/<int:income_id>/create-now', methods=['POST'])
@login_required
def create_income_now(income_id):
    """Manually create income entry from recurring income
    Security: Only allows creating from user's own recurring income
    """
    # Security check: verify income belongs to current user
    recurring_income = Income.query.filter_by(id=income_id, user_id=current_user.id).first()
    if not recurring_income:
        return jsonify({'success': False, 'message': 'Recurring income not found'}), 404
    
    if recurring_income.frequency == 'once':
        return jsonify({'success': False, 'message': 'This is not a recurring income'}), 400
    
    try:
        # Create new income entry based on recurring income
        new_income = Income(
            amount=recurring_income.amount,
            currency=recurring_income.currency,
            description=recurring_income.description,
            source=recurring_income.source,
            user_id=current_user.id,
            tags=recurring_income.tags,
            frequency='once',  # Created income is one-time
            date=datetime.utcnow()
        )
        
        db.session.add(new_income)
        
        # Update recurring income's next due date and last created date
        recurring_income.last_created_date = datetime.utcnow()
        if recurring_income.auto_create and recurring_income.is_active:
            recurring_income.next_due_date = calculate_income_next_due_date(
                recurring_income.frequency,
                recurring_income.custom_days,
                recurring_income.last_created_date
            )
        
        db.session.commit()
        
        return jsonify({
            'success': True,
            'message': 'Income created successfully',
            'income': new_income.to_dict(),
            'recurring_income': recurring_income.to_dict()
        }), 201
        
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Error creating income from recurring: {str(e)}")
        return jsonify({'success': False, 'message': 'Failed to create income'}), 500


@bp.route('/sources', methods=['GET'])
@login_required
def get_income_sources():
    """Get list of income sources
    Returns predefined sources for consistency
    """
    sources = [
        {'value': 'Salary', 'label': 'Salary', 'icon': 'payments'},
        {'value': 'Freelance', 'label': 'Freelance', 'icon': 'work'},
        {'value': 'Investment', 'label': 'Investment', 'icon': 'trending_up'},
        {'value': 'Rental', 'label': 'Rental Income', 'icon': 'home'},
        {'value': 'Gift', 'label': 'Gift', 'icon': 'card_giftcard'},
        {'value': 'Bonus', 'label': 'Bonus', 'icon': 'star'},
        {'value': 'Refund', 'label': 'Refund', 'icon': 'refresh'},
        {'value': 'Other', 'label': 'Other', 'icon': 'category'}
    ]
    
    return jsonify({'sources': sources})


@bp.route('/summary', methods=['GET'])
@login_required
def get_income_summary():
    """Get income summary for dashboard
    Security: Only returns data for current_user
    """
    start_date = request.args.get('start_date')
    end_date = request.args.get('end_date')
    
    # Security: Filter by current user
    query = Income.query.filter_by(user_id=current_user.id)
    
    if start_date:
        query = query.filter(Income.date >= datetime.fromisoformat(start_date))
    
    if end_date:
        query = query.filter(Income.date <= datetime.fromisoformat(end_date))
    
    # Calculate totals by source
    income_by_source = db.session.query(
        Income.source,
        db.func.sum(Income.amount).label('total'),
        db.func.count(Income.id).label('count')
    ).filter_by(user_id=current_user.id)
    
    if start_date:
        income_by_source = income_by_source.filter(Income.date >= datetime.fromisoformat(start_date))
    if end_date:
        income_by_source = income_by_source.filter(Income.date <= datetime.fromisoformat(end_date))
    
    income_by_source = income_by_source.group_by(Income.source).all()
    
    total_income = sum(item.total for item in income_by_source)
    
    breakdown = [
        {
            'source': item.source,
            'total': float(item.total),
            'count': item.count,
            'percentage': (float(item.total) / total_income * 100) if total_income > 0 else 0
        }
        for item in income_by_source
    ]
    
    return jsonify({
        'total_income': total_income,
        'count': sum(item.count for item in income_by_source),
        'breakdown': breakdown
    })
