from flask import render_template, request, redirect, url_for, flash, session, jsonify, Blueprint
import mysql.connector
from db_config import connection_pool
import uuid
import smtplib
import random
from werkzeug.security import generate_password_hash, check_password_hash
from oauthlib.oauth2 import WebApplicationClient
import requests
from google.oauth2 import id_token
from google.auth.transport import requests as google_requests
import json
from smtp import send_email
from datetime import datetime, timedelta
import logging
from chat import get_subjects
import secrets
import hashlib
import base64

# Helper for PKCE
def base64url_encode(data):
    return base64.urlsafe_b64encode(data).rstrip(b'=').decode('ascii')

def base64url_sha256(s):
    return base64url_encode(hashlib.sha256(s.encode('utf-8')).digest())

# Инициализировать логгер
logger = logging.getLogger(__name__)

# Создаем сессию requests для повторного использования соединений
http_session = requests.Session()
http_session.mount('https://', requests.adapters.HTTPAdapter(
    max_retries=3,
    pool_connections=10,
    pool_maxsize=10,
    pool_block=False
))

# Добавляем конфигурацию Google OAuth
#GOOGLE_CLIENT_ID = "737525267890-vdvku4qvr6kscgv6cngi3oph11cvffaj.apps.googleusercontent.com"
#GOOGLE_CLIENT_SECRET = "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"

GOOGLE_CLIENT_ID = "257612821175-v7519s63gr9h46ku730116ie27vqf78s.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET = "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"

GOOGLE_DISCOVERY_URL = "https://accounts.google.com/.well-known/openid-configuration"

# VK OAuth configuration
VK_CLIENT_ID = "54047085"  # Замените на ваш ID приложения ВКонтакте
VK_CLIENT_SECRET = "ZPpDsemRVdjB0wvq1IWG"  # Замените на ваш защищенный ключ ВКонтакте
VK_AUTHORIZE_URL = "https://id.vk.com/authorize"
VK_ACCESS_TOKEN_URL = "https://id.vk.com/oauth2/auth"
VK_GET_USER_INFO_URL = "https://api.vk.com/method/users.get"

# Yandex OAuth configuration
YANDEX_CLIENT_ID = "7a9f6898404642bdaf2031d08ab51fcd" # Replace with your Yandex OAuth application ID
YANDEX_USER_INFO_URL = "https://login.yandex.ru/info"

# Инициализируем OAuth клиент
client = WebApplicationClient(GOOGLE_CLIENT_ID)

def register():
    if request.method == 'POST':
        if request.is_json:
            data = request.get_json()
            username = data.get('username')
            email = data.get('email')
            password = data.get('password')
            confirmation_code = data.get('confirmation_code')
            
            # Получаем токен Turnstile из запроса
            turnstile_token = data.get('cf-turnstile-response')
            
            # Проверка наличия токена
            if not turnstile_token:
                return jsonify({"error": "Проверка не пройдена. Пожалуйста, подтвердите, что вы не робот."}), 400
            
            # Проверка токена через API Cloudflare
            secret_key = "0x4AAAAAABDzYN4_IrXo_0bQA_nKH8mlwVU"
            try:
                response = http_session.post('https://challenges.cloudflare.com/turnstile/v0/siteverify', data={
                    'secret': secret_key,
                    'response': turnstile_token,
                    'remoteip': request.remote_addr
                }, timeout=5.0)  # Увеличиваем таймаут до 5 секунд
                
                result = response.json()
                
                # Добавляем логирование полного ответа от Cloudflare
                logger.info(f"Cloudflare Turnstile verification response: {result}")
                
                # Проверка результата верификации
                if not result.get('success', False):
                    logger.error(f"Turnstile validation failed: {result}")
                    return jsonify({"error": "Проверка не пройдена. Пожалуйста, попробуйте еще раз."}), 400
            except (requests.exceptions.RequestException, requests.exceptions.Timeout, ConnectionError, json.JSONDecodeError) as e:
                # При ошибке соединения или таймауте, логируем ошибку, но пропускаем проверку
                logger.error(f"Ошибка при обращении к Cloudflare Turnstile: {e}")
                # Пропускаем проверку и продолжаем регистрацию

            # Если код подтверждения не передан – это первый шаг регистрации
            if not confirmation_code:
                # Проверка сложности пароля
                if len(password) <= 5 or not any(char.isdigit() for char in password) or not any(char.isalpha() for char in password):
                    return jsonify({
                        'error': 'Пароль должен содержать как минимум одну цифру, одну букву и быть длиннее 5 символов'
                    }), 400

                conn = connection_pool.get_connection()
                cursor = conn.cursor(dictionary=True)
                try:
                    # Проверка существующего email
                    cursor.execute("SELECT * FROM user WHERE email = %s", (email,))
                    existing_user = cursor.fetchone()
                    if existing_user:
                        # Изменяем текст ошибки на более понятный
                        return jsonify({'error': 'Пользователь с таким email уже существует'}), 400

                    # Если имя не задано, берем часть email до '@'
                    if not username:
                        username = email.split('@')[0]

                    hashed_password = generate_password_hash(password)
                    cursor.execute(
                        "INSERT INTO user (name, email, password, created_at) VALUES (%s, %s, %s, NOW())",
                        (username, email, hashed_password)
                    )
                    conn.commit()

                    # Генерация и отправка кода подтверждения
                    if not generate_and_save_confirmation_code(email):
                        return jsonify({'error': 'Ошибка при отправке кода подтверждения'}), 500

                    return jsonify({'message': 'confirmation_code_sent'}), 200

                except mysql.connector.Error as err:
                    print(f"Ошибка: {err}")
                    return jsonify({'error': 'Ошибка при регистрации'}), 500
                finally:
                    cursor.close()
                    conn.close()
            else:
                # --- Форменная (не JSON) ветка (при необходимости можно обновить аналогичным образом) ---
                username = request.form.get('username')
                email = request.form.get('email')
                password = request.form.get('password')
                confirm_password = request.form.get('confirm_password')
                confirmation_code = request.form.get('confirmation_code')

                # Здесь можно аналогичным образом разделить шаги регистрации и подтверждения
                # Например, если confirmation_code отсутствует, создать пользователя и отправить код,
                # иначе вернуть ошибку или выполнить подтверждение через отдельный эндпоинт.
                flash('Некорректный запрос', 'danger')
                return redirect(url_for('register_route'))

    # GET запрос – показываем страницу регистрации
    return render_template('register.html', subjects=get_subjects(), vk_callback_url=_build_vk_callback_url())

def login():
    if request.method == 'POST':
        if request.is_json:
            data = request.get_json()
            email = data.get('email')
            password = data.get('password')
        else:
            email = request.form['email']
            password = request.form['password']
        
        print(f"Attempting login for email: {email}")  # Логирование email
        
        conn = connection_pool.get_connection()
        cursor = conn.cursor(dictionary=True)
        
        try:
            cursor.execute("""
                SELECT u.id, u.password, sb.textbook_id 
                FROM user u 
                LEFT JOIN selected_book sb ON u.id = sb.user_id 
                WHERE u.email = %s
                LIMIT 1
            """, (email,))
            user = cursor.fetchone()
            
            if user:
                print(f"User found: {user['id']}")  # Логирование найденного пользователя
                stored_password_hash = user['password']
                # Вывод типа хеша для отладки
                print("Тип хеша из БД:", type(stored_password_hash))
                # Если хэш не является строкой, пробуем декодировать его
                if not isinstance(stored_password_hash, str):
                    try:
                        stored_password_hash = stored_password_hash.decode('utf-8')
                    except Exception as e:
                        print(f"Ошибка декодирования хэша пароля: {e}")
                        if request.is_json:
                            return jsonify({'error': 'Ошибка аутентификации'}), 401
                        flash('Ошибка аутентификации', 'error')
                        return redirect(url_for('login_route'))

                # Для отладки можно вывести первые символы хеша, чтобы убедиться, что он имеет ожидаемый формат
                print("Stored hash (первые 20 символов):", stored_password_hash[:20])

                if check_password_hash(stored_password_hash, password):
                    print("Password check passed")  # Логирование успешной проверки пароля
                    session.permanent = True  # Make session cookie persistent
                    session['user_id'] = user['id']
                    if user['textbook_id']:
                        session['selected_textbook'] = user['textbook_id']
                    response_data = {
                        'message': 'success',
                        'needs_textbook': not user['textbook_id']
                    }
                    if request.is_json:
                        return jsonify(response_data), 200
                    return redirect(url_for('chat_route'))
                else:
                    print("Password check failed")  # Логирование неудачной проверки пароля
            else:
                print("User not found")  # Логирование, если пользователь не найден
            
            if request.is_json:
                return jsonify({'error': 'Неверный email или пароль'}), 401
            flash('Неверный email или пароль.', 'error')
                
        except mysql.connector.Error as err:
            print(f"Ошибка: {err}")
            if request.is_json:
                return jsonify({'error': 'Произошла ошибка при входе'}), 500
            flash('Произошла ошибка при входе.', 'error')
        finally:
            cursor.close()
            conn.close()
            
    return render_template('login.html', subjects=get_subjects(), vk_callback_url=_build_vk_callback_url())

def confirm_registration(token):
    # Use pooled connection instead of creating a new one
    conn = connection_pool.get_connection()
    cursor = conn.cursor()
    
    try:
        cursor.execute("SELECT * FROM user WHERE confirmation_token = %s", (token,))
        user = cursor.fetchone()
        
        if user:
            cursor.execute("UPDATE user SET verified = 1 WHERE confirmation_token = %s", (token,))
            conn.commit()
            flash('Регистрация подтверждена.', 'success')
        else:
            flash('Неверный или устаревший токен.', 'danger')
    
    except mysql.connector.Error as err:
        print(f"Ошибка: {err}")
        flash('Ошибка подтверждения регистрации.', 'danger')
    
    finally:
        cursor.close()
        conn.close()
    
    return redirect(url_for('login_route'))

def forgot_password():
    if request.method == 'POST':
        email = request.form.get('email')
        if not email:
            flash('Пожалуйста, введите email', 'danger')
            return redirect(url_for('forgot_password_route'))
        
        conn = connection_pool.get_connection()
        cursor = conn.cursor(dictionary=True)
        try:
            cursor.execute("SELECT id FROM user WHERE email = %s", (email,))
            user = cursor.fetchone()  # Явное чтение результата запроса
            if not user:
                flash('Пользователь с таким email не найден', 'danger')
                return redirect(url_for('forgot_password_route'))
            
            # Генерация уникального токена и установка срока действия (30 минут)
            token = str(uuid.uuid4())
            expiration = datetime.now() + timedelta(minutes=30)
            
            # Обновляем запись пользователя. Поля reset_token и reset_token_expiration
            # должны существовать в таблице user.
            cursor.execute(
                "UPDATE user SET reset_token = %s, reset_token_expiration = %s WHERE email = %s",
                (token, expiration, email)
            )
            # Добавляем вызов rowcount для обработки результата UPDATE запроса
            affected_rows = cursor.rowcount
            conn.commit()
            
            # Формируем временную ссылку для сброса пароля с фиксированным доменом
            reset_link = f"https://classgpt.ru/new_password/{token}"
            subject = "Сброс пароля"
           
            body = """
            <html>
                <body>
                    <p>Здравствуйте!</p>
                    <p>Для сброса пароля на сайте ClassGPT.ru перейдите по следующей ссылке: <a href="{}">{}</a></p>
                    <p>Ссылка действительна в течение 30 минут.</p>
                    <p><br></p>
                    <p><b>ClassGPT</b></p>
                </body>
            </html>
            """.format(reset_link, reset_link)
            
          
            
            if not send_email(email, subject, body):
                flash('Ошибка при отправке email. Попробуйте позже.', 'danger')
            else:
                flash('На вашу почту отправлено письмо для сброса пароля.', 'success')
        except mysql.connector.Error as err:
            print(f"Ошибка при сбросе пароля: {err}")
            flash('Ошибка при обработке запроса. Попробуйте позже.', 'danger')
        finally:
            cursor.close()
            conn.close()
        
        return redirect(url_for('login_route'))
    
    return render_template('forgot_password.html', subjects=get_subjects())

def reset_password():
    if request.method == 'POST':
        entered_code = request.form.get('reset_code')
        if 'reset_code' in session and session['reset_code'] == int(entered_code):
            return redirect(url_for('change_password_route'))
        else:
            flash('Неверный код.', 'danger')
    
    return render_template('reset_password.html', subjects=get_subjects())

def change_password():
    if request.method == 'POST':
        new_password = request.form.get('new_password')
        # Логика для обновления пароля в базе данных
        # ...
        flash('Пароль успешно изменен.', 'success')
        return redirect(url_for('login_route'))
    
    return render_template('change_password.html', subjects=get_subjects())

def check_auth():
    """Проверка авторизации пользователя"""
    if 'user_id' not in session:
        return jsonify({
            'authenticated': False,
            'redirect': url_for('register_route')
        })
    return jsonify({'authenticated': True})

def _build_google_callback_url():
    """Builds callback URL suitable for current environment."""
    host = request.host or ""
    is_local = host.startswith("localhost") or host.startswith("127.0.0.1")
    if is_local:
        # In local dev Google allows http://localhost... entries
        return url_for('google_callback_handler_route', _external=True, _scheme='http')
    # Production and any non-local host must be https
    return url_for('google_callback_handler_route', _external=True, _scheme='https')

def _build_vk_callback_url():
    """Builds VK callback URL suitable for current environment, always using HTTPS."""
    # VK ID requires HTTPS for all redirect URLs, even for localhost.
    # For local development, you will need to serve your Flask app over HTTPS (e.g., with ngrok or local SSL).
    return url_for('vk_callback_handler_route', _external=True, _scheme='https')

def google_login_handler():
    # Получаем URL для авторизации Google
    google_provider_cfg = requests.get(GOOGLE_DISCOVERY_URL).json()
    authorization_endpoint = google_provider_cfg["authorization_endpoint"]
    
    # Формируем URL callback с учетом окружения
    callback_url = _build_google_callback_url()
    # Генерируем state и сохраняем в сессии для защиты от CSRF
    oauth_state = uuid.uuid4().hex
    session['oauth_state'] = oauth_state
    
    # Формируем URL для запроса
    request_uri = client.prepare_request_uri(
        authorization_endpoint,
        redirect_uri=callback_url,
        scope=["openid", "email", "profile"],
        state=oauth_state,
    )
    print(f"Redirect URI sent to Google: {callback_url}")
    return redirect(request_uri)

def vk_login_handler():
    # Формируем URL callback с учетом окружения
    callback_url = _build_vk_callback_url()
    # Генерируем state и сохраняем в сессии для защиты от CSRF
    oauth_state = uuid.uuid4().hex
    session['vk_oauth_state'] = oauth_state

    # Generate PKCE code_verifier and code_challenge
    code_verifier = secrets.token_urlsafe(64)
    session['vk_pkce_verifier'] = code_verifier
    code_challenge = base64url_sha256(code_verifier)

    # Формируем URL для запроса авторизации VK ID с PKCE
    request_uri = (
        "https://id.vk.com/authorize?"
        f"client_id={VK_CLIENT_ID}"
        f"&response_type=code"
        f"&redirect_uri={callback_url}"
        f"&code_challenge={code_challenge}&code_challenge_method=S256"
        f"&scope=email"
        f"&state={oauth_state}" # Always include state for CSRF protection
    )
    logger.debug(f"VK Login: Redirect URI sent to VK: {request_uri}") # New log
    return redirect(request_uri)

def google_callback_handler():
    try:
        code = request.args.get("code")
        returned_state = request.args.get("state")
        # Проверяем соответствие state
        expected_state = session.get('oauth_state')
        if not expected_state or returned_state != expected_state:
            print("Несовпадение параметра state в OAuth callback")
            return "Ошибка при авторизации: недействительный state", 400
        # Удаляем state из сессии после проверки
        session.pop('oauth_state', None)
        if not code:
            print("Код авторизации отсутствует")
            return "Ошибка при авторизации: отсутствует код", 400
            
        google_provider_cfg = requests.get(GOOGLE_DISCOVERY_URL).json()
        token_endpoint = google_provider_cfg["token_endpoint"]

        # Тот же callback URL, что и на первом шаге
        callback_url = _build_google_callback_url()
        print(f"Callback URL used for token exchange: {callback_url}")

        token_data = {
            'code': code,
            'client_id': GOOGLE_CLIENT_ID,
            'client_secret': GOOGLE_CLIENT_SECRET,
            'redirect_uri': callback_url,
            'grant_type': 'authorization_code',
        }
        
        print(f"Sending request with data: {token_data}")  # Отладка
        print(f"To endpoint: {token_endpoint}")  # Отладка

        token_response = requests.post(
            token_endpoint,
            data=token_data,
            headers={
                'Accept': 'application/json',
                'Content-Type': 'application/x-www-form-urlencoded',
            }
        )

        print(f"Token response status: {token_response.status_code}")
        print(f"Token response text: {token_response.text}")

        if token_response.status_code != 200:
            print(f"Ошибка получения токена: {token_response.text}")
            return f"Ошибка при авторизации: {token_response.text}", 400

        # Получаем данные токена
        token_json = token_response.json()
        
        # Получаем информацию о пользователе с помощью id_token
        try:
            idinfo = id_token.verify_oauth2_token(
                token_json['id_token'], 
                google_requests.Request(), 
                GOOGLE_CLIENT_ID
            )
            
            if idinfo['aud'] != GOOGLE_CLIENT_ID:
                return "Ошибка аутентификации: неверный получатель", 401

            users_email = idinfo['email']
            users_name = idinfo.get('given_name', users_email.split('@')[0])
            unique_id = idinfo['sub']

            # Проверяем, существует ли пользователь
            conn = connection_pool.get_connection()
            cursor = conn.cursor(dictionary=True)
            
            try:
                # Сначала проверяем, существует ли пользователь с таким email
                cursor.execute("SELECT * FROM user WHERE email = %s", (users_email,))
                user = cursor.fetchone()
                
                if user:
                    # Если пользователь уже существует, обновляем google_id, если его нет
                    if not user.get('google_id'):
                        cursor.execute(
                            "UPDATE user SET google_id = %s WHERE id = %s",
                            (unique_id, user['id'])
                        )
                        conn.commit()
                        print(f"Обновлен существующий пользователь {user['id']} с Google ID: {unique_id}")
                else:
                    # Если пользователя с таким email нет, проверяем по google_id
                    cursor.execute("SELECT * FROM user WHERE google_id = %s", (unique_id,))
                    user = cursor.fetchone()
                    
                    if not user:
                        # Только если пользователь не найден ни по email, ни по google_id, создаем нового
                        print(f"Создаем нового пользователя: {users_email}")
                        hashed_password = generate_password_hash(unique_id)
                        cursor.execute(
                            "INSERT INTO user (name, email, password, created_at, google_id) VALUES (%s, %s, %s, NOW(), %s)",
                            (users_name, users_email, hashed_password, unique_id)
                        )
                        conn.commit()
                        cursor.execute("SELECT id FROM user WHERE google_id = %s", (unique_id,))
                        user = cursor.fetchone()
                
                session.permanent = True  # Make session cookie persistent
                session['user_id'] = user['id']
                print(f"Пользователь успешно авторизован: {user['id']}")
                return redirect(url_for('chat_route'))
                
            except mysql.connector.Error as err:
                print(f"Ошибка при выполнении SQL-запроса: {err}")
                conn.rollback()
                return "Ошибка при регистрации пользователя", 500
            finally:
                cursor.close()
                conn.close()
                
        except ValueError as e:
            print(f"Ошибка верификации токена: {e}")
            return f"Ошибка верификации токена: {str(e)}", 401
            
    except Exception as e:
        print(f"Ошибка при обработке callback: {str(e)}")
        return f"Ошибка при авторизации: {str(e)}", 500

def vk_callback_handler():
    try:
        if request.method == 'POST':
            data = request.get_json()
            code = data.get("code")
            device_id = data.get("device_id")
            state = data.get("state")  # Get state from POST data
            code_verifier = data.get("code_verifier") # Get code_verifier from POST data
            logger.debug(f"VK Callback (POST): Received code={code}, device_id={device_id}, state={state}, code_verifier={code_verifier}")
        else: # Assumed GET request from VK direct redirect
            code = request.args.get("code")
            state = request.args.get("state")
            device_id = None # Device ID is not typically in GET parameters from VK direct redirect
            # For direct GET redirect, code_verifier will be from session as it's generated by backend
            code_verifier = session.pop('vk_pkce_verifier', None) 
            logger.debug(f"Current session keys in GET callback: {list(session.keys())}") # Debugging session content
            logger.debug(f"VK Callback (GET): Received code={code}, state={state}, code_verifier={code_verifier}")

        if not code:
            logger.error("Отсутствует код авторизации в VK callback")
            return jsonify({'error': 'Ошибка при авторизации: отсутствует код'}), 400

        # Проверяем соответствие state только для GET запросов, где VK непосредственно перенаправляет
        # For POST requests (OneTap), state is passed in the body and doesn't need session check
        if request.method == 'GET':
            expected_state = session.get('vk_oauth_state')
            if not expected_state or state != expected_state:
                logger.error(f"Несовпадение параметра state в VK callback (GET). Expected: {expected_state}, Received: {state}")
                return jsonify({'error': 'Ошибка при авторизации: недействительный state'}), 400
            session.pop('vk_oauth_state', None)

        # Получаем токен доступа
        token_endpoint = VK_ACCESS_TOKEN_URL  # Use the correct VK ID PKCE token endpoint
        callback_url = _build_vk_callback_url()
        token_data = {
            "client_id": VK_CLIENT_ID,
            "code": code,
            "redirect_uri": callback_url,
            "code_verifier": code_verifier, # Add code_verifier
            "grant_type": "authorization_code", # Add grant_type as per VK ID documentation
            "state": state, # Add state to token_data
        }
        if device_id: # Add device_id if available (from OneTap flow)
            token_data["device_id"] = device_id

        logger.debug(f"VK Callback: code_verifier sent: {code_verifier}") # Debug code_verifier
        logger.debug(f"VK Callback: Sending token request to {token_endpoint} with data {token_data}")

        token_response = requests.post(
            token_endpoint,
            data=token_data,
            headers={
                'Content-Type': 'application/x-www-form-urlencoded',
            }
        )

        logger.debug(f"VK Token Response Status: {token_response.status_code}")
        logger.debug(f"VK Token Response Text: {token_response.text}")

        if token_response.status_code != 200:
            logger.error(f"Ошибка получения токена VK: {token_response.text}")
            return jsonify({'error': f"Ошибка при авторизации: {token_response.text}"}), 400

        token_json = token_response.json()
        access_token = token_json.get("access_token")
        user_id = token_json.get("user_id")
        email_from_vk = token_json.get("email") # VK может возвращать email здесь
        logger.debug(f"VK Token JSON: {token_json}")

        if not access_token or not user_id:
            logger.error(f"Отсутствует access_token или user_id в ответе VK: {token_json}")
            return jsonify({'error': 'Ошибка при авторизации: отсутствуют токены'}), 400

        # Получаем информацию о пользователе
        user_info_url = VK_GET_USER_INFO_URL
        user_info_params = {
            "user_ids": user_id,
            "fields": "first_name,last_name,photo_max_orig",
            "access_token": access_token,
            "v": "5.131",
        }
        logger.debug(f"VK Callback: Sending user info request to {user_info_url} with params {user_info_params}")

        user_info_response = requests.get(user_info_url, params=user_info_params)

        logger.debug(f"VK User Info Response Status: {user_info_response.status_code}")
        logger.debug(f"VK User Info Response Text: {user_info_response.text}")

        if user_info_response.status_code != 200:
            logger.error(f"Ошибка получения информации о пользователе VK: {user_info_response.text}")
            return jsonify({'error': f"Ошибка при авторизации: {user_info_response.text}"}), 400

        user_info_json = user_info_response.json()
        user_data = user_info_json.get("response", [{}])[0]
        logger.debug(f"VK User Info JSON: {user_info_json}")

        if not user_data:
            logger.error(f"Не удалось получить информацию о пользователе VK: {user_info_json}")
            return jsonify({'error': 'Ошибка при авторизации: не удалось получить информацию о пользователе'}), 400

        vk_user_id = user_data.get("id")
        vk_first_name = user_data.get("first_name")
        vk_last_name = user_data.get("last_name")
        vk_photo_url = user_data.get("photo_max_orig")

        # Формируем имя пользователя
        username = f"{vk_first_name} {vk_last_name}"
        if not username.strip(): # Проверяем, что имя не пустое после обрезки пробелов
            username = str(vk_user_id)
        
        # Используем email из ответа VK, если он есть, иначе формируем
        users_email = email_from_vk if email_from_vk else f"{vk_user_id}@vk.com"

        logger.debug(f"VK User Data: vk_user_id={vk_user_id}, username={username}, users_email={users_email}")

        # Проверяем, существует ли пользователь
        conn = connection_pool.get_connection()
        cursor = conn.cursor(dictionary=True)
        try:
            # Сначала проверяем, существует ли пользователь с таким email
            cursor.execute("SELECT * FROM user WHERE email = %s", (users_email,))
            user = cursor.fetchone()
            
            if user:
                # Если пользователь уже существует, обновляем vk_id, если его нет
                if not user.get('vk_id'):
                    cursor.execute(
                        "UPDATE user SET vk_id = %s WHERE id = %s",
                        (vk_user_id, user['id'])
                    )
                    conn.commit()
                    logger.info(f"Обновлен существующий пользователь {user['id']} с VK ID: {vk_user_id}")
            else:
                # Если пользователя с таким email нет, проверяем по vk_id
                cursor.execute("SELECT * FROM user WHERE vk_id = %s", (vk_user_id,))
                user = cursor.fetchone()
                
                if not user:
                    # Только если пользователь не найден ни по email, ни по vk_id, создаем нового
                    logger.info(f"Создаем нового пользователя: {users_email}")
                    hashed_password = generate_password_hash(str(vk_user_id)) # Используем vk_user_id как пароль
                    cursor.execute(
                        "INSERT INTO user (name, email, password, created_at, vk_id) VALUES (%s, %s, %s, NOW(), %s)",
                        (username, users_email, hashed_password, vk_user_id) 
                    )
                    conn.commit()
                    cursor.execute("SELECT id FROM user WHERE vk_id = %s", (vk_user_id,))
                    user = cursor.fetchone()
            
            if user:
                session.permanent = True  # Make session cookie persistent
                session['user_id'] = user['id']
                logger.info(f"Пользователь успешно авторизован: {user['id']}")
                # Instead of redirecting directly, return JSON success response for AJAX
                return jsonify({'message': 'success', 'redirect': url_for('chat_route')}), 200
            else:
                logger.error("Не удалось авторизовать или создать пользователя после VK Callback.")
                return jsonify({'error': 'Ошибка при регистрации пользователя: не удалось получить ID пользователя'}), 500
                
        except mysql.connector.Error as err:
            logger.error(f"Ошибка при выполнении SQL-запроса в VK Callback: {err}")
            conn.rollback()
            return jsonify({'error': 'Ошибка при регистрации пользователя'}), 500
        finally:
            cursor.close()
            conn.close()
                
    except Exception as e:
        logger.error(f"Ошибка при обработке VK callback: {str(e)}", exc_info=True)
        return jsonify({'error': f"Ошибка при авторизации: {str(e)}"}), 500

def yandex_callback_handler():
    """
    Callback-хендлер Яндекс ID.
    - GET  -> отдать страницу-ретранслятор `yandex_callback.html` (sdk-suggest-token)
    - POST -> принять JSON {access_token, ...}, сходить на /info, создать/найти пользователя, выдать redirect

    Ожидаемый ответ фронту: {"message": "success", "redirect": url}
    """
    try:
        logger.info("Yandex Callback: %s %s", request.method, request.url)

        if request.method == 'GET':
            # Страница с `sdk-suggest-token` для отправки токена на основную вкладку
            logger.info("Yandex Callback (GET): render yandex_callback.html")
            return render_template('yandex_callback.html')

        if request.method != 'POST':
            logger.error("Yandex Callback: unsupported method %s", request.method)
            return jsonify({'error': 'Неподдерживаемый метод запроса'}), 405

        # ---------- POST: принимаем токен от фронта ----------
        data = request.get_json(silent=True) or {}
        logger.info("Yandex Callback (POST): body=%s", data)

        access_token = data.get('access_token')
        auth_code = data.get('code')

        if auth_code and not access_token:
            # Теперь используем implicit-flow через SDK: обмен кода на токен не поддерживаем
            logger.error("Yandex Callback: received 'code' but no 'access_token' (ожидается token flow)")
            return jsonify({'error': 'Используйте поток авторизации с токеном (response_type=token)'}), 400

        if not access_token:
            logger.error("Yandex Callback: access_token missing")
            return jsonify({'error': 'Ошибка при авторизации: отсутствует access_token'}), 400

        # ---------- Запрашиваем профиль пользователя ----------
        try:
            resp = http_session.get(
                YANDEX_USER_INFO_URL,
                headers={'Authorization': f'OAuth {access_token}'},
                params={'format': 'json'},
                timeout=6.0
            )
            resp.raise_for_status()
            ui = resp.json()
        except requests.exceptions.RequestException as e:
            logger.error("Yandex /info request error: %s", e, exc_info=True)
            return jsonify({'error': 'Не удалось получить профиль Яндекс'}), 400

        yandex_user_id = ui.get('id')
        login_from_yandex = ui.get('login')
        # email может прийти в default_email, либо в массиве emails
        email_from_yandex = ui.get('default_email') or (ui.get('emails') or [None])[0]

        logger.info(
            "Yandex /info: id=%s login=%s email=%s",
            yandex_user_id, login_from_yandex, email_from_yandex
        )

        if not yandex_user_id:
            logger.error("Yandex /info: missing 'id' in response: %s", ui)
            return jsonify({'error': 'Профиль Яндекс без id'}), 400

        users_email = email_from_yandex or f"{(login_from_yandex or yandex_user_id)}@yandex.ru"
        users_name = (login_from_yandex or users_email.split('@')[0])[:255]  # защита от переполнения

        # ---------- Работа с БД: найти/создать пользователя ----------
        conn = None
        cursor = None
        try:
            conn = connection_pool.get_connection()
            cursor = conn.cursor(dictionary=True)

            # 1) Пытаемся найти по yandex_id
            cursor.execute("SELECT id, email, yandex_id FROM user WHERE yandex_id = %s", (yandex_user_id,))
            user = cursor.fetchone()

            if not user:
                # 2) Если не нашли — попробуем по email (можем привязать yandex_id к существующему аккаунту)
                cursor.execute("SELECT id, email, yandex_id FROM user WHERE email = %s", (users_email,))
                user = cursor.fetchone()

                if user and not user.get('yandex_id'):
                    logger.info("Link Yandex ID to existing user id=%s email=%s", user['id'], user['email'])
                    cursor.execute(
                        "UPDATE user SET yandex_id = %s WHERE id = %s",
                        (yandex_user_id, user['id'])
                    )
                    conn.commit()
                elif not user:
                    # 3) Создаём нового пользователя
                    logger.info("Create new user via Yandex ID: %s", users_email)
                    # генерируем служебный пароль (вход по Яндекс ID)
                    dummy_password = generate_password_hash(str(uuid.uuid4()))
                    cursor.execute(
                        "INSERT INTO user (name, email, password, created_at, yandex_id) "
                        "VALUES (%s, %s, %s, NOW(), %s)",
                        (users_name, users_email, dummy_password, yandex_user_id)
                    )
                    conn.commit()
                    cursor.execute("SELECT id, email, yandex_id FROM user WHERE yandex_id = %s", (yandex_user_id,))
                    user = cursor.fetchone()

            if not user:
                logger.error("Yandex Callback: user not found/created after DB ops")
                return jsonify({'error': 'Ошибка при регистрации пользователя'}), 500

            # ---------- Авторизация сессии ----------
            session.permanent = True
            session['user_id'] = user['id']
            logger.info("Yandex auth success: user_id=%s", user['id'])

            return jsonify({'message': 'success', 'redirect': url_for('chat_route')}), 200

        except mysql.connector.Error as err:
            if conn:
                conn.rollback()
            logger.error("SQL error (Yandex): %s", err, exc_info=True)
            return jsonify({'error': 'Ошибка при регистрации пользователя'}), 500
        finally:
            try:
                if cursor: cursor.close()
                if conn: conn.close()
            except Exception:
                pass

    except Exception as e:
        logger.error("Yandex Callback: unhandled error: %s", e, exc_info=True)
        return jsonify({'error': f'Ошибка при авторизации: {str(e)}'}), 500


def insert_new_user():
    if request.method == 'POST':
        data = request.get_json()
        email = data.get('email')
        password = data.get('password')
        
        if not email or not password:
            return jsonify({'success': False, 'error': 'Email и пароль обязательны'}), 400
        
        try:
            conn = connection_pool.get_connection()
            with conn.cursor(dictionary=True) as cursor:
                # Проверяем, существует ли уже пользователь с таким email
                cursor.execute("SELECT * FROM user WHERE email = %s", (email,))
                existing_user = cursor.fetchone()
                if existing_user:
                    return jsonify({'success': False, 'error': 'Пользователь с таким email уже существует'}), 400
                
                # Создаем нового пользователя
                hashed_password = generate_password_hash(password)
                cursor.execute(
                    "INSERT INTO user (email, password, created_at) VALUES (%s, %s, NOW())",
                    (email, hashed_password)
                )
                conn.commit()
            
            # Генерируем и сохраняем код
            code = generate_and_save_confirmation_code(email)
            if code:
                return jsonify({'success': True}), 200
            else:
                return jsonify({'success': False, 'error': 'Ошибка при отправке письма'}), 500
                
        except mysql.connector.Error as err:
            print(f"Ошибка при создании пользователя: {err}")
            if conn:
                conn.rollback()
            return jsonify({'success': False, 'error': 'Ошибка при создании пользователя'}), 500
        finally:
            if conn:
                conn.close()

def generate_and_save_confirmation_code(email):
    # Генерируем 4-значный код
    confirmation_code = str(random.randint(1000, 9999))
    
    try:
        conn = connection_pool.get_connection()
        with conn.cursor() as cursor:
            # Обновляем поле confirmation_code в таблице user
            cursor.execute(
                "UPDATE user SET confirmation_code = %s WHERE email = %s",
                (confirmation_code, email)
            )
            conn.commit()
            
            body = """
            <html>
                <body>
                    <p>Здравствуйте!</p>
                    <p>Ваш код подтверждения регистрации на сайте ClassGPT.ru: <b>{}</b></p>
                    <p><br></p>
                    <p><b>ClassGPT</b></p>
                </body>
            </html>
            """.format(confirmation_code)
            
            
            
            # Отправляем код по email
            #print(f"Отправляем письмо на {email} с кодом {confirmation_code}")
            if not send_email(email, "Подтверждение регистрации", body):
                raise Exception("Ошибка при отправке письма регистрации")
            
            return confirmation_code
            
    except mysql.connector.Error as err:
        print(f"Ошибка при обновлении кода подтверждения: {err}")
        if conn:
            conn.rollback()
        return None
    except Exception as e:
        print(f"Ошибка при отправке кода подтверждения: {e}")
        if conn:
            conn.rollback()
        return None
    finally:
        if conn:
            conn.close()

def verify_confirmation_code():
    data = request.get_json()
    if not data:
        return jsonify({'success': False, 'error': 'Отсутствуют данные в запросе'}), 400
    
    email = data.get('email')
    confirmation_code = data.get('confirmation_code')
    
    if not email or not confirmation_code:
        return jsonify({'success': False, 'error': 'Не указаны email или код подтверждения'}), 400
    
    try:
        conn = connection_pool.get_connection()
        with conn.cursor() as cursor:
            cursor.execute("SELECT confirmation_code FROM user WHERE email = %s", (email,))
            result = cursor.fetchall()
            if result:
                db_code = str(result[0][0])  # Приводим код из базы данных к строке
                if db_code == str(confirmation_code):  # Приводим код из запроса к строке
                    return jsonify({'success': True}), 200
                else:
                    return jsonify({'success': False, 'error': 'Неверный код подтверждения'}), 400
            else:
                return jsonify({'success': False, 'error': 'Код подтверждения не найден'}), 400
                
    except mysql.connector.Error as err:
        print(f"Ошибка при проверке кода подтверждения: {err}")
        return jsonify({'success': False, 'error': 'Ошибка сервера'}), 500
    finally:
        if conn:
            conn.close()


def send_confirmation_code():
    data = request.get_json()
    email = data.get('email')
    confirmation_code = data.get('confirmation_code')
    turnstile_token = data.get('cf-turnstile-response')

    if not email or not confirmation_code:
        return jsonify({'error': 'Email и код подтверждения обязательны'}), 400
        
    # Проверка наличия токена
    if not turnstile_token:
        return jsonify({"error": "Проверка не пройдена. Пожалуйста, подтвердите, что вы не робот."}), 400
    
    # Проверка токена через API Cloudflare
    secret_key = "0x4AAAAAABDzYN4_IrXo_0bQA_nKH8mlwVU"
    try:
        response = http_session.post('https://challenges.cloudflare.com/turnstile/v0/siteverify', data={
            'secret': secret_key,
            'response': turnstile_token,
            'remoteip': request.remote_addr
        }, timeout=5.0)  # Увеличиваем таймаут до 5 секунд
        
        result = response.json()
        
        # Добавляем логирование полного ответа от Cloudflare
        logger.info(f"Cloudflare Turnstile verification response: {result}")
        
        # Проверка результата верификации
        if not result.get('success', False):
            logger.error(f"Turnstile validation failed: {result}")
            return jsonify({"error": "Проверка не пройдена. Пожалуйста, попробуйте еще раз."}), 400
    except (requests.exceptions.RequestException, requests.exceptions.Timeout, ConnectionError, json.JSONDecodeError) as e:
        # При ошибке соединения или таймауте, логируем ошибку, но пропускаем проверку
        logger.error(f"Ошибка при обращении к Cloudflare Turnstile: {e}")
        # Пропускаем проверку и продолжаем регистрацию

    try:
        conn = connection_pool.get_connection()
        cursor = conn.cursor()
        cursor.execute("SELECT confirmation_code FROM user WHERE email = %s", (email,))
        result = cursor.fetchone()
        if result:
            db_code = str(result[0])
            if db_code == str(confirmation_code):
                # Обновляем статус пользователя как подтвержденного
                cursor.execute("UPDATE user SET verified = 1 WHERE email = %s", (email,))
                conn.commit()
                return jsonify({'message': 'Пользователь подтвержден'}), 200
            else:
                return jsonify({'error': 'Неверный код подтверждения'}), 400
        else:
            return jsonify({'error': 'Пользователь не найден'}), 400
    except mysql.connector.Error as err:
        print(f"Ошибка при проверке кода подтверждения: {err}")
        return jsonify({'error': 'Ошибка сервера'}), 500
    finally:
        if cursor:
            cursor.close()
        if conn:
            conn.close()

def new_password(token):
    if not token:
        flash('Неверный или устаревший токен', 'danger')
        return redirect(url_for('login_route'))
    
    # Use pooled connection instead of creating a new one
    conn = connection_pool.get_connection()
    cursor = conn.cursor(dictionary=True)
    try:
        cursor.execute("SELECT id, reset_token_expiration FROM user WHERE reset_token = %s", (token,))
        user = cursor.fetchone()
        if not user:
            flash('Неверный или устаревший токен', 'danger')
            cursor.close()
            conn.close()
            return redirect(url_for('login_route'))
        
        if user['reset_token_expiration'] and datetime.now() > user['reset_token_expiration']:
            flash('Срок действия ссылки истек. Запросите сброс пароля снова.', 'danger')
            cursor.close()
            conn.close()
            return redirect(url_for('forgot_password_route'))
        
        if request.method == 'POST':
            password = request.form.get('password')
            confirm_password = request.form.get('confirm_password')
            
            # Проверка сложности пароля и совпадения полей
            if not password or password != confirm_password or len(password) <= 5 or \
               not any(char.isdigit() for char in password) or not any(char.isalpha() for char in password):
                flash('Пароль должен быть длиннее 5 символов и содержать как минимум одну цифру, одну букву, и оба поля должны совпадать', 'danger')
                return render_template('new_password.html', token=token, subjects=get_subjects())
            
            hashed_password = generate_password_hash(password)
            cursor.execute(
                "UPDATE user SET password = %s, reset_token = NULL, reset_token_expiration = NULL WHERE id = %s",
                (hashed_password, user['id'])
            )
            conn.commit()
            flash('Пароль успешно изменён. Теперь вы можете войти в систему.', 'success')
            cursor.close()
            conn.close()
            return redirect(url_for('login_route'))
        
        # GET-запрос – отображаем форму для ввода нового пароля
        return render_template('new_password.html', token=token, subjects=get_subjects())
    
    except mysql.connector.Error as err:
        print(f"Ошибка при установке нового пароля: {err}")
        flash('Ошибка при обработке запроса. Попробуйте позже.', 'danger')
        if cursor: cursor.close()
        if conn and conn.is_connected(): conn.close()
        return redirect(url_for('login_route'))
    finally:
        # Убедимся, что соединение всегда закрывается
        if cursor: cursor.close()
        if conn and conn.is_connected(): conn.close()