from flask import Blueprint, request, jsonify, current_app
from flask_login import login_required, current_user
from app import db
from app.models import RecurringExpense, Expense, Category
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from collections import defaultdict
import re

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


def calculate_next_due_date(frequency, day_of_period=None, from_date=None):
    """Calculate next due date based on frequency"""
    base_date = from_date or datetime.utcnow()
    
    if frequency == 'daily':
        return base_date + timedelta(days=1)
    elif frequency == 'weekly':
        # day_of_period is day of week (0=Monday, 6=Sunday)
        target_day = day_of_period if day_of_period is not None else base_date.weekday()
        days_ahead = target_day - base_date.weekday()
        if days_ahead <= 0:
            days_ahead += 7
        return base_date + timedelta(days=days_ahead)
    elif frequency == 'monthly':
        # day_of_period is day of month (1-31)
        target_day = day_of_period if day_of_period is not None else base_date.day
        next_month = base_date + relativedelta(months=1)
        try:
            return next_month.replace(day=min(target_day, 28))  # Safe day
        except ValueError:
            # Handle months with fewer days
            return next_month.replace(day=28)
    elif frequency == 'yearly':
        return base_date + relativedelta(years=1)
    else:
        return base_date + timedelta(days=30)


@bp.route('/', methods=['GET'])
@login_required
def get_recurring_expenses():
    """Get all recurring expenses for current user"""
    # Security: Filter by user_id
    recurring = RecurringExpense.query.filter_by(user_id=current_user.id).order_by(
        RecurringExpense.is_active.desc(),
        RecurringExpense.next_due_date.asc()
    ).all()
    
    return jsonify({
        'recurring_expenses': [r.to_dict() for r in recurring]
    })


@bp.route('/', methods=['POST'])
@login_required
def create_recurring_expense():
    """Create a new recurring expense"""
    data = request.get_json()
    
    # Validate required fields
    if not data or not data.get('name') or not data.get('amount') or not data.get('category_id') or not data.get('frequency'):
        return jsonify({'success': False, 'message': 'Missing required fields'}), 400
    
    # Security: Verify category belongs to current user
    category = Category.query.filter_by(id=int(data.get('category_id')), user_id=current_user.id).first()
    if not category:
        return jsonify({'success': False, 'message': 'Invalid category'}), 400
    
    # Validate frequency
    valid_frequencies = ['daily', 'weekly', 'monthly', 'yearly']
    frequency = data.get('frequency')
    if frequency not in valid_frequencies:
        return jsonify({'success': False, 'message': 'Invalid frequency'}), 400
    
    # Calculate next due date
    day_of_period = data.get('day_of_period')
    next_due_date = data.get('next_due_date')
    
    if next_due_date:
        next_due_date = datetime.fromisoformat(next_due_date)
    else:
        next_due_date = calculate_next_due_date(frequency, day_of_period)
    
    # Create recurring expense
    recurring = RecurringExpense(
        name=data.get('name'),
        amount=float(data.get('amount')),
        currency=data.get('currency', current_user.currency),
        category_id=int(data.get('category_id')),
        frequency=frequency,
        day_of_period=day_of_period,
        next_due_date=next_due_date,
        auto_create=data.get('auto_create', False),
        is_active=data.get('is_active', True),
        notes=data.get('notes'),
        detected=False,  # Manually created
        user_id=current_user.id
    )
    
    db.session.add(recurring)
    db.session.commit()
    
    return jsonify({
        'success': True,
        'recurring_expense': recurring.to_dict()
    }), 201


@bp.route('/<int:recurring_id>', methods=['PUT'])
@login_required
def update_recurring_expense(recurring_id):
    """Update a recurring expense"""
    # Security: Filter by user_id
    recurring = RecurringExpense.query.filter_by(id=recurring_id, user_id=current_user.id).first()
    
    if not recurring:
        return jsonify({'success': False, 'message': 'Recurring expense not found'}), 404
    
    data = request.get_json()
    
    # Update fields
    if data.get('name'):
        recurring.name = data.get('name')
    if data.get('amount'):
        recurring.amount = float(data.get('amount'))
    if data.get('currency'):
        recurring.currency = data.get('currency')
    if data.get('category_id'):
        # Security: Verify category belongs to current user
        category = Category.query.filter_by(id=int(data.get('category_id')), user_id=current_user.id).first()
        if not category:
            return jsonify({'success': False, 'message': 'Invalid category'}), 400
        recurring.category_id = int(data.get('category_id'))
    if data.get('frequency'):
        valid_frequencies = ['daily', 'weekly', 'monthly', 'yearly']
        if data.get('frequency') not in valid_frequencies:
            return jsonify({'success': False, 'message': 'Invalid frequency'}), 400
        recurring.frequency = data.get('frequency')
    if 'day_of_period' in data:
        recurring.day_of_period = data.get('day_of_period')
    if data.get('next_due_date'):
        recurring.next_due_date = datetime.fromisoformat(data.get('next_due_date'))
    if 'auto_create' in data:
        recurring.auto_create = data.get('auto_create')
    if 'is_active' in data:
        recurring.is_active = data.get('is_active')
    if 'notes' in data:
        recurring.notes = data.get('notes')
    
    db.session.commit()
    
    return jsonify({
        'success': True,
        'recurring_expense': recurring.to_dict()
    })


@bp.route('/<int:recurring_id>', methods=['DELETE'])
@login_required
def delete_recurring_expense(recurring_id):
    """Delete a recurring expense"""
    # Security: Filter by user_id
    recurring = RecurringExpense.query.filter_by(id=recurring_id, user_id=current_user.id).first()
    
    if not recurring:
        return jsonify({'success': False, 'message': 'Recurring expense not found'}), 404
    
    db.session.delete(recurring)
    db.session.commit()
    
    return jsonify({'success': True, 'message': 'Recurring expense deleted'})


@bp.route('/<int:recurring_id>/create-expense', methods=['POST'])
@login_required
def create_expense_from_recurring(recurring_id):
    """Manually create an expense from a recurring expense"""
    # Security: Filter by user_id
    recurring = RecurringExpense.query.filter_by(id=recurring_id, user_id=current_user.id).first()
    
    if not recurring:
        return jsonify({'success': False, 'message': 'Recurring expense not found'}), 404
    
    # Create expense
    expense = Expense(
        amount=recurring.amount,
        currency=recurring.currency,
        description=recurring.name,
        category_id=recurring.category_id,
        user_id=current_user.id,
        tags=['recurring', recurring.frequency],
        date=datetime.utcnow()
    )
    expense.set_tags(['recurring', recurring.frequency])
    
    # Update recurring expense
    recurring.last_created_date = datetime.utcnow()
    recurring.next_due_date = calculate_next_due_date(
        recurring.frequency,
        recurring.day_of_period,
        recurring.next_due_date
    )
    
    db.session.add(expense)
    db.session.commit()
    
    return jsonify({
        'success': True,
        'expense': expense.to_dict(),
        'recurring_expense': recurring.to_dict()
    }), 201


@bp.route('/detect', methods=['POST'])
@login_required
def detect_recurring_patterns():
    """
    Detect recurring expense patterns from historical expenses
    Returns suggestions for potential recurring expenses
    """
    # Get user's expenses from last 6 months
    six_months_ago = datetime.utcnow() - relativedelta(months=6)
    expenses = Expense.query.filter(
        Expense.user_id == current_user.id,
        Expense.date >= six_months_ago
    ).order_by(Expense.date.asc()).all()
    
    if len(expenses) < 10:
        return jsonify({
            'suggestions': [],
            'message': 'Not enough expense history to detect patterns'
        })
    
    # Group expenses by similar descriptions and amounts
    patterns = defaultdict(list)
    
    for expense in expenses:
        # Normalize description (lowercase, remove numbers/special chars)
        normalized_desc = re.sub(r'[^a-z\s]', '', expense.description.lower()).strip()
        
        # Create a key based on normalized description and approximate amount
        amount_bucket = round(expense.amount / 10) * 10  # Group by 10 currency units
        key = f"{normalized_desc}_{amount_bucket}_{expense.category_id}"
        
        patterns[key].append(expense)
    
    suggestions = []
    
    # Analyze patterns
    for key, expense_list in patterns.items():
        if len(expense_list) < 3:  # Need at least 3 occurrences
            continue
        
        # Calculate intervals between expenses
        intervals = []
        for i in range(1, len(expense_list)):
            days_diff = (expense_list[i].date - expense_list[i-1].date).days
            intervals.append(days_diff)
        
        if not intervals:
            continue
        
        avg_interval = sum(intervals) / len(intervals)
        # Check variance to ensure consistency
        variance = sum((x - avg_interval) ** 2 for x in intervals) / len(intervals)
        std_dev = variance ** 0.5
        
        # Determine if pattern is consistent
        if std_dev / avg_interval > 0.3:  # More than 30% variance
            continue
        
        # Determine frequency
        frequency = None
        day_of_period = None
        confidence = 0
        
        if 25 <= avg_interval <= 35:  # Monthly
            frequency = 'monthly'
            # Get most common day of month
            days = [e.date.day for e in expense_list]
            day_of_period = max(set(days), key=days.count)
            confidence = 90 - (std_dev / avg_interval * 100)
        elif 6 <= avg_interval <= 8:  # Weekly
            frequency = 'weekly'
            days = [e.date.weekday() for e in expense_list]
            day_of_period = max(set(days), key=days.count)
            confidence = 85 - (std_dev / avg_interval * 100)
        elif 360 <= avg_interval <= 370:  # Yearly
            frequency = 'yearly'
            confidence = 80 - (std_dev / avg_interval * 100)
        
        if frequency and confidence > 60:  # Only suggest if confidence > 60%
            # Use most recent expense data
            latest = expense_list[-1]
            avg_amount = sum(e.amount for e in expense_list) / len(expense_list)
            
            # Check if already exists as recurring expense
            existing = RecurringExpense.query.filter_by(
                user_id=current_user.id,
                name=latest.description,
                category_id=latest.category_id
            ).first()
            
            if not existing:
                suggestions.append({
                    'name': latest.description,
                    'amount': round(avg_amount, 2),
                    'currency': latest.currency,
                    'category_id': latest.category_id,
                    'category_name': latest.category.name,
                    'category_color': latest.category.color,
                    'frequency': frequency,
                    'day_of_period': day_of_period,
                    'confidence_score': round(confidence, 1),
                    'occurrences': len(expense_list),
                    'detected': True
                })
    
    # Sort by confidence score
    suggestions.sort(key=lambda x: x['confidence_score'], reverse=True)
    
    return jsonify({
        'suggestions': suggestions[:10],  # Return top 10
        'message': f'Found {len(suggestions)} potential recurring expenses'
    })


@bp.route('/accept-suggestion', methods=['POST'])
@login_required
def accept_suggestion():
    """Accept a detected recurring expense suggestion and create it"""
    data = request.get_json()
    
    if not data or not data.get('name') or not data.get('amount') or not data.get('category_id') or not data.get('frequency'):
        return jsonify({'success': False, 'message': 'Missing required fields'}), 400
    
    # Security: Verify category belongs to current user
    category = Category.query.filter_by(id=int(data.get('category_id')), user_id=current_user.id).first()
    if not category:
        return jsonify({'success': False, 'message': 'Invalid category'}), 400
    
    # Calculate next due date
    day_of_period = data.get('day_of_period')
    next_due_date = calculate_next_due_date(data.get('frequency'), day_of_period)
    
    # Create recurring expense
    recurring = RecurringExpense(
        name=data.get('name'),
        amount=float(data.get('amount')),
        currency=data.get('currency', current_user.currency),
        category_id=int(data.get('category_id')),
        frequency=data.get('frequency'),
        day_of_period=day_of_period,
        next_due_date=next_due_date,
        auto_create=data.get('auto_create', False),
        is_active=True,
        detected=True,  # Auto-detected
        confidence_score=data.get('confidence_score', 0),
        user_id=current_user.id
    )
    
    db.session.add(recurring)
    db.session.commit()
    
    return jsonify({
        'success': True,
        'recurring_expense': recurring.to_dict()
    }), 201


@bp.route('/upcoming', methods=['GET'])
@login_required
def get_upcoming_recurring():
    """Get upcoming recurring expenses (next 30 days)"""
    # Security: Filter by user_id
    thirty_days_later = datetime.utcnow() + timedelta(days=30)
    
    recurring = RecurringExpense.query.filter(
        RecurringExpense.user_id == current_user.id,
        RecurringExpense.is_active == True,
        RecurringExpense.next_due_date <= thirty_days_later
    ).order_by(RecurringExpense.next_due_date.asc()).all()
    
    return jsonify({
        'upcoming': [r.to_dict() for r in recurring]
    })


@bp.route('/process-due', methods=['POST'])
@login_required
def process_due_manual():
    """
    Manually trigger processing of due recurring expenses
    Admin only for security - prevents users from spamming expense creation
    """
    if not current_user.is_admin:
        return jsonify({'success': False, 'message': 'Unauthorized'}), 403
    
    try:
        from app.scheduler import process_due_recurring_expenses
        process_due_recurring_expenses()
        return jsonify({
            'success': True,
            'message': 'Recurring expenses processed successfully'
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'message': f'Error processing recurring expenses: {str(e)}'
        }), 500


@bp.route('/sync-currency', methods=['POST'])
@login_required
def sync_currency():
    """
    Sync all user's recurring expenses to use their current profile currency
    Security: Only updates current user's recurring expenses
    """
    try:
        # Update all recurring expenses to match user's current currency
        RecurringExpense.query.filter_by(user_id=current_user.id).update(
            {'currency': current_user.currency}
        )
        db.session.commit()
        
        return jsonify({
            'success': True,
            'message': 'All recurring expenses synced to your current currency'
        })
    except Exception as e:
        db.session.rollback()
        return jsonify({
            'success': False,
            'message': f'Error syncing currency: {str(e)}'
        }), 500
