import { useState, useEffect, useRef } from "react";
import { useParams, Link, useSearchParams } from "react-router-dom";
import ReactMarkdown from "react-markdown";

import { useAuth } from "../contexts/AuthContext";
import { saveSession as apiSaveSession } from "../services/api";
import {
  generateStoryResponseStream,
  buildSystemPrompt,
  sendMessage,
  generateStorySummary,
  extractKeyEvents,
  updateWorldState,
  shouldUpdateWorldState,
} from "../services/deepseek";
import type {
  Story,
  GameSession,
  ChatMessage,
  MessageVersion,
  PlayerCharacter,
  WorldState,
} from "../types";
import {
  useGameSession,
  useStreamingResponse,
  useCharacterDetection,
  useLazyMessages,
} from "../hooks";
import {
  SessionSelector,
  CharacterPanel,
  StreamingText,
} from "../components/game";
import "./GamePage.css";

function generateId(): string {
  return Math.random().toString(36).substring(2) + Date.now().toString(36);
}

function estimateTokens(messages: ChatMessage[]): number {
  if (!messages || messages.length === 0) return 0;
  const totalChars = messages.reduce((sum, msg) => sum + msg.content.length, 0);
  return Math.round(totalChars / 3);
}

function formatTokens(tokens: number): string {
  if (tokens >= 1000000) return `${(tokens / 1000000).toFixed(1)}M`;
  if (tokens >= 1000) return `${(tokens / 1000).toFixed(1)}K`;
  return tokens.toString();
}

export default function GamePage() {
  const { id } = useParams<{ id: string }>();
  const [searchParams] = useSearchParams();
  const { isAuthenticated } = useAuth();

  // Game session hook
  const {
    story,
    session,
    sessionsList,
    currentSessionId,
    playerCharacter,
    isInitialLoading,
    setSession,
    createNewSession,
    switchSession,
    removeSession,
    markUnsaved,
  } = useGameSession({
    storyId: id,
    characterIdFromUrl: searchParams.get("character"),
    isAuthenticated,
  });

  // Streaming response hook
  const {
    streamingContent,
    updateStreamingContent,
    flushStreamingContent,
    resetStreaming,
    startStreaming,
    abortController,
    // abort - не используется, т.к. кнопка "Остановить" закомментирована
    getLatestContent,
  } = useStreamingResponse();

  // Character detection hook
  const lastAssistantContent = session?.messages
    ?.slice()
    .reverse()
    .find((m) => m.role === "assistant")?.content;

  const { activeCharacter } = useCharacterDetection(
    story?.characters || [],
    lastAssistantContent,
    streamingContent,
  );

  // Lazy messages hook for performance
  const {
    visibleMessages,
    hasHiddenMessages,
    hiddenCount,
    loadMore: loadMoreMessages,
    handleScroll: handleLazyScroll,
    totalCount,
  } = useLazyMessages(session?.messages, currentSessionId);

  // Track previous totalCount to detect new messages (start at 0 to trigger initial scroll)
  const prevTotalCountRef = useRef(0);

  // Local state
  const [input, setInput] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const [isContinuing, setIsContinuing] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [showScrollButton, setShowScrollButton] = useState(false);
  const [editingMessageId, setEditingMessageId] = useState<string | null>(null);
  const [editContent, setEditContent] = useState("");
  const [isOocMode, setIsOocMode] = useState(false);

  // Refs
  const messagesEndRef = useRef<HTMLDivElement>(null);
  const messagesContainerRef = useRef<HTMLDivElement>(null);
  const inputRef = useRef<HTMLTextAreaElement>(null);
  const sessionRef = useRef<GameSession | null>(null);
  const pendingMessagesRef = useRef<ChatMessage[]>([]);

  // Keep sessionRef in sync
  useEffect(() => {
    sessionRef.current = session;
  }, [session]);

  // Warn before leaving during loading
  useEffect(() => {
    const handleBeforeUnload = (e: BeforeUnloadEvent) => {
      if (isLoading) {
        e.preventDefault();
        e.returnValue = "";
      }
    };
    window.addEventListener("beforeunload", handleBeforeUnload);
    return () => window.removeEventListener("beforeunload", handleBeforeUnload);
  }, [isLoading]);

  // Initial load effect - start story if needed
  useEffect(() => {
    const initStory = async () => {
      if (!story || !session || !currentSessionId || !playerCharacter) return;
      if (session.messages.length === 0) {
        startStory(story, session, playerCharacter, currentSessionId);
      }
    };
    initStory();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [story, session?.messages?.length, currentSessionId, playerCharacter]);

  // Scroll effects - scroll to bottom only when NEW messages are added
  useEffect(() => {
    // Only scroll if totalCount increased (new message added)
    // Don't scroll when loading more old messages (which increases visibleMessages but not totalCount)
    if (totalCount > prevTotalCountRef.current) {
      const timer = setTimeout(() => scrollToBottom(), 50);
      prevTotalCountRef.current = totalCount;
      return () => clearTimeout(timer);
    }
    // Don't update ref here - only update when we actually scroll or on session change
  }, [totalCount, currentSessionId]);

  // Auto-scroll during streaming
  useEffect(() => {
    if (isLoading && streamingContent) {
      // Use instant scroll during streaming to avoid lag
      messagesEndRef.current?.scrollIntoView({ behavior: "auto" });
    }
  }, [isLoading, streamingContent]);

  useEffect(() => {
    if (session && !isInitialLoading) {
      // Reset prevTotalCountRef for new session and scroll to bottom
      prevTotalCountRef.current = session.messages?.length || 0;
      // Scroll multiple times with delays to ensure DOM is ready
      const t1 = setTimeout(() => scrollToBottom(true), 100);
      const t2 = setTimeout(() => scrollToBottom(true), 300);
      return () => {
        clearTimeout(t1);
        clearTimeout(t2);
      };
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currentSessionId, isInitialLoading]);

  const scrollToBottom = (instant = false) => {
    // Try scrollIntoView first
    messagesEndRef.current?.scrollIntoView({
      behavior: instant ? "auto" : "smooth",
    });
    // Also set scrollTop as fallback
    if (messagesContainerRef.current) {
      messagesContainerRef.current.scrollTop =
        messagesContainerRef.current.scrollHeight;
    }
  };

  const handleScroll = () => {
    const container = messagesContainerRef.current;
    if (!container) return;
    const { scrollTop, scrollHeight, clientHeight } = container;
    const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
    setShowScrollButton(distanceFromBottom > 200);

    // Lazy load more messages when scrolling up
    handleLazyScroll(container);
  };

  const startStory = async (
    storyData: Story,
    sessionData: GameSession,
    character: PlayerCharacter | null,
    sessionId: string,
  ) => {
    if (!character) return;
    if (storyData.firstMessage?.trim()) {
      const firstMessageContent = storyData.firstMessage.replace(
        /\{user\}/gi,
        character.name,
      );
      const assistantMessage: ChatMessage = {
        id: generateId(),
        role: "assistant",
        content: firstMessageContent,
        timestamp: new Date(),
      };

      const updatedSession: GameSession = {
        ...sessionData,
        messages: [assistantMessage],
      };
      await apiSaveSession(storyData.id, sessionId, updatedSession);
      setSession(updatedSession);
      return;
    }

    setIsLoading(true);
    setError(null);

    try {
      const systemPrompt = buildSystemPrompt(storyData, character);
      const response = await sendMessage([
        { role: "system", content: systemPrompt },
        { role: "user", content: "Начни историю." },
      ]);

      const assistantMessage: ChatMessage = {
        id: generateId(),
        role: "assistant",
        content: response,
        timestamp: new Date(),
      };

      const updatedSession: GameSession = {
        ...sessionData,
        messages: [assistantMessage],
      };
      await apiSaveSession(storyData.id, sessionId, updatedSession);
      setSession(updatedSession);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Произошла ошибка");
    } finally {
      setIsLoading(false);
    }
  };

  const handleSend = async () => {
    if (!input.trim() || !story || !session || !currentSessionId || isLoading)
      return;

    const userMessage: ChatMessage = {
      id: generateId(),
      role: "user",
      content: isOocMode ? `[OOC: ${input.trim()}]` : input.trim(),
      timestamp: new Date(),
    };

    const updatedMessages = [...session.messages, userMessage];
    const tempSession = { ...session, messages: updatedMessages };
    setSession(tempSession);
    pendingMessagesRef.current = updatedMessages;
    markUnsaved();
    setInput("");

    if (inputRef.current) {
      inputRef.current.style.height = "auto";
    }

    setIsLoading(true);
    setError(null);
    const signal = startStreaming();

    // Immediately save user message
    try {
      await apiSaveSession(story.id, currentSessionId, tempSession);
    } catch {
      // Continue even if initial save fails
    }

    // Auto-save interval during streaming
    const autoSaveInterval = setInterval(async () => {
      if (abortController.current && story && currentSessionId) {
        const currentContent =
          document.querySelector(".message.streaming .message-content")
            ?.textContent || "";
        if (currentContent.trim()) {
          const partialMessage: ChatMessage = {
            id: generateId(),
            role: "assistant",
            content: currentContent,
            timestamp: new Date(),
          };
          const partialSession: GameSession = {
            ...sessionRef.current!,
            messages: [...pendingMessagesRef.current, partialMessage],
          };
          try {
            await apiSaveSession(story.id, currentSessionId, partialSession);
          } catch {
            // Ignore save errors during streaming
          }
        }
      }
    }, 5000);

    try {
      const response = await generateStoryResponseStream(
        story,
        session.messages,
        userMessage.content, // Use userMessage.content to include [OOC: ...] wrapper
        updateStreamingContent,
        playerCharacter || undefined,
        session,
        signal,
      );
      flushStreamingContent();
      clearInterval(autoSaveInterval);

      const assistantMessage: ChatMessage = {
        id: generateId(),
        role: "assistant",
        content: response,
        timestamp: new Date(),
      };

      const allMessages = [...updatedMessages, assistantMessage];

      // Skip state updates for OOC messages - they are meta-conversations
      let newKeyEvents = session.keyEvents || [];
      let newSummary = session.storySummary;
      let newWorldState = session.worldState;

      if (!isOocMode) {
        newKeyEvents = await extractKeyEvents(
          response,
          session.keyEvents || [],
        );

        // Calculate total context size
        const totalContextSize = allMessages.reduce(
          (sum, m) => sum + m.content.length,
          0,
        );
        const contextThreshold = 100000; // Generate summary when context gets large

        // Generate summary more frequently (every 8 messages) OR when context is large
        const shouldGenerateSummary =
          (allMessages.length % 8 === 0 && allMessages.length > 0) ||
          (totalContextSize > contextThreshold && allMessages.length > 10);

        if (shouldGenerateSummary) {
          console.log(
            `Generating summary: ${allMessages.length} messages, ${totalContextSize} chars`,
          );
          newSummary = await generateStorySummary(
            story,
            allMessages,
            session.storySummary,
          );
        }

        // Update world state for better character tracking
        if (shouldUpdateWorldState(allMessages.length, session.worldState)) {
          try {
            newWorldState = await updateWorldState(
              story,
              allMessages,
              session.worldState,
            );
          } catch (e) {
            console.warn("Failed to update world state:", e);
          }
        }
      }

      const finalSession: GameSession = {
        ...session,
        messages: allMessages,
        keyEvents: newKeyEvents,
        storySummary: newSummary,
        worldState: newWorldState,
      };

      await apiSaveSession(story.id, currentSessionId, finalSession);
      setSession(finalSession);
    } catch (err) {
      clearInterval(autoSaveInterval);
      if (err instanceof Error && err.name === "AbortError") {
        // User cancelled — save what we got (use getLatestContent for fresh buffer)
        const currentContent = getLatestContent();
        if (currentContent.trim()) {
          const partialMessage: ChatMessage = {
            id: generateId(),
            role: "assistant",
            content: currentContent,
            timestamp: new Date(),
          };
          const partialSession: GameSession = {
            ...session,
            messages: [...updatedMessages, partialMessage],
          };
          await apiSaveSession(story.id, currentSessionId, partialSession);
          setSession(partialSession);
        }
      } else {
        setError(err instanceof Error ? err.message : "Произошла ошибка");
        setSession(session);
        setInput(userMessage.content);
      }
    } finally {
      setIsLoading(false);
      resetStreaming();
      pendingMessagesRef.current = [];
      inputRef.current?.focus();
    }
  };

  // Закомментирована кнопка остановки - функция сохранена на будущее
  // const handleStop = () => {
  //   abort();
  // };

  // Retry: regenerate last AI response
  const handleRetry = async () => {
    if (!story || !session || !currentSessionId || isLoading) return;
    if (session.messages.length < 2) return;

    // Find last assistant message and corresponding user message
    const messages = session.messages;
    let lastAssistantIndex = -1;
    for (let i = messages.length - 1; i >= 0; i--) {
      if (messages[i].role === "assistant") {
        lastAssistantIndex = i;
        break;
      }
    }

    if (lastAssistantIndex < 1) return; // Need at least user + assistant

    const lastUserMessage = messages[lastAssistantIndex - 1];
    if (lastUserMessage.role !== "user") return;

    // Remove last assistant message, keep everything before it
    const messagesWithoutLast = messages.slice(0, lastAssistantIndex);
    const tempSession = { ...session, messages: messagesWithoutLast };
    setSession(tempSession);
    pendingMessagesRef.current = messagesWithoutLast;
    markUnsaved();

    setIsLoading(true);
    setError(null);
    const signal = startStreaming();

    try {
      const response = await generateStoryResponseStream(
        story,
        messagesWithoutLast.slice(0, -1), // History without the user message we're regenerating for
        lastUserMessage.content,
        updateStreamingContent,
        playerCharacter || undefined,
        tempSession,
        signal,
      );
      flushStreamingContent();

      const newAssistantMessage: ChatMessage = {
        id: generateId(),
        role: "assistant",
        content: response,
        timestamp: new Date(),
      };

      const finalMessages = [...messagesWithoutLast, newAssistantMessage];

      // Skip state updates for OOC
      const isOoc = lastUserMessage.content.startsWith("[OOC:");
      let newWorldState = session.worldState;
      if (
        !isOoc &&
        shouldUpdateWorldState(finalMessages.length, session.worldState)
      ) {
        try {
          newWorldState = await updateWorldState(
            story,
            finalMessages,
            session.worldState,
          );
        } catch (e) {
          console.warn("Failed to update world state:", e);
        }
      }

      const finalSession: GameSession = {
        ...session,
        messages: finalMessages,
        worldState: newWorldState,
      };

      await apiSaveSession(story.id, currentSessionId, finalSession);
      setSession(finalSession);
    } catch (err) {
      if (err instanceof Error && err.name === "AbortError") {
        const currentContent = getLatestContent();
        if (currentContent.trim()) {
          const partialMessage: ChatMessage = {
            id: generateId(),
            role: "assistant",
            content: currentContent,
            timestamp: new Date(),
          };
          const partialSession: GameSession = {
            ...session,
            messages: [...messagesWithoutLast, partialMessage],
          };
          await apiSaveSession(story.id, currentSessionId, partialSession);
          setSession(partialSession);
        }
      } else {
        setError(err instanceof Error ? err.message : "Произошла ошибка");
        setSession(session); // Restore original
      }
    } finally {
      setIsLoading(false);
      resetStreaming();
      pendingMessagesRef.current = [];
    }
  };

  // Erase: remove last message pair (user + assistant)
  const handleErase = async () => {
    if (!story || !session || !currentSessionId || isLoading) return;
    if (session.messages.length < 2) return;

    const messages = session.messages;
    let removeCount = 0;

    // Remove last assistant if exists
    if (messages[messages.length - 1].role === "assistant") {
      removeCount++;
      // Also remove the user message before it
      if (
        messages.length >= 2 &&
        messages[messages.length - 2].role === "user"
      ) {
        removeCount++;
      }
    } else if (messages[messages.length - 1].role === "user") {
      // Just remove the user message
      removeCount = 1;
    }

    if (removeCount === 0) return;

    const newMessages = messages.slice(0, -removeCount);
    const newSession: GameSession = {
      ...session,
      messages: newMessages,
    };

    try {
      await apiSaveSession(story.id, currentSessionId, newSession);
      setSession(newSession);
      markUnsaved();
    } catch (err) {
      setError(err instanceof Error ? err.message : "Ошибка удаления");
    }
  };

  // Continue: продолжить генерацию истории (дополняет последнее сообщение ИИ)
  const handleContinue = async () => {
    if (isLoading || !session || !story || !currentSessionId) return;
    if (session.messages.length === 0) return;

    // Find last assistant message
    const messages = session.messages;
    let lastAssistantIndex = -1;
    for (let i = messages.length - 1; i >= 0; i--) {
      if (messages[i].role === "assistant") {
        lastAssistantIndex = i;
        break;
      }
    }

    if (lastAssistantIndex === -1) return; // No assistant message to continue

    const lastAssistantMessage = messages[lastAssistantIndex];

    setIsLoading(true);
    setIsContinuing(true);
    setError(null);
    const signal = startStreaming();

    try {
      // Generate continuation without adding user message
      // Pass special instruction to continue from where AI left off
      const response = await generateStoryResponseStream(
        story,
        messages.slice(0, lastAssistantIndex + 1), // History INCLUDING the message we're continuing
        "[CONTINUE]", // Special marker for continuation
        updateStreamingContent,
        playerCharacter || undefined,
        { ...session, messages: messages.slice(0, lastAssistantIndex + 1) },
        signal,
      );
      flushStreamingContent();

      // Append to existing assistant message
      const updatedAssistantMessage: ChatMessage = {
        ...lastAssistantMessage,
        content: lastAssistantMessage.content + "\n\n" + response,
      };

      const finalMessages = [
        ...messages.slice(0, lastAssistantIndex),
        updatedAssistantMessage,
        ...messages.slice(lastAssistantIndex + 1),
      ];

      const finalSession: GameSession = {
        ...session,
        messages: finalMessages,
      };

      await apiSaveSession(story.id, currentSessionId, finalSession);
      setSession(finalSession);
    } catch (err) {
      if (err instanceof Error && err.name === "AbortError") {
        const currentContent = getLatestContent();
        if (currentContent.trim()) {
          // Still append partial content
          const updatedAssistantMessage: ChatMessage = {
            ...lastAssistantMessage,
            content: lastAssistantMessage.content + "\n\n" + currentContent,
          };
          const partialMessages = [
            ...messages.slice(0, lastAssistantIndex),
            updatedAssistantMessage,
            ...messages.slice(lastAssistantIndex + 1),
          ];
          const partialSession: GameSession = {
            ...session,
            messages: partialMessages,
          };
          await apiSaveSession(story.id, currentSessionId, partialSession);
          setSession(partialSession);
        }
      } else {
        setError(err instanceof Error ? err.message : "Произошла ошибка");
      }
    } finally {
      setIsLoading(false);
      setIsContinuing(false);
      resetStreaming();
    }
  };

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === "Enter" && !e.shiftKey) {
      e.preventDefault();
      handleSend();
    }
  };

  const handleEditMessage = (messageId: string, content: string) => {
    setEditingMessageId(messageId);
    setEditContent(content);
  };

  const handleCancelEdit = () => {
    setEditingMessageId(null);
    setEditContent("");
  };

  const handleSaveEdit = async (messageId: string) => {
    if (!session || !story || !currentSessionId || !editContent.trim()) return;

    const messageIndex = session.messages.findIndex((m) => m.id === messageId);
    if (messageIndex === -1) return;

    const message = session.messages[messageIndex];
    const nextMessage = session.messages[messageIndex + 1];
    const currentAiResponse =
      nextMessage?.role === "assistant" ? nextMessage.content : undefined;

    const versions: MessageVersion[] = message.versions || [
      {
        content: message.content,
        timestamp: message.timestamp,
        aiResponse: currentAiResponse,
      },
    ];

    const currentVersionIdx = message.activeVersion || 0;
    if (
      versions[currentVersionIdx] &&
      !versions[currentVersionIdx].aiResponse &&
      currentAiResponse
    ) {
      versions[currentVersionIdx] = {
        ...versions[currentVersionIdx],
        aiResponse: currentAiResponse,
      };
    }

    const newVersion: MessageVersion = {
      content: editContent.trim(),
      timestamp: new Date(),
    };
    const newVersions: MessageVersion[] = [...versions, newVersion];
    const newActiveVersion = newVersions.length - 1;

    const updatedUserMessage: ChatMessage = {
      ...message,
      content: editContent.trim(),
      versions: newVersions,
      activeVersion: newActiveVersion,
    };

    const messagesUpToEdit = session.messages.slice(0, messageIndex);
    const updatedMessages = [...messagesUpToEdit, updatedUserMessage];

    const tempSession = { ...session, messages: updatedMessages };
    setSession(tempSession);
    pendingMessagesRef.current = updatedMessages;
    markUnsaved();
    setEditingMessageId(null);
    setEditContent("");
    setIsLoading(true);
    setError(null);
    const signal = startStreaming();

    try {
      await apiSaveSession(story.id, currentSessionId, tempSession);
    } catch {
      // Continue even if save fails
    }

    const autoSaveInterval = setInterval(async () => {
      if (abortController.current && story && currentSessionId) {
        const currentContent = getLatestContent();
        if (currentContent.trim()) {
          const partialMessage: ChatMessage = {
            id: generateId(),
            role: "assistant",
            content: currentContent,
            timestamp: new Date(),
          };
          const partialSession: GameSession = {
            ...sessionRef.current!,
            messages: [...pendingMessagesRef.current, partialMessage],
          };
          try {
            await apiSaveSession(story.id, currentSessionId, partialSession);
          } catch {
            // Ignore
          }
        }
      }
    }, 5000);

    // Reset worldState — it's now stale (was calculated from future messages)
    let recalculatedWorldState: WorldState | undefined = undefined;
    try {
      recalculatedWorldState = await updateWorldState(
        story,
        messagesUpToEdit,
        undefined, // pass undefined — force fresh calculation, ignore old state
      );
    } catch (e) {
      console.warn("WorldState recalculation failed on edit:", e);
      // Continue without worldState — better than using stale future state
    }

    const sessionForEdit: GameSession = {
      ...session,
      messages: messagesUpToEdit,
      worldState: recalculatedWorldState,
    };

    try {
      const response = await generateStoryResponseStream(
        story,
        messagesUpToEdit,
        editContent.trim(),
        updateStreamingContent,
        playerCharacter || undefined,
        sessionForEdit,
        signal,
      );
      flushStreamingContent();
      clearInterval(autoSaveInterval);

      const finalVersions: MessageVersion[] = [...newVersions];
      finalVersions[newActiveVersion] = {
        ...finalVersions[newActiveVersion],
        aiResponse: response,
      };

      const finalUserMessage: ChatMessage = {
        ...updatedUserMessage,
        versions: finalVersions,
      };

      const assistantMessage: ChatMessage = {
        id: generateId(),
        role: "assistant",
        content: response,
        timestamp: new Date(),
      };

      const allMessages = [
        ...messagesUpToEdit,
        finalUserMessage,
        assistantMessage,
      ];

      // Update world state after edit regeneration (based on recalculated state)
      let newWorldState = recalculatedWorldState;
      if (shouldUpdateWorldState(allMessages.length, recalculatedWorldState)) {
        try {
          newWorldState = await updateWorldState(
            story,
            allMessages,
            recalculatedWorldState,
          );
        } catch (e) {
          console.warn("Failed to update world state:", e);
        }
      }

      const finalSession: GameSession = {
        ...session,
        messages: allMessages,
        worldState: newWorldState,
      };

      await apiSaveSession(story.id, currentSessionId, finalSession);
      setSession(finalSession);
    } catch (err) {
      clearInterval(autoSaveInterval);
      if (err instanceof Error && err.name === "AbortError") {
        const currentContent = getLatestContent();
        if (currentContent.trim()) {
          const finalVersions: MessageVersion[] = [...newVersions];
          finalVersions[newActiveVersion] = {
            ...finalVersions[newActiveVersion],
            aiResponse: currentContent,
          };

          const finalUserMessage: ChatMessage = {
            ...updatedUserMessage,
            versions: finalVersions,
          };

          const partialMessage: ChatMessage = {
            id: generateId(),
            role: "assistant",
            content: currentContent,
            timestamp: new Date(),
          };
          const partialSession: GameSession = {
            ...session,
            messages: [...messagesUpToEdit, finalUserMessage, partialMessage],
          };
          await apiSaveSession(story.id, currentSessionId, partialSession);
          setSession(partialSession);
        }
      } else {
        setError(err instanceof Error ? err.message : "Произошла ошибка");
      }
    } finally {
      setIsLoading(false);
      resetStreaming();
      pendingMessagesRef.current = [];
    }
  };

  const handleSwitchVersion = async (
    messageId: string,
    direction: "prev" | "next",
  ) => {
    if (!session || !story || !currentSessionId) return;

    const messageIndex = session.messages.findIndex((m) => m.id === messageId);
    if (messageIndex === -1) return;

    const message = session.messages[messageIndex];
    if (!message.versions || message.versions.length <= 1) return;

    const currentVersion = message.activeVersion || 0;
    let newVersion: number;

    if (direction === "prev") {
      newVersion =
        currentVersion > 0 ? currentVersion - 1 : message.versions.length - 1;
    } else {
      newVersion =
        currentVersion < message.versions.length - 1 ? currentVersion + 1 : 0;
    }

    const nextMessage = session.messages[messageIndex + 1];
    const currentAiResponse =
      nextMessage?.role === "assistant" ? nextMessage.content : undefined;

    const updatedVersions: MessageVersion[] = [...message.versions];
    if (currentAiResponse && updatedVersions[currentVersion]) {
      updatedVersions[currentVersion] = {
        ...updatedVersions[currentVersion],
        aiResponse: currentAiResponse,
      };
    }

    const selectedVersion = updatedVersions[newVersion];

    const updatedMessage: ChatMessage = {
      ...message,
      content: selectedVersion.content,
      versions: updatedVersions,
      activeVersion: newVersion,
    };

    const updatedMessages = [...session.messages];
    updatedMessages[messageIndex] = updatedMessage;

    if (selectedVersion.aiResponse && nextMessage?.role === "assistant") {
      const updatedAiMessage: ChatMessage = {
        ...nextMessage,
        content: selectedVersion.aiResponse,
      };
      updatedMessages[messageIndex + 1] = updatedAiMessage;
    }

    const updatedSession = { ...session, messages: updatedMessages };
    setSession(updatedSession);

    await apiSaveSession(story.id, currentSessionId, updatedSession);
  };

  const handleCreateNewSession = async () => {
    if (!story || !id) return;

    const characterId = playerCharacter?.id || session?.playerId;
    if (!characterId) {
      window.location.href = `/story/${id}`;
      return;
    }

    // Create new session - the existing effect will start the story
    // when session state updates with 0 messages
    await createNewSession(characterId);
  };

  const handleSwitchSession = async (sessionId: string) => {
    if (sessionId === currentSessionId) return;
    await switchSession(sessionId);
  };

  const handleDeleteSession = async (
    sessionId: string,
    sessionName: string,
  ) => {
    const confirmed = confirm(`Удалить сессию "${sessionName}"?`);
    if (!confirmed) return;

    await removeSession(sessionId);
  };

  const currentSessionName =
    sessionsList.find((s) => s.id === currentSessionId)?.name || "Сессия";

  if (isInitialLoading) {
    return (
      <div className="game-page">
        <div className="game-loading">
          <p>Загрузка игры...</p>
        </div>
      </div>
    );
  }

  if (!story) {
    return (
      <div className="game-page">
        <div className="game-error">
          <h2>История не найдена</h2>
          <Link to="/" className="back-link">
            ← Вернуться к списку
          </Link>
        </div>
      </div>
    );
  }

  return (
    <div className="game-page">
      <header className="game-header">
        <Link to={`/story/${story.id}`} className="back-btn">
          ←
        </Link>
        <div className="header-info">
          <h1>{story.title}</h1>
          <SessionSelector
            sessions={sessionsList}
            currentSessionId={currentSessionId}
            currentSessionName={currentSessionName}
            onCreateNew={handleCreateNewSession}
            onSwitch={handleSwitchSession}
            onDelete={handleDeleteSession}
          />
        </div>
        <div className="header-stats">
          <span className="stat-badge tokens">
            🎟️ {formatTokens(estimateTokens(session?.messages || []))}
          </span>
        </div>
      </header>

      <div
        className={`game-layout ${activeCharacter?.avatarUrl ? "has-character" : ""}`}
        style={
          activeCharacter?.avatarUrl
            ? ({
                "--character-bg": `url(${activeCharacter.avatarUrl})`,
              } as React.CSSProperties)
            : undefined
        }
      >
        <CharacterPanel character={activeCharacter} />

        <div className="game-content">
          <div
            className="messages-container"
            ref={messagesContainerRef}
            onScroll={handleScroll}
          >
            {hasHiddenMessages && (
              <button
                className="load-more-messages-btn"
                onClick={loadMoreMessages}
              >
                ↑ Загрузить ещё {hiddenCount} сообщений
              </button>
            )}
            {/* Filter out pending user message until streaming starts, but NOT during Continue */}
            {(isLoading && !streamingContent && !isContinuing
              ? visibleMessages.slice(0, -1)
              : visibleMessages
            ).map((message, index) => {
              // Check if this is the last assistant message
              const isLastAssistant =
                message.role === "assistant" &&
                index === visibleMessages.length - 1;
              const isContinuingThis =
                isLastAssistant && isContinuing && streamingContent;

              return (
                <div
                  key={message.id}
                  className={`message ${message.role}${isLastAssistant ? " last-assistant" : ""}${isContinuingThis ? " continuing" : ""}`}
                >
                  {editingMessageId === message.id ? (
                    <div className="message-edit-form">
                      <textarea
                        value={editContent}
                        onChange={(e) => setEditContent(e.target.value)}
                        className="message-edit-textarea"
                        autoFocus
                      />
                      <div className="message-edit-actions">
                        <button
                          className="edit-cancel-btn"
                          onClick={handleCancelEdit}
                        >
                          Отмена
                        </button>
                        <button
                          className="edit-save-btn"
                          onClick={() => handleSaveEdit(message.id)}
                        >
                          Сохранить и переиграть
                        </button>
                      </div>
                    </div>
                  ) : (
                    <>
                      <div className="message-content">
                        {message.role === "assistant" ? (
                          <StreamingText
                            content={
                              isContinuingThis && streamingContent
                                ? message.content + "\n\n" + streamingContent
                                : message.content
                            }
                            isStreaming={
                              !!(isContinuingThis && streamingContent)
                            }
                          />
                        ) : (
                          <ReactMarkdown>{message.content}</ReactMarkdown>
                        )}
                      </div>
                      <div className="message-footer">
                        <span className="message-time">
                          {new Date(message.timestamp).toLocaleTimeString(
                            "ru-RU",
                            {
                              hour: "2-digit",
                              minute: "2-digit",
                            },
                          )}
                        </span>
                        {message.role === "user" && !isLoading && (
                          <div className="message-actions">
                            {message.versions &&
                              message.versions.length > 1 && (
                                <div className="version-switcher">
                                  <button
                                    className="version-btn"
                                    onClick={() =>
                                      handleSwitchVersion(message.id, "prev")
                                    }
                                  >
                                    ‹
                                  </button>
                                  <span className="version-indicator">
                                    {(message.activeVersion || 0) + 1}/
                                    {message.versions.length}
                                  </span>
                                  <button
                                    className="version-btn"
                                    onClick={() =>
                                      handleSwitchVersion(message.id, "next")
                                    }
                                  >
                                    ›
                                  </button>
                                </div>
                              )}
                            <button
                              className="edit-btn"
                              onClick={() =>
                                handleEditMessage(message.id, message.content)
                              }
                              title="Редактировать"
                            >
                              ✏️
                            </button>
                          </div>
                        )}
                      </div>
                    </>
                  )}
                </div>
              );
            })}

            {isLoading && streamingContent && !isContinuing && (
              <div className="message assistant streaming">
                <div className="message-content">
                  <StreamingText
                    content={streamingContent}
                    isStreaming={true}
                  />
                </div>
              </div>
            )}

            {isLoading && !streamingContent && !isContinuing && (
              <div className="message assistant streaming">
                <div className="message-content">
                  <span className="loading-text">Генерация...</span>
                </div>
              </div>
            )}

            {/* Small loader for Continue mode */}
            {isLoading && !streamingContent && isContinuing && (
              <div className="continue-loader">
                <span className="continue-loader-dots">
                  <span>.</span>
                  <span>.</span>
                  <span>.</span>
                </span>
              </div>
            )}

            {error && (
              <div className="error-message">
                <span>⚠️ {error}</span>
                <button onClick={() => setError(null)}>✕</button>
              </div>
            )}

            <div ref={messagesEndRef} />
          </div>

          {showScrollButton && (
            <button
              className="scroll-to-bottom-btn"
              onClick={() => scrollToBottom()}
            >
              ↓
            </button>
          )}

          {/* Hide input during loading */}
          {!isLoading && (
            <>
              {/* Action buttons: Retry, Erase */}
              {session && session.messages.length >= 2 && (
                <div className="chat-actions">
                  <button
                    type="button"
                    className="action-btn continue-btn"
                    onClick={handleContinue}
                    title="Продолжить историю"
                  >
                    ▶ Continue
                  </button>
                  <button
                    type="button"
                    className="action-btn retry-btn"
                    onClick={handleRetry}
                    title="Перегенерировать последний ответ"
                  >
                    🔄 Retry
                  </button>
                  <button
                    type="button"
                    className="action-btn erase-btn"
                    onClick={handleErase}
                    title="Удалить последнюю пару сообщений"
                  >
                    🗑️ Erase
                  </button>
                </div>
              )}

              <form
                className={`input-container${isOocMode ? " ooc-mode" : ""}`}
                onSubmit={(e) => {
                  e.preventDefault();
                  handleSend();
                }}
              >
                <button
                  type="button"
                  className={`ooc-btn${isOocMode ? " active" : ""}`}
                  onClick={() => setIsOocMode(!isOocMode)}
                  title={
                    isOocMode ? "Вернуться в игру" : "OOC: написать напрямую ИИ"
                  }
                >
                  OOC
                </button>
                <textarea
                  ref={inputRef}
                  value={input}
                  onChange={(e) => {
                    setInput(e.target.value);
                    e.target.style.height = "auto";
                    e.target.style.height =
                      Math.min(e.target.scrollHeight, 150) + "px";
                  }}
                  onKeyDown={handleKeyDown}
                  placeholder={
                    isOocMode
                      ? "[OOC] Напиши инструкцию для ИИ..."
                      : "Что ты хочешь сделать?..."
                  }
                  disabled={false}
                  rows={1}
                  name="chat-input"
                  autoComplete="off"
                  autoCorrect="on"
                  autoCapitalize="sentences"
                  spellCheck={true}
                  enterKeyHint="send"
                  data-form-type="other"
                  data-lpignore="true"
                  data-gramm="false"
                />
                <button
                  type="submit"
                  disabled={!input.trim()}
                  className="send-btn"
                >
                  ➤
                </button>
              </form>
            </>
          )}
        </div>
      </div>
    </div>
  );
}
