"""
✅ COMPLETE FIXED LOGIN API
- Proper token type handling
- Refresh token support
- OTP password reset
- Rate limiting & security
"""

from flask import Blueprint, request, jsonify, current_app
from flask_mail import Mail, Message
from flask_jwt_extended import (
    create_access_token, 
    create_refresh_token, 
    jwt_required, 
    get_jwt_identity, 
    get_jwt
)
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import datetime, timedelta
import time
import random
import re
import traceback

from db.db import get_db_connection

# ============================================================
# SECURITY CONFIGURATION
# ============================================================
failed_attempts = {}
ip_requests = {}

MAX_ATTEMPTS = 5
LOCK_TIME = timedelta(minutes=15)
RATE_LIMIT_WINDOW = timedelta(minutes=1)
MAX_REQUESTS_PER_MIN = 10

# ============================================================
# BLUEPRINT SETUP
# ============================================================
login_bp = Blueprint('login', __name__)
mail = Mail()
otp_store = {}


# ============================================================
# ✅ FIXED LOGIN ENDPOINT
# ============================================================
@login_bp.route('/login', methods=['POST'])
def login():
    """
    User login with proper token generation
    Returns both access and refresh tokens
    """
    
    # Validate Content-Type
    if not request.is_json:
        return jsonify({'message': 'Content-Type must be application/json'}), 415

    data = request.get_json()
    email = data.get('email', '').strip()
    password = data.get('password', '')
    user_type = data.get('userType')  # Optional role filter

    # Input validation
    if not email or not password:
        return jsonify({'message': 'Email and password are required'}), 400

    now = datetime.now()
    ip = request.remote_addr

    # -------------------- RATE LIMITING --------------------
    if ip not in ip_requests:
        ip_requests[ip] = []
    
    # Clean old requests
    ip_requests[ip] = [t for t in ip_requests[ip] if now - t < RATE_LIMIT_WINDOW]
    
    if len(ip_requests[ip]) >= MAX_REQUESTS_PER_MIN:
        return jsonify({'message': 'Too many requests. Please try again later.'}), 429
    
    ip_requests[ip].append(now)

    # -------------------- ACCOUNT LOCK CHECK --------------------
    if email in failed_attempts:
        attempts = failed_attempts[email]
        if attempts["count"] >= MAX_ATTEMPTS:
            if now - attempts["last_attempt"] < LOCK_TIME:
                remaining = LOCK_TIME - (now - attempts["last_attempt"])
                minutes = int(remaining.total_seconds() / 60)
                return jsonify({
                    'message': f'Account locked. Try again in {minutes} minutes.'
                }), 429
            else:
                # Reset after lock time expires
                failed_attempts[email] = {"count": 0, "last_attempt": now}

    # -------------------- DATABASE CONNECTION --------------------
    conn = get_db_connection()
    if not conn:
        return jsonify({'message': 'Database connection failed'}), 500

    try:
        cursor = conn.cursor(dictionary=True)
        
        # Get user data with store info
        cursor.execute("""
            SELECT 
                u.id, 
                u.name, 
                u.email, 
                u.password, 
                u.role, 
                u.image,
                u.is_active,
                u.store_id,
                s.store_name
            FROM users u
            LEFT JOIN stores s ON u.store_id = s.id
            WHERE u.email = %s
        """, (email,))
        
        user = cursor.fetchone()

        # -------------------- USER VALIDATION --------------------
        if not user:
            return jsonify({'message': 'Invalid email or password'}), 401

        # Check if account is active
        if not user.get('is_active', 1):
            return jsonify({'message': 'Account is deactivated. Contact administrator.'}), 403

        # -------------------- PASSWORD VERIFICATION --------------------
        if not check_password_hash(user['password'], password):
            # Increment failed attempts
            if email not in failed_attempts:
                failed_attempts[email] = {"count": 1, "last_attempt": now}
            else:
                failed_attempts[email]["count"] += 1
                failed_attempts[email]["last_attempt"] = now
            
            remaining_attempts = MAX_ATTEMPTS - failed_attempts[email]["count"]
            if remaining_attempts > 0:
                return jsonify({
                    'message': f'Invalid email or password. {remaining_attempts} attempts remaining.'
                }), 401
            else:
                return jsonify({
                    'message': 'Account locked due to too many failed attempts. Try again later.'
                }), 429

        # -------------------- ROLE VALIDATION (OPTIONAL) --------------------
        if user_type and user['role'] != user_type:
            return jsonify({
                'message': f'Invalid credentials for {user_type} login'
            }), 403

        # -------------------- SUCCESS - RESET FAILED ATTEMPTS --------------------
        if email in failed_attempts:
            failed_attempts[email] = {"count": 0, "last_attempt": now}

        # -------------------- TOKEN GENERATION --------------------
        user_id = user['id']
        
        # ✅ Common claims for both tokens
        common_claims = {
            'user_id': user_id,
            'name': user['name'],
            'email': user['email'],
            'role': user['role'],
            'store_id': user.get('store_id'),
            'store_name': user.get('store_name'),
            'image': user.get('image')
        }
        
        # ✅ Access token (1 hour) - for API calls
        access_token = create_access_token(
            identity=str(user_id),
            additional_claims=common_claims,
            fresh=True
        )
        
        # ✅ Refresh token (24 hours) - for getting new access tokens
        refresh_token = create_refresh_token(
            identity=str(user_id),
            additional_claims={'role': user['role']}  # Minimal claims for security
        )
        
        # -------------------- LOGGING --------------------
        print(f"\n{'='*60}")
        print(f"✅ LOGIN SUCCESS")
        print(f"{'='*60}")
        print(f"User: {user['name']} (ID: {user_id})")
        print(f"Email: {user['email']}")
        print(f"Role: {user['role']}")
        print(f"Store: {user.get('store_name', 'N/A')} (ID: {user.get('store_id', 'N/A')})")
        print(f"IP: {ip}")
        print(f"Time: {now.strftime('%Y-%m-%d %H:%M:%S')}")
        print(f"{'='*60}\n")

        # -------------------- RESPONSE --------------------
        return jsonify({
            'success': True,
            'message': 'Login successful',
            'access_token': access_token,
            'refresh_token': refresh_token,
            'token': access_token,  # ✅ Backward compatibility
            'expires_in': 3600,  # 1 hour in seconds
            'user': {
                'id': user_id,
                'name': user['name'],
                'email': user['email'],
                'role': user['role'],
                'store_id': user.get('store_id'),
                'store_name': user.get('store_name'),
                'image': user.get('image')
            }
        }), 200

    except Exception as e:
        print(f"\n{'='*60}")
        print(f"❌ LOGIN ERROR")
        print(f"{'='*60}")
        print(f"Error: {str(e)}")
        traceback.print_exc()
        print(f"{'='*60}\n")
        return jsonify({'message': 'Internal server error'}), 500

    finally:
        cursor.close()
        conn.close()


# ============================================================
# ✅ REFRESH TOKEN ENDPOINT
# ============================================================
@login_bp.route('/refresh', methods=['POST'])
@jwt_required(refresh=True)  # ✅ Requires refresh token
def refresh():
    """
    Generate new access token using refresh token
    """
    try:
        print(f"\n{'='*60}")
        print(f"🔄 TOKEN REFRESH REQUEST")
        print(f"{'='*60}")
        
        # Get user identity from refresh token
        current_user = get_jwt_identity()
        claims = get_jwt()
        
        print(f"User ID: {current_user}")
        print(f"Role: {claims.get('role')}")
        
        # ✅ Get fresh user data from database
        conn = get_db_connection()
        if not conn:
            return jsonify({'error': 'Database connection failed'}), 500
        
        cursor = conn.cursor(dictionary=True)
        
        try:
            cursor.execute("""
                SELECT 
                    u.id, 
                    u.name, 
                    u.email, 
                    u.role, 
                    u.image,
                    u.is_active,
                    u.store_id,
                    s.store_name
                FROM users u
                LEFT JOIN stores s ON u.store_id = s.id
                WHERE u.id = %s
            """, (current_user,))
            
            user = cursor.fetchone()
            
            if not user:
                print(f"❌ User not found: {current_user}")
                return jsonify({'error': 'User not found'}), 404
            
            # Check if user is still active
            if not user.get('is_active', 1):
                print(f"❌ User deactivated: {current_user}")
                return jsonify({'error': 'Account is deactivated'}), 403
            
            # ✅ Create new access token with fresh data
            new_access_token = create_access_token(
                identity=str(user['id']),
                additional_claims={
                    'user_id': user['id'],
                    'name': user['name'],
                    'email': user['email'],
                    'role': user['role'],
                    'store_id': user.get('store_id'),
                    'store_name': user.get('store_name'),
                    'image': user.get('image')
                },
                fresh=False  # Not a fresh login
            )
            
            print(f"✅ New access token generated")
            print(f"User: {user['name']}")
            print(f"{'='*60}\n")
            
            return jsonify({
                'access_token': new_access_token,
                'expires_in': 3600,
                'message': 'Token refreshed successfully'
            }), 200
            
        finally:
            cursor.close()
            conn.close()
            
    except Exception as e:
        print(f"\n{'='*60}")
        print(f"❌ REFRESH ERROR")
        print(f"{'='*60}")
        print(f"Error: {str(e)}")
        traceback.print_exc()
        print(f"{'='*60}\n")
        
        return jsonify({
            'error': 'Token refresh failed',
            'message': 'Invalid or expired refresh token'
        }), 401


# ============================================================
# ✅ VERIFY TOKEN ENDPOINT
# ============================================================
@login_bp.route('/verify-token', methods=['GET'])
@jwt_required()
def verify_token():
    """
    Verify if current access token is valid
    """
    try:
        current_user = get_jwt_identity()
        claims = get_jwt()
        
        return jsonify({
            'valid': True,
            'user_id': claims.get('user_id'),
            'name': claims.get('name'),
            'email': claims.get('email'),
            'role': claims.get('role'),
            'store_id': claims.get('store_id'),
            'expires_at': claims.get('exp')
        }), 200
        
    except Exception as e:
        return jsonify({
            'valid': False,
            'error': str(e)
        }), 401


# ============================================================
# ✅ LOGOUT ENDPOINT
# ============================================================
@login_bp.route('/logout', methods=['POST'])
@jwt_required()
def logout():
    """
    Logout endpoint
    In production, add token to blacklist here
    """
    try:
        current_user = get_jwt_identity()
        claims = get_jwt()
        
        print(f"\n🚪 LOGOUT: {claims.get('name')} (ID: {current_user})\n")
        
        # TODO: Add token to blacklist/revocation list in production
        
        return jsonify({
            'success': True,
            'message': 'Logged out successfully'
        }), 200
        
    except Exception as e:
        return jsonify({
            'success': False,
            'message': 'Logout failed',
            'error': str(e)
        }), 500


# ============================================================
# OTP PASSWORD RESET ENDPOINTS
# ============================================================

@login_bp.route('/send-otp', methods=['POST'])
def send_otp():
    """
    Send OTP to user's email for password reset
    """
    data = request.get_json()
    email = data.get('email', '').strip()

    if not email:
        return jsonify({'message': 'Email is required'}), 400

    # Validate email format
    if not re.match(r"[^@]+@[^@]+\.[^@]+", email):
        return jsonify({'message': 'Invalid email format'}), 400

    # Check if user exists
    conn = get_db_connection()
    if not conn:
        return jsonify({'message': 'Database connection failed'}), 500

    try:
        cursor = conn.cursor(dictionary=True)
        cursor.execute("SELECT id, name, is_active FROM users WHERE email = %s", (email,))
        user = cursor.fetchone()
        
        if not user:
            return jsonify({'message': 'Email not found'}), 404
        
        if not user.get('is_active', 1):
            return jsonify({'message': 'Account is deactivated'}), 403
        
        user_id = user['id']
        user_name = user['name']
        
        # Generate 6-digit OTP
        otp = random.randint(100000, 999999)
        
        # Store OTP with 5-minute expiry
        otp_store[email] = {
            'otp': otp,
            'expires_at': time.time() + 300  # 5 minutes
        }
        
        # Email template
        html_template = f"""
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Password Reset OTP</title>
        </head>
        <body style="margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f4f7fa;">
            <table width="100%" cellpadding="0" cellspacing="0" style="background-color: #f4f7fa; padding: 40px 0;">
                <tr>
                    <td align="center">
                        <table width="600" cellpadding="0" cellspacing="0" style="background-color: #ffffff; border-radius: 16px; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.1);">
                            <tr>
                                <td style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 40px 30px; text-align: center;">
                                    <h1 style="color: #ffffff; margin: 0; font-size: 28px; font-weight: 600;">🔐 Abesekara POS</h1>
                                    <p style="color: #e8e8ff; margin: 10px 0 0; font-size: 14px;">Password Reset Verification</p>
                                </td>
                            </tr>
                            <tr>
                                <td style="padding: 40px 30px;">
                                    <h2 style="color: #333333; margin: 0 0 20px; font-size: 22px;">Hello {user_name}! 👋</h2>
                                    <p style="color: #666666; font-size: 16px; line-height: 1.6; margin: 0 0 30px;">
                                        We received a request to reset your password. Use the verification code below to proceed:
                                    </p>
                                    <table width="100%" cellpadding="0" cellspacing="0">
                                        <tr>
                                            <td align="center" style="padding: 20px 0;">
                                                <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 12px; padding: 3px; display: inline-block;">
                                                    <div style="background-color: #ffffff; border-radius: 10px; padding: 25px 50px;">
                                                        <p style="margin: 0; color: #999999; font-size: 12px; text-transform: uppercase; letter-spacing: 1px; font-weight: 600;">Your OTP Code</p>
                                                        <p style="margin: 10px 0 0; font-size: 42px; font-weight: 700; letter-spacing: 8px; color: #667eea; font-family: 'Courier New', monospace;">{otp}</p>
                                                    </div>
                                                </div>
                                            </td>
                                        </tr>
                                    </table>
                                    <table width="100%" cellpadding="0" cellspacing="0" style="margin: 30px 0;">
                                        <tr>
                                            <td style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 15px 20px; border-radius: 8px;">
                                                <p style="margin: 0; color: #856404; font-size: 14px; line-height: 1.5;">
                                                    ⏰ <strong>This code will expire in 5 minutes.</strong><br>Please use it as soon as possible.
                                                </p>
                                            </td>
                                        </tr>
                                    </table>
                                    <p style="color: #666666; font-size: 14px; line-height: 1.6; margin: 30px 0 0;">
                                        If you didn't request this password reset, please ignore this email or contact support.
                                    </p>
                                </td>
                            </tr>
                            <tr>
                                <td style="background-color: #f8f9fa; padding: 30px; text-align: center; border-top: 1px solid #e9ecef;">
                                    <p style="margin: 0 0 10px; color: #999999; font-size: 12px;">This is an automated message, please do not reply.</p>
                                    <p style="margin: 0; color: #999999; font-size: 12px;">© 2025 Abesekara POS. All rights reserved.</p>
                                </td>
                            </tr>
                        </table>
                    </td>
                </tr>
            </table>
        </body>
        </html>
        """
        
        # Send email
        try:
            msg = Message(
                "🔐 Your Password Reset OTP",
                sender=current_app.config['MAIL_USERNAME'],
                recipients=[email]
            )
            msg.html = html_template
            msg.body = f"Hello {user_name},\n\nYour OTP code: {otp}\n\nExpires in 5 minutes.\n\nIf you didn't request this, please ignore this email."
            
            mail.send(msg)
            
            print(f"✅ OTP sent to {email}: {otp}")
            
            return jsonify({
                'success': True,
                'message': 'OTP sent successfully! Check your email.'
            }), 200
            
        except Exception as e:
            print(f"❌ Mail error: {e}")
            traceback.print_exc()
            return jsonify({
                'message': f'Failed to send OTP. Please try again later.'
            }), 500
            
    finally:
        cursor.close()
        conn.close()


@login_bp.route('/verify-otp', methods=['POST'])
def verify_otp():
    """
    Verify OTP code
    """
    data = request.get_json()
    email = data.get('email', '').strip()
    otp = data.get('otp', '').strip()

    if not email or not otp:
        return jsonify({'message': 'Email and OTP are required'}), 400

    if email not in otp_store:
        return jsonify({'message': 'No OTP found for this email'}), 404

    stored_data = otp_store[email]
    
    # Check expiry
    if time.time() > stored_data['expires_at']:
        del otp_store[email]
        return jsonify({'message': 'OTP has expired. Please request a new one.'}), 400
    
    # Verify OTP
    if str(otp) == str(stored_data['otp']):
        # Don't delete OTP yet - keep it for password reset
        return jsonify({
            'success': True,
            'message': 'OTP verified successfully'
        }), 200
    else:
        return jsonify({'message': 'Invalid OTP'}), 400


@login_bp.route('/reset-password', methods=['POST'])
def reset_password():
    """
    Reset password after OTP verification
    """
    data = request.get_json()
    email = data.get('email', '').strip()
    new_password = data.get('new_password', '').strip()
    otp = data.get('otp', '').strip()

    if not email or not new_password or not otp:
        return jsonify({'message': 'Email, OTP, and new password are required'}), 400

    # Validate password strength
    if len(new_password) < 6:
        return jsonify({'message': 'Password must be at least 6 characters long'}), 400

    # Verify OTP one more time
    if email not in otp_store:
        return jsonify({'message': 'OTP not found or expired'}), 404
    
    stored_data = otp_store[email]
    
    if time.time() > stored_data['expires_at']:
        del otp_store[email]
        return jsonify({'message': 'OTP has expired'}), 400
    
    if str(otp) != str(stored_data['otp']):
        return jsonify({'message': 'Invalid OTP'}), 400

    # Hash new password
    hashed_password = generate_password_hash(new_password)

    # Update password in database
    conn = get_db_connection()
    if not conn:
        return jsonify({'message': 'Database connection failed'}), 500

    try:
        cursor = conn.cursor()
        cursor.execute(
            "UPDATE users SET password = %s WHERE email = %s", 
            (hashed_password, email)
        )
        conn.commit()
        
        # Clear OTP after successful reset
        if email in otp_store:
            del otp_store[email]
        
        # Clear failed login attempts
        if email in failed_attempts:
            del failed_attempts[email]
        
        print(f"✅ Password reset successful for {email}")
        
        return jsonify({
            'success': True,
            'message': 'Password reset successfully! You can now login with your new password.'
        }), 200
        
    except Exception as e:
        print(f"❌ Password reset error: {e}")
        traceback.print_exc()
        return jsonify({'message': 'Failed to reset password'}), 500
        
    finally:
        cursor.close()
        conn.close()