from flask import Blueprint, request, jsonify
from flask_jwt_extended import create_access_token, jwt_required
from app.db import get_db_connection
from app.auth import get_password_hash, verify_password, get_current_user
from app.email_service import send_password_reset_email
from app.utils.logger import get_logger
from datetime import datetime, timedelta, timezone
import secrets
import uuid

auth_bp = Blueprint('auth', __name__)
logger = get_logger(__name__)

# Importar limiter desde app.__init__ cuando esté disponible
def get_limiter():
    """Obtiene el limiter global si está disponible."""
    try:
        from app import limiter
        return limiter
    except:
        return None

@auth_bp.route('/register', methods=['POST'])
def register():
    """Register a new user with complete information."""
    try:
        data = request.get_json()
        if not data:
            return jsonify({'error': 'Request body is required'}), 400
        
        # Extraer todos los campos requeridos
        username = data.get('username') or data.get('nombre_usuario')
        full_name = data.get('full_name') or data.get('nombre_completo')
        email = data.get('email') or data.get('correo')
        phone = data.get('phone') or data.get('celular')
        user_type = data.get('user_type') or data.get('role', 'customer')  # 'customer' o 'business'
        password = data.get('password') or data.get('contraseña')
        confirm_password = data.get('confirm_password') or data.get('confirmar_contraseña')
        
        # Validar campos requeridos
        from app.utils.validators import (
            validate_email, validate_gmail, validate_length, validate_role,
            validate_phone, validate_username, validate_text
        )
        
        # Validar username
        if not username:
            return jsonify({'error': 'Username is required'}), 400
        username = username.strip()
        if not validate_username(username):
            return jsonify({'error': 'Username must be 3-30 characters, alphanumeric with underscores or hyphens only'}), 400
        
        # Validar nombre completo
        if not full_name:
            return jsonify({'error': 'Full name is required'}), 400
        full_name = full_name.strip()
        if not validate_text(full_name):
            return jsonify({'error': 'Full name contains invalid characters'}), 400
        if not validate_length(full_name, min_length=2, max_length=255):
            return jsonify({'error': 'Full name must be between 2 and 255 characters'}), 400
        
        # Validar email (debe ser Gmail)
        if not email:
            return jsonify({'error': 'Email is required'}), 400
        email = email.lower().strip()
        if not validate_gmail(email):
            return jsonify({'error': 'Email must be a valid Gmail address (@gmail.com)'}), 400
        if not validate_length(email, max_length=255):
            return jsonify({'error': 'Email is too long (max 255 characters)'}), 400
        
        # Validar celular
        if not phone:
            return jsonify({'error': 'Phone number is required'}), 400
        phone = phone.strip()
        if not validate_phone(phone):
            return jsonify({'error': 'Phone number format is invalid'}), 400
        if not validate_length(phone, min_length=7, max_length=20):
            return jsonify({'error': 'Phone number must be between 7 and 20 characters'}), 400
        
        # Validar tipo de usuario (solo customer o business en registro)
        if user_type not in ['customer', 'business']:
            return jsonify({'error': 'User type must be either "customer" or "business"'}), 400
        
        # Validar contraseña
        if not password:
            return jsonify({'error': 'Password is required'}), 400
        if not validate_length(password, min_length=6, max_length=128):
            return jsonify({'error': 'Password must be between 6 and 128 characters'}), 400
        
        # Validar confirmación de contraseña
        if not confirm_password:
            return jsonify({'error': 'Password confirmation is required'}), 400
        if password != confirm_password:
            return jsonify({'error': 'Passwords do not match'}), 400
        
        conn = get_db_connection()
        if not conn:
            logger.error("Database connection error in register", extra={'username': username, 'email': email})
            return jsonify({'error': 'Database connection error'}), 500
        
        try:
            cursor = conn.cursor()
            
            # Verificar si el username ya existe
            cursor.execute("SELECT id FROM usuarios WHERE nombre_usuario = %s", (username,))
            existing_username = cursor.fetchone()
            if existing_username:
                cursor.close()
                conn.close()
                return jsonify({'error': 'Username already taken'}), 400
            
            # Verificar si el email ya existe
            cursor.execute("SELECT id FROM usuarios WHERE correo = %s", (email,))
            existing_email = cursor.fetchone()
            if existing_email:
                cursor.close()
                conn.close()
                return jsonify({'error': 'Email already registered'}), 400
            
            # Verificar si el celular ya existe (opcional, pero recomendado)
            cursor.execute("SELECT id FROM usuarios WHERE celular = %s", (phone,))
            existing_phone = cursor.fetchone()
            if existing_phone:
                cursor.close()
                conn.close()
                return jsonify({'error': 'Phone number already registered'}), 400
            
            # Crear nuevo usuario
            user_id = f"user-{username}-{uuid.uuid4().hex[:8]}"
            hash_contraseña = get_password_hash(password)
            
            # Si es 'business', se guarda como 'customer' inicialmente hasta que se apruebe la solicitud
            # El rol 'business-admin' se asigna después de aprobar la solicitud de negocio
            role_to_save = 'customer'
            
            cursor.execute("""
                INSERT INTO usuarios (id, nombre_usuario, nombre_completo, correo, celular, hash_contraseña, rol, creado_en)
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
            """, (user_id, username, full_name, email, phone, hash_contraseña, role_to_save, datetime.now(timezone.utc)))
            
            conn.commit()
            
            # Log change for audit
            from app.governance.audit import log_change
            new_user_data = {
                'id': user_id,
                'nombre_usuario': username,
                'nombre_completo': full_name,
                'correo': email,
                'celular': phone,
                'rol': role_to_save,
                'user_type_requested': user_type
            }
            log_change(table='usuarios', record_id=user_id, action='INSERT',
                      user_id=None, user_email=email, new_data=new_user_data)
            
            cursor.close()
            conn.close()
            
            response_data = {
                'id': user_id,
                'username': username,
                'full_name': full_name,
                'email': email,
                'phone': phone,
                'role': role_to_save,
                'user_type_requested': user_type,
                'business_id': None,
                'business_application_status': None
            }
            
            # Si el usuario solicitó ser negocio, informar que debe solicitar aprobación
            if user_type == 'business':
                response_data['message'] = 'Registration successful. To become a business, you need to request business approval.'
            
            logger.info(f"User registered successfully: {username} ({email})", extra={
                'user_id': user_id,
                'username': username,
                'email': email,
                'user_type': user_type
            })
            
            return jsonify(response_data), 201
        
        except Exception as create_error:
            conn.rollback()
            cursor.close()
            conn.close()
            logger.error(f"Error creating user: {str(create_error)}", exc_info=True, extra={
                'action': 'register',
                'username': username,
                'email': email
            })
            return jsonify({'error': 'Failed to create user. Please try again.'}), 500
    
    except Exception as e:
        logger.error(f"Unexpected error in register: {str(e)}", exc_info=True, extra={
            'action': 'register'
        })
        return jsonify({'error': 'Registration failed. Please try again.'}), 500

@auth_bp.route('/login', methods=['POST'])
def login():
    """Login and get access token. Accepts username or email."""
    try:
        data = request.get_json()
        
        if not data:
            return jsonify({'error': 'Request body is required'}), 400
        
        # Aceptar username, email, o username_or_email
        username_or_email = data.get('username') or data.get('email') or data.get('username_or_email')
        password = data.get('password')
        
        if not username_or_email or not password:
            return jsonify({'error': 'Username/Email and password are required'}), 400
        
        # Normalizar entrada
        username_or_email = username_or_email.strip()
        
        # Determinar si es un email o un username
        from app.utils.validators import validate_email, validate_length, validate_username
        
        is_email = '@' in username_or_email and validate_email(username_or_email.lower())
        is_username = not is_email and validate_username(username_or_email)
        
        if not is_email and not is_username:
            return jsonify({'error': 'Invalid username or email format'}), 400
        
        if not validate_length(password, min_length=1, max_length=128):
            return jsonify({'error': 'Invalid password format'}), 400
        
        conn = get_db_connection()
        if not conn:
            return jsonify({'error': 'Cannot connect to database server. Check WAMP/MySQL is running.'}), 500
        
        try:
            cursor = conn.cursor()
            
            # Buscar por email o username según corresponda
            if is_email:
                email_lower = username_or_email.lower()
                cursor.execute("""
                    SELECT id, nombre_usuario, correo, hash_contraseña, rol, negocio_id, estado_solicitud_negocio
                    FROM usuarios WHERE correo = %s
                """, (email_lower,))
            else:
                cursor.execute("""
                    SELECT id, nombre_usuario, correo, hash_contraseña, rol, negocio_id, estado_solicitud_negocio
                    FROM usuarios WHERE nombre_usuario = %s
                """, (username_or_email,))
            
            user_data = cursor.fetchone()
            cursor.close()
            conn.close()
            
            if not user_data:
                # Log failed login attempt
                from app.governance.audit import log_access
                login_identifier = username_or_email.lower() if is_email else username_or_email
                log_access(user_id=None, user_email=login_identifier if is_email else None, 
                          access_type='LOGIN_FAILED', successful=False,
                          error_message='User not found')
                return jsonify({'error': 'Incorrect username/email or password'}), 401
            
            # user_data es un diccionario (DictCursor), no una tupla
            user_id = user_data['id']
            user_username = user_data.get('nombre_usuario', '')
            user_email = user_data['correo']
            hash_contraseña = user_data['hash_contraseña']
            user_role = user_data['rol']
            negocio_id = user_data['negocio_id']
            estado_solicitud_negocio = user_data['estado_solicitud_negocio']
            
            # Verify password
            if not hash_contraseña:
                # Log failed login attempt
                from app.governance.audit import log_access
                log_access(user_id=user_id, user_email=user_email, 
                          access_type='LOGIN_FAILED', successful=False,
                          error_message='No password hash')
                return jsonify({'error': 'Incorrect username/email or password'}), 401
            
            password_valid = verify_password(password, hash_contraseña)
            
            if not password_valid:
                # Log failed login attempt
                from app.governance.audit import log_access
                log_access(user_id=user_id, user_email=user_email, 
                          access_type='LOGIN_FAILED', successful=False,
                          error_message='Invalid password')
                return jsonify({'error': 'Incorrect username/email or password'}), 401
            
            # Create access token
            access_token = create_access_token(identity=user_id)
            
            # Log successful access
            from app.governance.audit import log_access
            log_access(user_id=user_id, user_email=user_email, 
                      access_type='LOGIN', successful=True)
            
            return jsonify({
                'access_token': access_token,
                'token_type': 'bearer',
                'user': {
                    'id': user_id,
                    'username': user_username,
                    'email': user_email,
                    'role': user_role
                }
            }), 200
        
        except Exception as query_error:
            conn.close()
            error_msg = str(query_error)
            logger.error(f"Database error in login: {error_msg}", exc_info=True, extra={
                'action': 'login',
                'username_or_email': username_or_email
            })
            
            if 'Access denied' in error_msg or '1045' in error_msg:
                return jsonify({'error': 'Database authentication error. Check .env configuration.'}), 500
            elif 'Unknown database' in error_msg or '1049' in error_msg:
                return jsonify({'error': 'Database not found. Check database name in .env'}), 500
            elif 'Can\'t connect' in error_msg or '2003' in error_msg:
                return jsonify({'error': 'Cannot connect to database server. Check WAMP/MySQL is running.'}), 500
            else:
                return jsonify({'error': 'Database query error'}), 500
    
    except Exception as e:
        logger.error(f"Unexpected error in login: {str(e)}", exc_info=True, extra={
            'action': 'login'
        })
        return jsonify({'error': 'Login failed'}), 500

@auth_bp.route('/me', methods=['GET'])
@jwt_required()
def get_me():
    """Get current user information."""
    try:
        from flask_jwt_extended import get_jwt_identity
        from app.utils.serializers import user_to_dict
        
        user_id = get_jwt_identity()
        if not user_id:
            return jsonify({'error': 'User not found'}), 404
        
        # Obtener datos completos del usuario desde la base de datos
        conn = get_db_connection()
        if not conn:
            return jsonify({'error': 'Database connection error'}), 500
        
        try:
            cursor = conn.cursor()
            cursor.execute("""
                SELECT id, nombre_usuario, nombre_completo, correo, celular, rol, negocio_id, estado_solicitud_negocio, creado_en
                FROM usuarios WHERE id = %s
            """, (user_id,))
            user_data = cursor.fetchone()
            cursor.close()
            conn.close()
            
            if not user_data:
                return jsonify({'error': 'User not found'}), 404
            
            return jsonify(user_to_dict(user_data)), 200
        except Exception as db_error:
            if conn:
                cursor.close()
                conn.close()
            logger.error(f"Database error in get_me: {str(db_error)}", exc_info=True, extra={
                'action': 'get_me',
                'user_id': user_id
            })
            return jsonify({'error': 'Error retrieving user information'}), 500
    except Exception as e:
        logger.error(f"Error in get_me: {str(e)}", exc_info=True, extra={
            'action': 'get_me'
        })
        return jsonify({'error': 'Error retrieving user information'}), 500

@auth_bp.route('/forgot-password', methods=['POST'])
def forgot_password():
    """Request password reset. Generates a reset token and sends email."""
    data = request.get_json()
    email = data.get('email')
    
    if not email:
        return jsonify({'error': 'Email is required'}), 400
    
    email = email.lower().strip()
    
    conn = get_db_connection()
    if not conn:
        return jsonify({'error': 'Database connection error'}), 500
    
    try:
        cursor = conn.cursor()
        cursor.execute("SELECT id FROM usuarios WHERE correo = %s", (email,))
        user_data = cursor.fetchone()
        
        # Always return success to prevent email enumeration
        if user_data:
            user_id = user_data['id']
            
            # Invalidar tokens anteriores
            cursor.execute("""
                UPDATE tokens_recuperacion 
                SET usado = %s 
                WHERE usuario_id = %s AND usado = %s
            """, (True, user_id, False))
            
            # Generar nuevo token
            reset_token = secrets.token_urlsafe(32)
            expires_at = datetime.now(timezone.utc) + timedelta(hours=1)
            token_id = f"token-{secrets.token_urlsafe(16)}"
            
            cursor.execute("""
                INSERT INTO tokens_recuperacion (id, usuario_id, token, expira_en, usado)
                VALUES (%s, %s, %s, %s, %s)
            """, (token_id, user_id, reset_token, expires_at, False))
            
            conn.commit()
            
            # Enviar email
            try:
                send_password_reset_email(email, reset_token)
                logger.info(f"Password reset email sent", extra={
                    'action': 'forgot_password',
                    'email': email
                })
            except Exception as e:
                logger.warning(f"Error sending password reset email: {str(e)}", extra={
                    'action': 'forgot_password',
                    'email': email
                })
        
        cursor.close()
        conn.close()
        
        return jsonify({
            'message': 'Si el email existe, se enviará un enlace de recuperación.'
        }), 200
    
    except Exception as e:
        conn.rollback()
        cursor.close()
        conn.close()
        logger.error(f"Error in forgot_password: {str(e)}", exc_info=True, extra={
            'action': 'forgot_password',
            'email': email
        })
        return jsonify({'error': 'Error processing request'}), 500

@auth_bp.route('/reset-password', methods=['POST'])
def reset_password():
    """Reset password with token."""
    data = request.get_json()
    if not data:
        return jsonify({'error': 'Request body is required'}), 400
    
    email = data.get('email')
    token = data.get('token')
    new_password = data.get('new_password')
    
    if not all([email, token, new_password]):
        return jsonify({'error': 'Email, token y nueva contraseña son requeridos'}), 400
    
    email = email.lower().strip()
    
    # Validate email format
    from app.utils.validators import validate_email, validate_length
    if not validate_email(email):
        return jsonify({'error': 'Email format is invalid'}), 400
    
    # Validate password length
    if not validate_length(new_password, min_length=6, max_length=128):
        return jsonify({'error': 'La contraseña debe tener entre 6 y 128 caracteres'}), 400
    
    # Validate token format
    if not validate_length(token, min_length=10, max_length=255):
        return jsonify({'error': 'Token format is invalid'}), 400
    
    conn = get_db_connection()
    if not conn:
        return jsonify({'error': 'Database connection error'}), 500
    
    try:
        cursor = conn.cursor()
        
        # Buscar usuario
        cursor.execute("SELECT id, hash_contraseña FROM usuarios WHERE correo = %s", (email,))
        user_data = cursor.fetchone()
        
        if not user_data:
            cursor.close()
            conn.close()
            return jsonify({'error': 'Token o email inválido'}), 400
        
        user_id = user_data['id']
        current_hash_contraseña = user_data['hash_contraseña']
        
        # Buscar token válido
        cursor.execute("""
            SELECT id, expira_en FROM tokens_recuperacion
            WHERE usuario_id = %s AND token = %s AND usado = %s
        """, (user_id, token, False))
        token_data = cursor.fetchone()
        
        if not token_data:
            cursor.close()
            conn.close()
            return jsonify({'error': 'Token inválido o ya utilizado'}), 400
        
        token_id = token_data['id']
        expires_at = token_data['expira_en']
        
        # Verificar expiración
        now_utc = datetime.now(timezone.utc)
        if expires_at.tzinfo is None:
            expires_at = expires_at.replace(tzinfo=timezone.utc)
        elif expires_at.tzinfo != timezone.utc:
            expires_at = expires_at.astimezone(timezone.utc)
        
        if now_utc > expires_at:
            cursor.execute("UPDATE tokens_recuperacion SET usado = %s WHERE id = %s", (True, token_id))
            conn.commit()
            cursor.close()
            conn.close()
            return jsonify({'error': 'El token ha expirado. Solicita uno nuevo.'}), 400
        
        # Verificar que la nueva contraseña sea diferente
        if verify_password(new_password, current_hash_contraseña):
            cursor.close()
            conn.close()
            return jsonify({'error': 'La nueva contraseña debe ser diferente a la actual'}), 400
        
        # Actualizar contraseña
        new_hash_contraseña = get_password_hash(new_password)
        cursor.execute("UPDATE usuarios SET hash_contraseña = %s WHERE id = %s", (new_hash_contraseña, user_id))
        
        # Marcar token como usado
        cursor.execute("UPDATE tokens_recuperacion SET usado = %s WHERE id = %s", (True, token_id))
        
        conn.commit()
        cursor.close()
        conn.close()
        
        return jsonify({'message': 'Contraseña restablecida exitosamente'}), 200
    
    except Exception as e:
        conn.rollback()
        cursor.close()
        conn.close()
        logger.error(f"Error in reset_password: {str(e)}", exc_info=True, extra={
            'action': 'reset_password',
            'email': email
        })
        return jsonify({'error': 'Error processing request'}), 500

@auth_bp.route('/verify-reset-token', methods=['POST'])
def verify_reset_token():
    """Verificar si un token de recuperación es válido."""
    data = request.get_json()
    if not data:
        return jsonify({'error': 'Request body is required'}), 400
    
    email = data.get('email')
    token = data.get('token')
    
    if not email or not token:
        return jsonify({'error': 'Email y token son requeridos'}), 400
    
    # Validate email format
    from app.utils.validators import validate_email, validate_length
    email = email.lower().strip()
    if not validate_email(email):
        return jsonify({'error': 'Email format is invalid'}), 400
    
    if not validate_length(token, min_length=10, max_length=255):
        return jsonify({'error': 'Token format is invalid'}), 400
    
    email = email.lower().strip()
    
    conn = get_db_connection()
    if not conn:
        return jsonify({'valid': False, 'error': 'Database connection error'}), 200
    
    try:
        cursor = conn.cursor()
        cursor.execute("SELECT id FROM usuarios WHERE correo = %s", (email,))
        user_data = cursor.fetchone()
        
        if not user_data:
            cursor.close()
            conn.close()
            return jsonify({'valid': False, 'error': 'Email no encontrado'}), 200
        
        user_id = user_data['id']
        
        cursor.execute("""
            SELECT expira_en FROM tokens_recuperacion
            WHERE usuario_id = %s AND token = %s AND usado = %s
        """, (user_id, token, False))
        token_data = cursor.fetchone()
        
        if not token_data:
            cursor.close()
            conn.close()
            return jsonify({'valid': False, 'error': 'Token inválido o ya utilizado'}), 200
        
        expires_at = token_data['expira_en']
        
        # Verificar expiración
        now_utc = datetime.now(timezone.utc)
        if expires_at.tzinfo is None:
            expires_at = expires_at.replace(tzinfo=timezone.utc)
        elif expires_at.tzinfo != timezone.utc:
            expires_at = expires_at.astimezone(timezone.utc)
        
        if now_utc > expires_at:
            cursor.close()
            conn.close()
            return jsonify({'valid': False, 'error': 'El token ha expirado'}), 200
        
        cursor.close()
        conn.close()
        return jsonify({'valid': True}), 200
    
    except Exception as e:
        cursor.close()
        conn.close()
        logger.error(f"Error in verify_reset_token: {str(e)}", exc_info=True, extra={
            'action': 'verify_reset_token',
            'email': email
        })
        return jsonify({'valid': False, 'error': 'Error processing request'}), 200

@auth_bp.route('/request-business', methods=['POST'])
def request_business():
    """
    Endpoint deshabilitado: Las solicitudes de negocio ahora solo están disponibles para usuarios registrados.
    Este endpoint redirige a los usuarios a registrarse primero.
    """
    return jsonify({
        'error': 'Business registration is only available for registered users',
        'message': 'To request a business account, please register first with user_type: "business"',
        'action_required': 'register',
        'register_endpoint': '/api/auth/register',
        'required_fields': {
            'username': 'string (3-30 characters)',
            'full_name': 'string (2-255 characters)',
            'email': 'string (must be Gmail)',
            'phone': 'string (7-20 characters)',
            'user_type': '"business"',
            'password': 'string (6-128 characters)',
            'confirm_password': 'string (must match password)'
        },
        'after_registration': 'Once registered, use POST /api/users/me/business-application to submit your business details'
    }), 403
