import logging
from datetime import datetime, timedelta
from flask import request, jsonify, render_template, redirect, url_for, flash, session, current_app
from flask_login import login_user, logout_user, login_required, current_user
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity, get_jwt
from werkzeug.security import check_password_hash
from app.auth import bp
from app import db
from app.auth.forms import LoginForm, RegistrationForm
from app.utils.security import validate_password_strength, generate_secure_token, rate_limit_check
from app.utils.logging import log_security_event

logger = logging.getLogger(__name__)

@bp.route('/login', methods=['GET', 'POST'])
def login():
    """User login endpoint"""
    if current_user.is_authenticated:
        return redirect(url_for('main.dashboard'))
    
    form = LoginForm()
    
    if form.validate_on_submit():
        # Rate limiting check
        client_ip = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
        if not rate_limit_check(client_ip, 'login', max_attempts=5, window_minutes=15):
            log_security_event('LOGIN_RATE_LIMIT', client_ip, username=form.username.data)
            flash('Too many login attempts. Please try again later.', 'error')
            return render_template('auth/login.html', form=form)
        
        from app.models import User, UserSession
        user = User.query.filter_by(username=form.username.data).first()
        
        if user and user.check_password(form.password.data):
            if not user.is_active:
                log_security_event('LOGIN_INACTIVE_USER', client_ip, user_id=user.id)
                flash('Your account has been deactivated.', 'error')
                return render_template('auth/login.html', form=form)
            
            # Update last login
            user.update_last_login()
            
            # Create user session
            user_session = UserSession(
                user_id=user.id,
                ip_address=client_ip,
                user_agent=request.headers.get('User-Agent', '')
            )
            db.session.add(user_session)
            db.session.commit()
            
            # Login user
            login_user(user, remember=form.remember_me.data)
            session['session_id'] = user_session.session_id
            
            log_security_event('LOGIN_SUCCESS', client_ip, user_id=user.id)
            
            # Redirect to next page or dashboard
            next_page = request.args.get('next')
            if not next_page or not next_page.startswith('/'):
                next_page = url_for('main.dashboard')
            
            return redirect(next_page)
        else:
            log_security_event('LOGIN_FAILED', client_ip, username=form.username.data)
            flash('Invalid username or password', 'error')
    
    return render_template('auth/login.html', form=form)

@bp.route('/logout')
@login_required
def logout():
    """User logout endpoint"""
    client_ip = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
    
    # Deactivate user session
    if 'session_id' in session:
        from app.models import UserSession
        user_session = UserSession.query.filter_by(session_id=session['session_id']).first()
        if user_session:
            user_session.deactivate()
    
    log_security_event('LOGOUT', client_ip, user_id=current_user.id)
    logout_user()
    session.clear()
    
    flash('You have been logged out successfully.', 'info')
    return redirect(url_for('auth.login'))

@bp.route('/register', methods=['GET', 'POST'])
def register():
    """User registration endpoint (admin only in production)"""
    if current_app.config.get('REGISTRATION_DISABLED', False):
        flash('Registration is disabled.', 'error')
        return redirect(url_for('auth.login'))
    
    if current_user.is_authenticated:
        return redirect(url_for('main.dashboard'))
    
    form = RegistrationForm()
    
    if form.validate_on_submit():
        client_ip = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
        
        # Rate limiting check
        if not rate_limit_check(client_ip, 'register', max_attempts=3, window_minutes=60):
            log_security_event('REGISTER_RATE_LIMIT', client_ip, username=form.username.data)
            flash('Too many registration attempts. Please try again later.', 'error')
            return render_template('auth/register.html', form=form)
        
        # Check if user already exists
        from app.models import User
        if User.query.filter_by(username=form.username.data).first():
            flash('Username already exists.', 'error')
            return render_template('auth/register.html', form=form)
        
        if User.query.filter_by(email=form.email.data).first():
            flash('Email already registered.', 'error')
            return render_template('auth/register.html', form=form)
        
        # Validate password strength
        if not validate_password_strength(form.password.data):
            flash('Password does not meet security requirements.', 'error')
            return render_template('auth/register.html', form=form)
        
        # Create new user
        user = User(
            username=form.username.data,
            email=form.email.data,
            is_active=True
        )
        user.set_password(form.password.data)
        
        db.session.add(user)
        db.session.commit()
        
        log_security_event('USER_REGISTERED', client_ip, user_id=user.id)
        flash('Registration successful! You can now log in.', 'success')
        return redirect(url_for('auth.login'))
    
    return render_template('auth/register.html', form=form)

@bp.route('/api/login', methods=['POST'])
def api_login():
    """API login endpoint for JWT authentication"""
    try:
        data = request.get_json()
        if not data or not data.get('username') or not data.get('password'):
            return jsonify({'error': 'Username and password required'}), 400
        
        client_ip = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
        
        # Rate limiting check
        if not rate_limit_check(client_ip, 'api_login', max_attempts=10, window_minutes=15):
            log_security_event('API_LOGIN_RATE_LIMIT', client_ip, username=data.get('username'))
            return jsonify({'error': 'Too many login attempts'}), 429
        
        from app.models import User, UserSession
        user = User.query.filter_by(username=data['username']).first()
        
        if user and user.check_password(data['password']):
            if not user.is_active:
                log_security_event('API_LOGIN_INACTIVE_USER', client_ip, user_id=user.id)
                return jsonify({'error': 'Account deactivated'}), 403
            
            # Update last login
            user.update_last_login()
            
            # Create access token
            access_token = create_access_token(
                identity=user.id,
                expires_delta=timedelta(hours=current_app.config['JWT_ACCESS_TOKEN_EXPIRES'] // 3600)
            )
            
            # Create user session
            user_session = UserSession(
                user_id=user.id,
                ip_address=client_ip,
                user_agent=request.headers.get('User-Agent', '')
            )
            db.session.add(user_session)
            db.session.commit()
            
            log_security_event('API_LOGIN_SUCCESS', client_ip, user_id=user.id)
            
            return jsonify({
                'access_token': access_token,
                'user': user.to_dict(),
                'session_id': user_session.session_id
            }), 200
        else:
            log_security_event('API_LOGIN_FAILED', client_ip, username=data.get('username'))
            return jsonify({'error': 'Invalid credentials'}), 401
    
    except Exception as e:
        logger.error(f"API login error: {str(e)}")
        return jsonify({'error': 'Internal server error'}), 500

@bp.route('/api/logout', methods=['POST'])
@jwt_required()
def api_logout():
    """API logout endpoint"""
    try:
        user_id = get_jwt_identity()
        client_ip = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
        
        # Get session ID from request
        data = request.get_json() or {}
        session_id = data.get('session_id')
        
        if session_id:
            from app.models import UserSession
            user_session = UserSession.query.filter_by(
                session_id=session_id,
                user_id=user_id
            ).first()
            if user_session:
                user_session.deactivate()
        
        log_security_event('API_LOGOUT', client_ip, user_id=user_id)
        
        return jsonify({'message': 'Logged out successfully'}), 200
    
    except Exception as e:
        logger.error(f"API logout error: {str(e)}")
        return jsonify({'error': 'Internal server error'}), 500

@bp.route('/api/refresh', methods=['POST'])
@jwt_required()
def api_refresh():
    """Refresh JWT token"""
    try:
        user_id = get_jwt_identity()
        from app.models import User
        user = User.query.get(user_id)
        
        if not user or not user.is_active:
            return jsonify({'error': 'User not found or inactive'}), 404
        
        # Create new access token
        access_token = create_access_token(
            identity=user.id,
            expires_delta=timedelta(hours=current_app.config['JWT_ACCESS_TOKEN_EXPIRES'] // 3600)
        )
        
        return jsonify({
            'access_token': access_token,
            'user': user.to_dict()
        }), 200
    
    except Exception as e:
        logger.error(f"Token refresh error: {str(e)}")
        return jsonify({'error': 'Internal server error'}), 500

@bp.route('/api/profile', methods=['GET'])
@jwt_required()
def api_profile():
    """Get user profile"""
    try:
        user_id = get_jwt_identity()
        from app.models import User
        user = User.query.get(user_id)
        
        if not user:
            return jsonify({'error': 'User not found'}), 404
        
        return jsonify({'user': user.to_dict()}), 200
    
    except Exception as e:
        logger.error(f"Profile fetch error: {str(e)}")
        return jsonify({'error': 'Internal server error'}), 500

@bp.route('/api/change-password', methods=['POST'])
@jwt_required()
def api_change_password():
    """Change user password"""
    try:
        user_id = get_jwt_identity()
        from app.models import User
        user = User.query.get(user_id)
        
        if not user:
            return jsonify({'error': 'User not found'}), 404
        
        data = request.get_json()
        if not data or not data.get('current_password') or not data.get('new_password'):
            return jsonify({'error': 'Current and new password required'}), 400
        
        # Verify current password
        if not user.check_password(data['current_password']):
            client_ip = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
            log_security_event('PASSWORD_CHANGE_FAILED', client_ip, user_id=user.id)
            return jsonify({'error': 'Current password incorrect'}), 400
        
        # Validate new password strength
        if not validate_password_strength(data['new_password']):
            return jsonify({'error': 'New password does not meet security requirements'}), 400
        
        # Update password
        user.set_password(data['new_password'])
        user.updated_at = datetime.utcnow()
        db.session.commit()
        
        client_ip = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
        log_security_event('PASSWORD_CHANGED', client_ip, user_id=user.id)
        
        return jsonify({'message': 'Password changed successfully'}), 200
    
    except Exception as e:
        logger.error(f"Password change error: {str(e)}")
        return jsonify({'error': 'Internal server error'}), 500

@bp.route('/api/sessions', methods=['GET'])
@jwt_required()
def api_user_sessions():
    """Get user's active sessions"""
    try:
        user_id = get_jwt_identity()
        from app.models import UserSession
        sessions = UserSession.query.filter_by(
            user_id=user_id,
            is_active=True
        ).order_by(UserSession.last_activity.desc()).all()
        
        return jsonify({
            'sessions': [session.to_dict() for session in sessions]
        }), 200
    
    except Exception as e:
        logger.error(f"Sessions fetch error: {str(e)}")
        return jsonify({'error': 'Internal server error'}), 500

@bp.route('/api/sessions/<session_id>', methods=['DELETE'])
@jwt_required()
def api_terminate_session(session_id):
    """Terminate a specific session"""
    try:
        user_id = get_jwt_identity()
        from app.models import UserSession
        user_session = UserSession.query.filter_by(
            session_id=session_id,
            user_id=user_id
        ).first()
        
        if not user_session:
            return jsonify({'error': 'Session not found'}), 404
        
        user_session.deactivate()
        
        client_ip = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
        log_security_event('SESSION_TERMINATED', client_ip, user_id=user_id, 
                          extra_data={'terminated_session_id': session_id})
        
        return jsonify({'message': 'Session terminated successfully'}), 200
    
    except Exception as e:
        logger.error(f"Session termination error: {str(e)}")
        return jsonify({'error': 'Internal server error'}), 500