"""
Этот файл содержит узлы для графа состояний агента.
Каждый узел выполняет определенную функцию, такую как
разбор команд, вызов LLM или выполнение сервисов.
"""

from langchain_core.messages import HumanMessage, SystemMessage, AIMessage

from llm_client import get_llm
from services import ImageGenerationService, SubtitleService, ImageAnalysisService, SummarizationService
from models import AgentState

from typing import Dict, Any, Optional
import uuid # Добавлено для генерации UUID


class CommandManager:
    """
    Менеджер для хранения и получения слеш-команд.
    В реальном приложении команды будут храниться в БД.
    """

    def __init__(self):
        self._commands = {
            "help": {
                "description": "Показывает список доступных команд.",
                "workflow": "help_workflow"
            },
            "imagine": {
                "description":
                "Генерирует изображения по промпту. Использование: /imagine [prompt]",
                "workflow": "image_generation_workflow"
            },
            "analyze": {
                "description":
                "Анализирует изображение по URL и промпту. Использование: /analyze [url] [prompt]",
                "workflow": "image_analysis_workflow"
            },
            "subtitles_meet": {
                "description": "Получает текущие субтитры из Google Meet.",
                "workflow": "get_meet_subtitles_workflow"
            },
            "subtitles_teams": {
                "description": "Получает текущие субтитры из MS Teams.",
                "workflow": "get_teams_subtitles_workflow"
            },
            "summarize": {
                "description": "Суммирует текущую историю диалога.",
                "workflow": "summarize_history_workflow"
            },
            "chat": {
                "description": "Ведет обычный диалог с LLM.",
                "workflow": "chat_workflow"
            },
        }

    def get_commands(self) -> Dict[str, Any]:
        """Возвращает все доступные команды."""
        return self._commands

    def get_command_workflow(self, command_name: str) -> Optional[str]:
        """Возвращает имя workflow для заданной команды."""
        return self._commands.get(command_name, {}).get("workflow")


# Инициализация сервисов (пример, убедитесь, что они доступны в этом файле или импортированы)
command_manager = CommandManager()


# Здесь мы меняем состояние графа, добавляя сообщение от пользователя
def parse_command_node(state: AgentState) -> AgentState:
    """
    Парсит вход пользователя на предмет слеш-команды и инициализирует узел пользователя в графе.
    """
    print("Выполняется узел: parse_command_node")
    user_input = state.input.strip()

    # Создаем ID для узла пользователя с помощью UUID
    user_node_id = str(uuid.uuid4())

    # Добавляем узел пользователя в граф для фронтенда с полным текстом сообщения
    state.graph_nodes.append({
        "id": user_node_id,
        "type": "user",
        "data": {
            "label": user_input[:30] + "..." if len(user_input) > 30 else user_input,
            "message": {"role": "user", "content": user_input, "node_id": user_node_id} # Сохраняем сообщение здесь
        }
    })

    # Добавляем ребро к новому узлу, если есть родительский узел
    if state.parent_node_id:
        edge_id = str(uuid.uuid4())
        state.graph_edges.append({
            "id": edge_id,
            "source": state.parent_node_id,
            "target": user_node_id
        })
    state.parent_node_id = user_node_id
    state.current_node_id = user_node_id

    # Обновляем временную историю чата, которая используется для передачи между узлами
    # и для вызова LLM в текущем раунде.
    state.temporary_chat_history.append({
        "role": "user",
        "content": user_input,
        "node_id": user_node_id
    })

    if user_input.startswith('/'):
        parts = user_input[1:].split(' ', 1)
        command_name = parts[0].lower()
        command_args = parts[1].split(' ') if len(parts) > 1 else []
        if command_name in command_manager.get_commands():
            state.command = command_name
            state.command_args = command_args
            print(
                f"Команда распознана: /{state.command} с аргументами: {state.command_args}"
            )
        else:
            state.error = f"Неизвестная команда: /{command_name}. Введите /help для списка команд."
            print(state.error)
            state.command = "error"  # Специальный флаг для обработки ошибок команд
    else:
        state.command = "chat"  # Обычный чат
    return state


def execute_command_node(state: AgentState) -> AgentState:
    """
    Выполняет соответствующий workflow для команды.
    Этот узел служит маршрутизатором для выбора следующего узла в зависимости от команды.
    """
    print(
        f"Выполняется узел: execute_command_node для команды: {state.command}")
    # Этот узел будет выступать в роли маршрутизатора
    # Фактическая логика команды будет в отдельных узлах, вызываемых по условию
    return state  # Просто передаем состояние дальше, чтобы маршрутизатор смог выбрать следующий узел


# Предполагаем, что используем Gemini для основного LLM и суммаризации/анализа
# В реальной системе можно выбрать динамически
DEFAULT_LLM_NAME = "gemini-2.0-flash" #"gemini-2.5-flash"
main_llm = get_llm(DEFAULT_LLM_NAME)


def call_llm_node(state: AgentState) -> AgentState:
    """
    Вызывает LLM для обработки обычного диалога или генерации ответа.
    Использует `temporary_chat_history` для передачи контекста LLM.
    """
    print("Выполняется узел: call_llm_node (для обычного чата)")
    try:
        messages_for_llm = []

        if state.system_prompt:
            messages_for_llm.append(SystemMessage(content=state.system_prompt))
        
        # Для обычного чата LLM использует текущую временную историю
        for msg in state.temporary_chat_history: # Используем temporary_chat_history
            if msg["role"] == "user":
                messages_for_llm.append(HumanMessage(content=msg["content"]))
            elif msg["role"] == "assistant":
                messages_for_llm.append(AIMessage(content=msg["content"]))

        model_name = state.selected_model
        llm = get_llm(model_name)
        response = llm.invoke(messages_for_llm)

        llm_node_id = str(uuid.uuid4())
        
        # Добавляем узел LLM в граф для фронтенда с полным текстом ответа
        state.graph_nodes.append({
            "id": llm_node_id,
            "type": "llm",
            "data": {
                "label": response[:30] + "..." if len(response) > 30 else response,
                "message": {"role": "assistant", "content": response, "node_id": llm_node_id} # Сохраняем сообщение здесь
            }
        })
        edge_id = str(uuid.uuid4())
        state.graph_edges.append({
            "id": edge_id,
            "source": state.parent_node_id,
            "target": llm_node_id
        })
        state.parent_node_id = llm_node_id
        state.current_node_id = llm_node_id

        # Обновляем временную историю чата с ответом LLM
        state.temporary_chat_history.append({
            "role": "assistant",
            "content": response,
            "node_id": llm_node_id
        })
        state.llm_response = response

    except Exception as e:
        state.error = f"Ошибка при вызове LLM: {e}"
        state.llm_response = f"Произошла ошибка: {e}"
        print(state.error)
    return state


image_gen_service = ImageGenerationService(
    base_url="https://render-service-gsu7.onrender.com/g2"
)  # TODO: Замените на реальный URL


def generate_images_node(state: AgentState) -> AgentState:
    """
    Генерирует изображения по промпту, переданному в команде `/imagine`.
    """
    print("Выполняется узел: generate_images_node")
    prompt = " ".join(state.command_args)
    if not prompt:
        state.error = "Для команды /imagine требуется промпт. Пример: /imagine кошка на луне"
        state.llm_response = state.error
        # Добавляем ошибку во временную историю для согласованности
        state.temporary_chat_history.append({"role": "assistant", "content": state.error})
        return state

    try:
        urls = image_gen_service.generate_images(prompt)
        state.image_urls = urls

        # Формируем текст ответа с использованием Markdown для отображения изображений
        image_markdown = "\n".join([
            f'<a href="{url}" target="_blank"><img src="{url}" alt="image{i}" style="max-width: 300px; max-height: 300px;"></a>'
            for i, url in enumerate(urls)
        ])
        response_text = f"Сгенерировано {len(urls)} изображений:\n{image_markdown}"
        state.llm_response = response_text

        img_gen_node_id = str(uuid.uuid4())
        # Добавляем узел генерации изображений в граф
        state.graph_nodes.append({
            "id": img_gen_node_id,
            "type": "tool",
            "data": {
                "label": "Генерация изображений",
                "details": prompt,
                "message": {"role": "assistant", "content": response_text, "node_id": img_gen_node_id}
            }
        })
        edge_id = str(uuid.uuid4())
        state.graph_edges.append({
            "id": edge_id,
            "source": state.parent_node_id,
            "target": img_gen_node_id
        })
        state.parent_node_id = img_gen_node_id
        state.current_node_id = img_gen_node_id

        # Обновляем временную историю чата
        state.temporary_chat_history.append({
            "role": "assistant",
            "content": response_text,
            "node_id": img_gen_node_id  # Привязываем к ID узла
        })

    except Exception as e:
        state.error = f"Ошибка генерации изображений: {e}"
        state.llm_response = f"Произошла ошибка при генерации изображений: {e}"
        print(state.error)
        # Добавляем ошибку во временную историю
        state.temporary_chat_history.append({"role": "assistant", "content": state.llm_response})
    return state


image_analysis_service = ImageAnalysisService(
    llm=get_llm("gemini-2.5-flash"))  # Gemini для анализа изображений


def analyze_image_node(state: AgentState) -> AgentState:
    """
    Анализирует изображение по URL и промпту, переданным в команде `/analyze`.
    """
    print("Выполняется узел: analyze_image_node")
    if len(state.command_args) < 2:
        state.error = "Для команды /analyze требуется URL изображения и промпт. Пример: /analyze [url] 'что на изображении?'"
        state.llm_response = state.error
        # Добавляем ошибку во временную историю для согласованности
        state.temporary_chat_history.append({"role": "assistant", "content": state.error})
        return state

    image_url = state.command_args[0]
    prompt = " ".join(state.command_args[1:])

    try:
        analysis_result = image_analysis_service.analyze_image(
            image_url, prompt)
        state.analysis_result = analysis_result
        response_text = f"Результат анализа изображения '{image_url}': {analysis_result}"
        state.llm_response = response_text

        img_analysis_node_id = str(uuid.uuid4())
        # Добавляем узел анализа изображения в граф
        state.graph_nodes.append({
            "id": img_analysis_node_id,
            "type": "tool",
            "data": {
                "label": "Анализ изображения",
                "details": f"URL: {image_url}, Промпт: {prompt}",
                "message": {"role": "assistant", "content": response_text, "node_id": img_analysis_node_id}
            }
        })
        edge_id = str(uuid.uuid4())
        state.graph_edges.append({
            "id": edge_id,
            "source": state.parent_node_id,
            "target": img_analysis_node_id
        })
        state.parent_node_id = img_analysis_node_id
        state.current_node_id = img_analysis_node_id

        # Обновляем временную историю чата
        state.temporary_chat_history.append({
            "role": "assistant",
            "content": response_text,
            "node_id": img_analysis_node_id
        })

    except Exception as e:
        state.error = f"Ошибка анализа изображения: {e}"
        state.llm_response = f"Произошла ошибка при анализе изображения: {e}"
        print(state.error)
        # Добавляем ошибку во временную историю
        state.temporary_chat_history.append({"role": "assistant", "content": state.llm_response})
    return state


subtitle_service = SubtitleService()


def get_meet_subtitles_node(state: AgentState) -> AgentState:
    """
    Получает субтитры из Google Meet через команду `/subtitles_meet`.
    """
    print("Выполняется узел: get_meet_subtitles_node")
    try:
        subtitles = subtitle_service.get_google_meet_subtitles()
        state.subtitles = subtitles
        response_text = f"Текущие субтитры из Google Meet: {subtitles}"
        state.llm_response = response_text

        meet_subtitles_node_id = str(uuid.uuid4())
        # Добавляем узел субтитров Meet в граф
        state.graph_nodes.append({
            "id": meet_subtitles_node_id,
            "type": "tool",
            "data": {
                "label": "Субтитры Google Meet",
                "details": subtitles[:30] + "..." if subtitles else "",
                "message": {"role": "assistant", "content": response_text, "node_id": meet_subtitles_node_id}
            }
        })
        edge_id = str(uuid.uuid4())
        state.graph_edges.append({
            "id": edge_id,
            "source": state.parent_node_id,
            "target": meet_subtitles_node_id
        })
        state.parent_node_id = meet_subtitles_node_id
        state.current_node_id = meet_subtitles_node_id

        # Обновляем временную историю чата
        state.temporary_chat_history.append({
            "role": "assistant",
            "content": response_text,
            "node_id": meet_subtitles_node_id
        })

    except Exception as e:
        state.error = f"Ошибка получения субтитров Google Meet: {e}"
        state.llm_response = f"Произошла ошибка при получении субтитров Google Meet: {e}"
        print(state.error)
        # Добавляем ошибку во временную историю
        state.temporary_chat_history.append({"role": "assistant", "content": state.llm_response})
    return state


def get_teams_subtitles_node(state: AgentState) -> AgentState:
    """
    Получает субтитры из MS Teams через команду `/subtitles_teams`.
    """
    print("Выполняется узел: get_teams_subtitles_node")
    try:
        subtitles = subtitle_service.get_ms_teams_subtitles()
        state.subtitles = subtitles
        response_text = f"Текущие субтитры из MS Teams: {subtitles}"
        state.llm_response = response_text

        teams_subtitles_node_id = str(uuid.uuid4())
        # Добавляем узел субтитров Teams в граф
        state.graph_nodes.append({
            "id": teams_subtitles_node_id,
            "type": "tool",
            "data": {
                "label": "Субтитры MS Teams",
                "details": subtitles[:30] + "..." if subtitles else "",
                "message": {"role": "assistant", "content": response_text, "node_id": teams_subtitles_node_id}
            }
        })
        edge_id = str(uuid.uuid4())
        state.graph_edges.append({
            "id": edge_id,
            "source": state.parent_node_id,
            "target": teams_subtitles_node_id
        })
        state.parent_node_id = teams_subtitles_node_id
        state.current_node_id = teams_subtitles_node_id

        # Обновляем временную историю чата
        state.temporary_chat_history.append({
            "role": "assistant",
            "content": response_text,
            "node_id": teams_subtitles_node_id
        })

    except Exception as e:
        state.error = f"Ошибка получения субтитров MS Teams: {e}"
        state.llm_response = f"Произошла ошибка при получении субтитров MS Teams: {e}"
        print(state.error)
        # Добавляем ошибку во временную историю
        state.temporary_chat_history.append({"role": "assistant", "content": state.llm_response})
    return state


summarization_service = SummarizationService(
    llm=get_llm("gemini-2.5-flash"))  # Gemini для суммаризации


def summarize_history_node(state: AgentState) -> AgentState:
    """
    Суммирует историю диалога, используя `temporary_chat_history` в качестве контекста.
    """
    print("Выполняется узел: summarize_history_node")
    try:
        # Для суммаризации используем временную историю, подготовленную в предыдущих узлах
        summarized_text = summarization_service.summarize_history_of_dicts(
            state.temporary_chat_history) # Изменен метод для использования List[Dict]
        state.summarized_history_text = summarized_text
        response_text = f"История диалога суммирована:\n{summarized_text}"
        state.llm_response = response_text

        summary_node_id = str(uuid.uuid4())
        # Добавляем узел суммаризации в граф
        state.graph_nodes.append({
            "id": summary_node_id,
            "type": "tool",
            "data": {
                "label": "Суммаризация истории",
                "details": summarized_text[:30] + "..." if summarized_text else "",
                "message": {"role": "assistant", "content": response_text, "node_id": summary_node_id}
            }
        })
        edge_id = str(uuid.uuid4())
        state.graph_edges.append({
            "id": edge_id,
            "source": state.parent_node_id,
            "target": summary_node_id
        })
        state.parent_node_id = summary_node_id
        state.current_node_id = summary_node_id

        # Обновляем временную историю чата
        state.temporary_chat_history.append({
            "role": "assistant",
            "content": response_text,
            "node_id": summary_node_id
        })

    except Exception as e:
        state.error = f"Ошибка суммаризации истории: {e}"
        state.llm_response = f"Произошла ошибка при суммаризации истории: {e}"
        print(state.error)
        # Добавляем ошибку во временную историю
        state.temporary_chat_history.append({"role": "assistant", "content": state.llm_response})
    return state


def handle_error_node(state: AgentState) -> AgentState:
    """
    Обрабатывает ошибки команд, формируя ответ для пользователя и добавляя его в граф.
    """
    print(f"Выполняется узел: handle_error_node. Ошибка: {state.error}")
    state.llm_response = state.error

    error_node_id = str(uuid.uuid4())
    # Добавляем узел ошибки в граф
    state.graph_nodes.append({
        "id": error_node_id,
        "type": "error",
        "data": {
            "label": "Ошибка",
            "details": state.error,
            "message": {"role": "assistant", "content": state.llm_response, "node_id": error_node_id}
        }
    })
    edge_id = str(uuid.uuid4())
    state.graph_edges.append({
        "id": edge_id,
        "source": state.parent_node_id,
        "target": error_node_id
    })
    state.parent_node_id = error_node_id
    state.current_node_id = error_node_id

    # Обновляем временную историю чата
    state.temporary_chat_history.append({
        "role": "assistant",
        "content": state.llm_response,
        "node_id": error_node_id
    })

    return state


def help_node(state: AgentState) -> AgentState:
    """
    Предоставляет информацию о доступных командах, формируя ответ для пользователя и добавляя его в граф.
    """
    print("Выполняется узел: help_node")
    commands_info = command_manager.get_commands()
    help_text = "Доступные команды:\n"
    for cmd, info in commands_info.items():
        help_text += f"/{cmd}: {info['description']}\n"
    state.llm_response = help_text

    help_node_id = str(uuid.uuid4())
    # Добавляем узел справки в граф
    state.graph_nodes.append({
        "id": help_node_id,
        "type": "info",
        "data": {
            "label": "Справка",
            "details": "Список команд",
            "message": {"role": "assistant", "content": state.llm_response, "node_id": help_node_id}
        }
    })
    edge_id = str(uuid.uuid4())
    state.graph_edges.append({
        "id": edge_id,
        "source": state.parent_node_id,
        "target": help_node_id
    })
    state.parent_node_id = help_node_id
    state.current_node_id = help_node_id

    # Обновляем временную историю чата
    state.temporary_chat_history.append({
        "role": "assistant",
        "content": state.llm_response,
        "node_id": help_node_id
    })

    return state