from flask import (
    render_template, 
    session, 
    request, 
    redirect, 
    url_for, 
    jsonify, 
    Response,
    send_from_directory
)
import mysql.connector
import json
import logging
from sympy import symbols
from deepseek_handler import get_llm_response_stream
from db_config import connection_pool
import os
import base64
import uuid
from datetime import datetime
from PIL import Image
import io
from deepseek_handler import get_model_info
from multi_agent_system import get_orchestrator, AgentState
from message_utils import get_last_messages
import time


from dotenv import load_dotenv
load_dotenv()
# Настройка логгера
logger = logging.getLogger(__name__)

# Simple in-process store to correlate POST /send_message and SSE by chat_id
# WARNING: This is per-process and volatile. For multi-process deployments use shared storage.
LATENCY_TRACES = {}

def chat():
    conn = connection_pool.get_connection()
    cursor = conn.cursor(dictionary=True)
    
    try:
        selected_textbook_id = request.args.get('textbook_id') or session.get('selected_textbook')
        selected_subject_id = request.args.get('subject') or session.get('selected_subject', 1)
        session['selected_subject'] = selected_subject_id
        
        logger.info(f"Chat route: URL subject param: {request.args.get('subject')}")
        logger.info(f"Chat route: Session subject before: {session.get('selected_subject')}")
        logger.info(f"Chat route: Final selected_subject_id: {selected_subject_id}")
        
        if 'user_id' in session:
            cursor.execute("""
                SELECT textbook_id 
                FROM selected_book 
                WHERE user_id = %s AND subject_id = %s
                LIMIT 1
            """, (session['user_id'], selected_subject_id))
            result = cursor.fetchone()
            if result and result['textbook_id']:
                selected_textbook_id = str(result['textbook_id'])
                session['selected_textbook'] = selected_textbook_id
        
        # Получаем все учебники для выбранного предмета
        cursor.execute("""
            SELECT id, author, publisher, year, publication_number, name, 
                   grade, cover_url 
            FROM textbook WHERE subject_id = %s
            ORDER BY name
        """, (selected_subject_id,))
        all_textbooks = cursor.fetchall()
        
        # Создаем словарь только для тех классов, для которых есть учебники
        textbooks_by_grade = {}
        for textbook in all_textbooks:
            grade = textbook['grade']
            if grade not in textbooks_by_grade:
                textbooks_by_grade[grade] = []
            textbooks_by_grade[grade].append(textbook)
        
        # Проверяем, что выбранный учебник действительно относится к выбранному предмету
        valid_textbook_ids = {str(tb['id']) for tb in all_textbooks}
        if selected_textbook_id not in valid_textbook_ids:
            selected_textbook_id = None
        
        # Получаем содержание для выбранного учебника
        textbook_contents = {}
        if selected_textbook_id:
            cursor.execute("SELECT chapter_title FROM contents WHERE textbook_id = %s order by seq_number", (selected_textbook_id,))
            chapters = cursor.fetchall()
            textbook_contents[selected_textbook_id] = [chapter['chapter_title'] for chapter in chapters]
        
        # Получаем список всех предметов
        subjects = get_subjects()
        
        return render_template('chat.html', 
                             textbooks=all_textbooks, 
                             textbook_contents=textbook_contents,
                             selected_textbook_id=selected_textbook_id,
                             selected_subject_id=selected_subject_id,
                             textbooks_by_grade=textbooks_by_grade,
                             subjects=subjects,
                             page_class='chat-page')
    
    except mysql.connector.Error as err:
        print(f"Ошибка: {err}")
        return render_template('chat.html', 
                             textbooks=[], 
                             textbook_contents={},
                             textbooks_by_grade={},
                             subjects=get_subjects(),
                             page_class='chat-page')
    finally:
        cursor.close()
        conn.close()



def save_message(chat_id, message_text, author=1):
    conn = connection_pool.get_connection()
    cursor = conn.cursor()
    
    try:
        # If message_text is a dictionary or list, serialize it to a JSON string.
        # Otherwise, use it as is.
        message_to_save = json.dumps(message_text, ensure_ascii=False) if isinstance(message_text, (dict, list)) else message_text

        cursor.execute(
            "INSERT INTO chat_message (chat_id, message_text, author, created_at) VALUES (%s, %s, %s, NOW())",
            (chat_id, message_to_save, author)
        )
        conn.commit()
        
    except mysql.connector.Error as err:
        print(f"Ошибка при сохранении сообщения: {err}")
        conn.rollback()
        raise
    finally:
        cursor.close()
        conn.close()

def create_chat(user_id, user_message=None, initial_data=None, subject_id=None, trace_id=None, reasoner_mode=0):
    print(f"Создание чата для user_id: {user_id}")
    conn = connection_pool.get_connection()
    cursor = conn.cursor(dictionary=True)
    
    try:
        # Если subject_id не передан, пробуем взять из сессии
        if subject_id is None:
            subject_id = session.get('selected_subject', 1)
        t_insert_start = time.perf_counter()
        try:
            cursor.execute(
                "INSERT INTO chat (user_id, created_at, subject_id, reasoner_mode) VALUES (%s, NOW(), %s, %s)",
                (user_id, subject_id, int(reasoner_mode))
            )
        except mysql.connector.Error as insert_err:
            # Fallback if the column reasoner_mode is missing in the chat table
            # Do a compatible insert without this column to avoid breaking older schemas
            if getattr(insert_err, 'errno', None) == 1054 or 'Unknown column' in str(insert_err):
                logger.warning("'reasoner_mode' column missing in 'chat' table. Falling back to insert without it.")
                cursor.execute(
                    "INSERT INTO chat (user_id, created_at, subject_id) VALUES (%s, NOW(), %s)",
                    (user_id, subject_id)
                )
            else:
                raise
        conn.commit()
        chat_id = cursor.lastrowid
        t_insert_end = time.perf_counter()

        t_save_initial_ms = None
        if initial_data:
            # Сохраняем системное сообщение с контекстом
            system_message = initial_data
            t_si_start = time.perf_counter()
            save_message(chat_id, system_message, author=0)
            t_si_end = time.perf_counter()
            t_save_initial_ms = int((t_si_end - t_si_start) * 1000)

        t_save_user_ms = None
        if user_message:
            t_su_start = time.perf_counter()
            save_message(chat_id, user_message, author=1)
            t_su_end = time.perf_counter()
            t_save_user_ms = int((t_su_end - t_su_start) * 1000)

        logger.info(
            f"[latency][{trace_id or '-'}] Chat create flow: chat_id={chat_id}, subject_id={subject_id}, "
            f"insert_ms={int((t_insert_end - t_insert_start)*1000)}, save_initial_ms={t_save_initial_ms}, "
            f"save_user_msg_ms={t_save_user_ms}"
        )

        return chat_id

    except mysql.connector.Error as err:
        print(f"Ошибка при создании чата: {err}")
        conn.rollback()
        raise
    finally:
        cursor.close()
        conn.close()




def send_message(user_message, chat_id=None, summary=None, mode=None, trace_id=None, t_request_epoch=None, t_request_perf=None):
    if 'user_id' not in session:
        def error_stream():
            error_data = json.dumps({
            'error': 'Unauthorized',
            'redirect': url_for('register')
            })
            yield f"data: {error_data}\n\n"
        resp = Response(error_stream(), mimetype='text/event-stream', status=401)
        resp.headers['Cache-Control'] = 'no-cache'
        resp.headers['X-Accel-Buffering'] = 'no'
        return resp

    user_id = session['user_id']
    selected_subject_id = session.get('selected_subject', 1)

    try:
        is_new_chat = not chat_id
        # Создаём новый чат или используем существующий
        if is_new_chat:
            # Создаем чат
            if summary:
                t_create_start = time.perf_counter()
                chat_id = create_chat(
                    user_id,
                    summary,
                    subject_id=selected_subject_id,
                    trace_id=trace_id,
                    reasoner_mode=1 if (mode == "reasoner-model") else 0,
                )
                t_create_end = time.perf_counter()
                t_su_start = time.perf_counter()
                save_message(chat_id, user_message, author=1)
                t_su_end = time.perf_counter()
                t_save_user_ms = int((t_su_end - t_su_start) * 1000)
                logger.info(
                    f"[latency][{trace_id or '-'}] POST /send_message: chat_created_ms={int((t_create_end - t_create_start)*1000)}, "
                    f"save_user_msg_ms={t_save_user_ms}, chat_id={chat_id}, subject_id={selected_subject_id}"
                )
            else:
                t_create_start = time.perf_counter()
                chat_id = create_chat(
                    user_id,
                    user_message,
                    subject_id=selected_subject_id,
                    trace_id=trace_id,
                    reasoner_mode=1 if (mode == "reasoner-model") else 0,
                )
                t_create_end = time.perf_counter()
                logger.info(
                    f"[latency][{trace_id or '-'}] POST /send_message: chat_created_ms={int((t_create_end - t_create_start)*1000)}, "
                    f"chat_id={chat_id}, subject_id={selected_subject_id}"
                )
        else:
            t_auth_start = time.perf_counter()
            conn = connection_pool.get_connection()
            cursor = conn.cursor(dictionary=True)
            try:
                cursor.execute("SELECT user_id FROM chat WHERE id = %s", (chat_id,))
                chat = cursor.fetchone()
                if not chat or chat['user_id'] != user_id:
                    def forbidden_stream():
                        yield f"data: {json.dumps({'error': 'Доступ запрещен'})}\n\n"
                    resp = Response(forbidden_stream(), mimetype='text/event-stream', status=403)
                    resp.headers['Cache-Control'] = 'no-cache'
                    resp.headers['X-Accel-Buffering'] = 'no'
                    return resp
                
                t_auth_end = time.perf_counter()
                t_su_start = time.perf_counter()
                save_message(chat_id, user_message, author=1)
                t_su_end = time.perf_counter()
                logger.info(
                    f"[latency][{trace_id or '-'}] POST /send_message: auth_check_ms={int((t_auth_end - t_auth_start)*1000)}, "
                    f"append_user_msg_ms={int((t_su_end - t_su_start)*1000)}, chat_id={chat_id}"
                )
            finally:
                cursor.close()
                conn.close()

        # STREAMING LOGIC STARTS HERE
        trace_id = trace_id or str(uuid.uuid4())[:8]
        t_request_epoch = t_request_epoch or time.time()
        t_request_perf = t_request_perf or time.perf_counter()
        logger.info(f"[latency][{trace_id}] SSE stream initiated: user_id={user_id}, chat_id={chat_id}, mode={mode}, t_epoch={t_request_epoch}")

        t_hist_start = time.perf_counter()
        formatted_messages = get_last_messages(chat_id)
        t_hist_end = time.perf_counter()
        
        t_subj_start = time.perf_counter()
        subject_id = session.get('selected_subject')
        if not subject_id:
            try:
                conn = connection_pool.get_connection()
                cursor = conn.cursor(dictionary=True)
                cursor.execute("SELECT subject_id FROM chat WHERE id = %s", (chat_id,))
                chat_info = cursor.fetchone()
                subject_id = chat_info['subject_id'] if chat_info else 1
            except Exception as db_error:
                logger.error(f"Ошибка при получении subject_id из БД: {db_error}")
                subject_id = 1
            finally:
                try: cursor.close()
                except Exception: pass
                try: conn.close()
                except Exception: pass
        if not subject_id: subject_id = 1
        t_subj_end = time.perf_counter()
        
        logger.info(
            f"[latency][{trace_id}] Pre-LLM prep done: chat_history_ms={int((t_hist_end - t_hist_start)*1000)}; "
            f"subject_lookup_ms={int((t_subj_end - t_subj_start)*1000)}; subject_id={subject_id}"
        )
        
        def generate():
            try:
                if is_new_chat:
                    yield f"event: chat_created\ndata: {json.dumps({'chat_id': chat_id})}\n\n"
                
                reasoning_message = "Пожалуйста, подождите, пока я обдумываю ваш вопрос...\n"
                yield f"data: {json.dumps({'content': reasoning_message})}\n\n"
                
                try:
                    # Always use the current user_message to avoid mismatch with history ordering
                    current_user_input_for_orchestrator = user_message

                    subj_int = int(subject_id)

                    if subj_int in (1, 2, 3, 12, 4, 13):
                        state = AgentState(
                            user_id=int(user_id),
                            subject_id=subj_int,
                            grade=8,
                            chat_id=int(chat_id) if chat_id else None
                        )

                        orchestrator = get_orchestrator()
                        agent_response = orchestrator.handle_message(current_user_input_for_orchestrator, state)

                        if agent_response and agent_response.content:
                            save_message(chat_id, agent_response.content, author=0)
                            yield f"data: {json.dumps({'content': agent_response.content})}\n\n"
                            
                            # Попытка предложить кнопки после ответа
                            try:
                                suggester = orchestrator.registry.get("ButtonSuggesterAgent")
                                if suggester:
                                    suggestions = suggester.suggest_buttons(agent_response.content, state)
                                    if suggestions:
                                        yield f"data: {json.dumps({'suggestions': suggestions})}\n\n"
                            except Exception as sugg_err:
                                logger.error(f"Error suggesting buttons: {sug_err}")
                        elif agent_response and agent_response.stream:
                            accumulated = []
                            first_chunk_logged = False
                            for chunk in agent_response.stream:
                                try:
                                    if not first_chunk_logged:
                                        try: logger.info(f"Stream first chunk type: {type(chunk)}; preview: {repr(chunk)[:200]}")
                                        except Exception: logger.info("Stream first chunk received (preview unavailable)")
                                        first_chunk_logged = True

                                    text_content = None
                                    if isinstance(chunk, str): text_content = chunk
                                    elif hasattr(chunk, 'text'): text_content = chunk.text
                                    elif isinstance(chunk, dict): text_content = chunk.get('content') or chunk.get('text')

                                    if text_content:
                                        accumulated.append(text_content)
                                        yield f"data: {json.dumps({'content': text_content})}\n\n"
                                    else:
                                        logger.debug("Stream chunk had no text content, skipped.")
                                except Exception as chunk_err:
                                    logger.debug(f"Error processing stream chunk: {chunk_err}")
                                    continue
                            if accumulated:
                                full_text = ''.join(accumulated)
                                save_message(chat_id, full_text, author=0)
                                
                                # Попытка предложить кнопки после ответа
                                try:
                                    suggester = orchestrator.registry.get("ButtonSuggesterAgent")
                                    if suggester:
                                        suggestions = suggester.suggest_buttons(full_text, state)
                                        if suggestions:
                                            yield f"data: {json.dumps({'suggestions': suggestions})}\n\n"
                                except Exception as sugg_err:
                                    logger.error(f"Error suggesting buttons: {sug_err}")
                            else:
                                yield f"data: {json.dumps({'error': 'Поток пуст: модель не вернула текст.'})}\n\n"
                        else:
                            yield f"data: {json.dumps({'error': 'Пустой ответ от системы'})}\n\n"
                    else:
                        accumulated = []
                        effective_mode = mode or "chat-model"
                        t_provider_start = time.perf_counter()
                        first_model_chunk_sent = False
                        for chunk in get_llm_response_stream(formatted_messages, effective_mode, subj_int, trace_id=trace_id):
                            try:
                                text_content = None
                                if isinstance(chunk, str): text_content = chunk
                                elif hasattr(chunk, 'text'): text_content = chunk.text
                                elif isinstance(chunk, dict): text_content = chunk.get('content') or chunk.get('text')
                                if text_content:
                                    accumulated.append(text_content)
                                    if not first_model_chunk_sent:
                                        first_model_chunk_sent = True
                                        t_first_chunk = time.perf_counter()
                                        logger.info(
                                            f"[latency][{trace_id}] First LLM chunk ready: "
                                            f"to_provider_start_ms={int((t_provider_start - t_request_perf)*1000)}, "
                                            f"provider_time_to_first_chunk_ms={int((t_first_chunk - t_provider_start)*1000)}, "
                                            f"total_to_first_llm_chunk_ms={int((t_first_chunk - t_request_perf)*1000)}, "
                                            f"subject_id={subj_int}, mode={effective_mode}, chat_id={chat_id}"
                                        )
                                    yield f"data: {json.dumps({'content': text_content})}\n\n"
                            except Exception as chunk_err:
                                logger.debug(f"Direct stream chunk error: {chunk_err}")
                                continue
                        if accumulated:
                            full_text = ''.join(accumulated)
                            save_message(chat_id, full_text, author=0)
                            
                            # Попытка предложить кнопки после ответа
                            try:
                                # For direct LLM response, we can create a temporary state
                                temp_state = AgentState(
                                    user_id=int(user_id),
                                    subject_id=subj_int,
                                    chat_id=int(chat_id) if chat_id else None
                                )
                                orchestrator = get_orchestrator()
                                suggester = orchestrator.registry.get("ButtonSuggesterAgent")
                                if suggester:
                                    suggestions = suggester.suggest_buttons(full_text, temp_state)
                                    if suggestions:
                                        yield f"data: {json.dumps({'suggestions': suggestions})}\n\n"
                            except Exception as sugg_err:
                                logger.error(f"Error suggesting buttons for direct LLM: {sug_err}")
                        else:
                            yield f"data: {json.dumps({'error': 'Пустой ответ от модели'})}\n\n"
                except Exception as orch_error:
                    logger.error(f"Stream handler error: {orch_error}")
                    yield f"data: {json.dumps({'error': str(orch_error)})}\n\n"
 
                yield "data: [DONE]\n\n"
                
            except Exception as e:
                error_message = f"Ошибка в generate: {str(e)}"
                logger.error(error_message, exc_info=True)
                yield f"data: {json.dumps({'error': error_message})}\n\n"

        resp = Response(generate(), mimetype='text/event-stream')
        resp.headers['Cache-Control'] = 'no-cache'
        resp.headers['X-Accel-Buffering'] = 'no'
        return resp

    except Exception as e:
        error_text = f"Ошибка при обработке сообщения: {e}"
        logger.error(error_text, exc_info=True)
        def error_gen():
            error_data = json.dumps({'error': error_text})
            yield f"data: {error_data}\n\n"
        resp = Response(error_gen(), mimetype='text/event-stream', status=500)
        resp.headers['Cache-Control'] = 'no-cache'
        resp.headers['X-Accel-Buffering'] = 'no'
        return resp


def get_chats():
    if 'user_id' not in session:
        return jsonify({'error': 'Unauthorized'}), 401
    
    user_id = session['user_id']
    conn = connection_pool.get_connection()
    cursor = conn.cursor(dictionary=True)
    
    try:
        cursor.execute("SELECT id, created_at FROM chat WHERE user_id = %s ORDER BY created_at DESC", (user_id,))
        chats = cursor.fetchall()
    except mysql.connector.Error as err:
        print(f"Ошибка: {err}")
        chats = []
    finally:
        cursor.close()
        conn.close()
    
    return {'chats': chats}

def get_chat_content(chat_id):
    if 'user_id' not in session:
        return redirect(url_for('login'))
    
    user_id = session['user_id']
    conn = connection_pool.get_connection()
    cursor = conn.cursor(dictionary=True)
    
    try:
        # Проверяем, принадлежит ли чат текущему пользователю
        cursor.execute("SELECT user_id FROM chat WHERE id = %s", (chat_id,))
        chat = cursor.fetchone()
        if not chat or chat['user_id'] != user_id:
            return {'error': 'Доступ запрещен'}, 403

        # Получаем сообщения в правильном порядке
        cursor.execute("""
            SELECT message_text, author, created_at
            FROM chat_message 
            WHERE chat_id = %s 
            ORDER BY created_at ASC
        """, (chat_id,))
        messages = cursor.fetchall()
        
        # Формируем список сообщений
        chat_messages = [
            {
                'message_text': message['message_text'],
                'author': 'ClassGPT' if message['author'] == 0 else 'Вы',
                'created_at': message['created_at'].strftime('%Y-%m-%d %H:%M:%S')
            }
            for message in messages
        ]
        
        return {'messages': chat_messages}
    except mysql.connector.Error as err:
        print(f"Ошибка: {err}")
        return {'error': 'Ошибка при получении данных'}, 500
    finally:
        cursor.close()
        conn.close()

def get_summary(chapter_id):
    # Убираем проверку авторизации
    conn = connection_pool.get_connection()
    cursor = conn.cursor(dictionary=True)
    
    try:
        # Получаем summary и дополнительную информацию о параграфе
        cursor.execute("""
            SELECT c.summary, c.chapter_title, c.paragraph_number, t.name as textbook_name 
            FROM contents c
            JOIN textbook t ON c.textbook_id = t.id
            WHERE c.id = %s
        """, (chapter_id,))
        result = cursor.fetchone()
        
        if result:
            return {
                'summary': result['summary'],
                'paragraph': {
                    'paragraph_number': result['paragraph_number'],
                    'chapter_title': result['chapter_title']
                }
            }
        else:
            return {'error': 'Содержание не найдено'}, 404
            
    except mysql.connector.Error as err:
        print(f"Ошибка: {err}")
        return {'error': 'Ошибка при получении данных'}, 500
    finally:
        cursor.close()
        conn.close()

def get_chapters_and_paragraphs(textbook_id):
    # Убираем проверку авторизации
    conn = connection_pool.get_connection()
    cursor = conn.cursor(dictionary=True) 
    
    try:
        # Получаем главы
        cursor.execute("SELECT id, chapter_title FROM contents WHERE textbook_id = %s AND parent_id IS NULL order by seq_number", (textbook_id,))
        chapters = cursor.fetchall()
        
        # Получаем параграфы для каждой главы
        chapters_with_paragraphs = []
        for chapter in chapters:
            cursor.execute("SELECT id, chapter_title, paragraph_number FROM contents WHERE parent_id = %s order by seq_number", (chapter['id'],))
            paragraphs = cursor.fetchall()
            chapters_with_paragraphs.append({
                'chapter': chapter,
                'paragraphs': paragraphs
            })
        
        return {'chapters': chapters_with_paragraphs}
    except mysql.connector.Error as err:
        print(f"Ошибка: {err}")
        return {'error': 'Ошибка при получении данных'}, 500
    finally:
        cursor.close()
        conn.close()

def convert_units(value, from_unit, to_unit):
    """
    Преобразует значение из одной единицы измерения в другую.
    Поддерживаемые единицы: 'kg', 'g', 'm3', 'cm3'.
    """
    # Определяем символы
    m, V = symbols('m V')

    # Преобразования
    conversions = {
        ('kg', 'g'): lambda x: x * 1000,
        ('g', 'kg'): lambda x: x / 1000,
        ('m3', 'cm3'): lambda x: x * 1e6,
        ('cm3', 'm3'): lambda x: x / 1e6
    }

    # Проверяем, есть ли преобразование для данных единиц
    if (from_unit, to_unit) in conversions:
        return conversions[(from_unit, to_unit)](value)
    else:
        raise ValueError(f"Преобразование из {from_unit} в {to_unit} не поддерживается.")

def welcome():
    return render_template('welcome.html', subjects=get_subjects(), page_class='welcome-page')



def serve_images(filename):
    return send_from_directory('static/images', filename)



def get_subjects():
    conn = connection_pool.get_connection()
    cursor = conn.cursor(dictionary=True)
    try:
        cursor.execute("SELECT id, name, enabled FROM subject WHERE id IN (1, 2, 5, 4, 12, 11, 10, 7, 3, 8, 13, 6) ORDER BY s_order")
        return cursor.fetchall()
    except mysql.connector.Error as err:
        print(f"Ошибка при получении предметов: {err}")
        return []
    finally:
        cursor.close()
        conn.close()


def get_subject_folder(subject_id):
    """
    Функция для получения папки предмета на основе subject_id
    """
    subject_id = int(subject_id) if subject_id else 1
    
    folder_map = {
        1: 'physics',
        2: 'algebra', 
        3: 'geometry',
        4: 'chemistry',
        5: 'biology',
        13: 'statistic',
        11: 'english',
        7: 'russian',
        8: 'social_studies',
        10: 'geography',
        12: 'informatics',
        6: 'history',
    }
    
    return folder_map.get(subject_id, 'physics')  # По умолчанию physics


def save_uploaded_image(base64_image, chat_id):
    """
    Сохраняет загруженное изображение и возвращает URL для доступа к нему
    """
    try:
        # Use the configured upload directory from Flask app
        from flask import current_app
        base_upload_dir = current_app.config['USER_IMAGES_DIR']
        date_folder = datetime.now().strftime('%Y-%m-%d')
        upload_dir = os.path.join(base_upload_dir, date_folder)
        os.makedirs(upload_dir, exist_ok=True)
        
        # Extract image data from base64 string
        if ',' in base64_image:
            header, image_data = base64_image.split(';base64,')
            # Определяем формат из заголовка base64 строки (например, data:image/jpeg)
            image_format = header.split('/')[1] if '/' in header else 'jpeg'
        else:
            image_data = base64_image
            image_format = 'jpeg'  # Формат по умолчанию, если не можем определить
            
        image_bytes = base64.b64decode(image_data)
        
        # Open and process image with PIL
        image = Image.open(io.BytesIO(image_bytes))
        
        # Уменьшаем изображение до максимального размера 500px по наибольшей стороне
        MAX_SIZE = 800
        if max(image.width, image.height) > MAX_SIZE:
            # Вычисляем новые размеры, сохраняя пропорции
            if image.width > image.height:
                new_width = MAX_SIZE
                new_height = int(image.height * (MAX_SIZE / image.width))
            else:
                new_height = MAX_SIZE
                new_width = int(image.width * (MAX_SIZE / image.height))
                
            # Изменяем размер изображения
            image = image.resize((new_width, new_height), Image.LANCZOS)
        
        # Используем формат из самого изображения, если не удалось определить из заголовка
        if image_format == 'jpeg' and hasattr(image, 'format') and image.format:
            image_format = image.format.lower()
        
        # Generate unique filename с оригинальным расширением
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        unique_id = str(uuid.uuid4())[:8]
        filename = f'chat_{chat_id}_{timestamp}_{unique_id}.{image_format}'
        
        # Use forward slashes for URLs
        url_path = f'{date_folder}/{filename}'
        # Use os.path.join for filesystem paths
        file_path = os.path.join(upload_dir, filename)
        
        # Save image в оригинальном формате
        image.save(file_path, format=image.format if image.format else image_format.upper())
        
        # Return URL with forward slashes
        image_url = url_for('serve_user_image', filename=url_path, _external=True, _scheme='https')
        if 'localhost' in image_url:
            image_url = image_url.replace('https://', 'http://')
        return {'image_url': image_url}
        
    except Exception as e:
        logger.error(f"Error saving image: {e}")
        raise

def upload_image():
    try:
        data = request.get_json()
        if not data or 'image' not in data:
            return jsonify({'error': 'No image data provided'}), 400

        chat_id = data.get('chat_id')

        # Allow anonymous users to attach images. Create a chat only for authorized users.
        if not chat_id:
            if 'user_id' in session:
                chat_id = create_chat(
                    session['user_id'],
                    subject_id=session.get('selected_subject', 1),
                    reasoner_mode=0,
                )
            else:
                # Use a stable placeholder for anonymous uploads
                chat_id = 'anon'

        result = save_uploaded_image(data['image'], chat_id)

        return jsonify({'image_url': result['image_url']})

    except Exception as e:
        logger.error(f"Ошибка при загрузке изображения: {e}")
        return jsonify({'error': str(e)}), 500

