import { Elysia } from "elysia";
import cors from "@elysiajs/cors";
import { authController } from "./controllers/auth";
import { sessionController } from "./controllers/session";
import { companyController } from "./controllers/company";
import { branchController } from "./controllers/branch";
import { serverController } from "./controllers/server";
import { chatController } from "./controllers/chat";
import { serverSessionService } from "./services/serverSession";
import { saveChatMessage } from "./services/chat";
import { Server } from "socket.io";
import { createServer } from "http";
import { AddressInfo } from "net";

const app = new Elysia();

app.use(
  cors({
    origin: true,
    // credentials: true,
  })
);

app.use(authController);
app.use(sessionController);
app.use(companyController);
app.use(branchController);
app.use(serverController);
app.use(chatController);

app.listen(process.env.PORT || 3000);

console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);

// Setup Socket.IO для WebRTC на отдельном порту
const httpServer = createServer();
const io = new Server(httpServer, {
  cors: {
    origin: "*",
  },
});

httpServer.listen(process.env.SOCKET_PORT || 3001, () => {
  console.log(
    `🎥 WebRTC Socket.IO server running on port ${
      (httpServer.address() as AddressInfo).port
    }`
  );
});

interface Room {
  id: string;
  participants: Set<string>;
}

interface User {
  id: string;
  roomId?: string;
  socketId: string;
}

const rooms = new Map<string, Room>();
const users = new Map<string, User>();

// Вспомогательные функции
function findUserBySocketId(socketId: string): User | undefined {
  for (const [userId, user] of users.entries()) {
    if (user.socketId === socketId) {
      return user;
    }
  }
  return undefined;
}

function findSocketIdByUserId(userId: string): string | undefined {
  const user = users.get(userId);
  return user?.socketId;
}

io.on("connection", (socket) => {
  console.log(`[WebRTC] User connected: ${socket.id}`);

  // Присоединение к комнате
  socket.on(
    "join-room",
    async ({ roomId, userId, isAudioEnabled, isVideoEnabled }) => {
      console.log(
        `[WebRTC] User ${userId} (socket: ${socket.id}) joining room ${roomId}, audio: ${isAudioEnabled}, video: ${isVideoEnabled}`
      );

      // Покинуть предыдущую комнату если была
      const existingUser = users.get(userId);
      if (existingUser?.roomId) {
        console.log(
          `[WebRTC] User ${userId} leaving previous room ${existingUser.roomId}`
        );
        socket.leave(existingUser.roomId);
        const prevRoom = rooms.get(existingUser.roomId);
        if (prevRoom) {
          prevRoom.participants.delete(userId);
          socket.to(existingUser.roomId).emit("user-left", userId);
        }
      }

      // Присоединиться к новой комнате
      socket.join(roomId);

      // Создать комнату если не существует
      if (!rooms.has(roomId)) {
        console.log(`[WebRTC] Creating new room: ${roomId}`);
        rooms.set(roomId, {
          id: roomId,
          participants: new Set(),
        });
      }

      const room = rooms.get(roomId)!;
      room.participants.add(userId);

      // Сохранить пользователя
      users.set(userId, {
        id: userId,
        roomId,
        socketId: socket.id,
      });

      console.log(
        `[WebRTC] Room ${roomId} now has participants:`,
        Array.from(room.participants)
      );

      // Уведомить других участников
      socket.to(roomId).emit("user-joined", userId);
      console.log(
        `[WebRTC] Notified room ${roomId} about user ${userId} joining`
      );

      // Отправить состояние аудио/видео нового пользователя всем в комнате
      socket
        .to(roomId)
        .emit("audio-toggle", { userId, isEnabled: isAudioEnabled !== false });
      socket
        .to(roomId)
        .emit("video-toggle", { userId, isEnabled: isVideoEnabled !== false });

      // Отправить список участников новому пользователю
      const participants = Array.from(room.participants).filter(
        (id) => id !== userId
      );
      console.log(
        `[WebRTC] Sending participant list to ${userId}:`,
        participants
      );
      socket.emit("room-participants", participants);
    }
  );

  // Покидание комнаты
  socket.on("leave-room", ({ roomId, userId }) => {
    console.log(`[WebRTC] User ${userId} leaving room ${roomId}`);

    socket.leave(roomId);
    const room = rooms.get(roomId);
    if (room) {
      room.participants.delete(userId);
      socket.to(roomId).emit("user-left", userId);

      // Удалить пустую комнату
      if (room.participants.size === 0) {
        rooms.delete(roomId);
        console.log(`[WebRTC] Deleted empty room ${roomId}`);
      }
    }

    const user = users.get(userId);
    if (user) {
      user.roomId = undefined;
    }
  });

  // WebRTC сигнализация
  socket.on("offer", ({ target, offer }) => {
    console.log(`[WebRTC] Offer from ${socket.id} to ${target}`);
    const targetSocketId = findSocketIdByUserId(target);
    const senderUser = findUserBySocketId(socket.id);

    if (targetSocketId && senderUser) {
      socket.to(targetSocketId).emit("offer", {
        offer,
        sender: senderUser.id,
      });
    }
  });

  socket.on("answer", ({ target, answer }) => {
    console.log(`[WebRTC] Answer from ${socket.id} to ${target}`);
    const targetSocketId = findSocketIdByUserId(target);
    const senderUser = findUserBySocketId(socket.id);

    if (targetSocketId && senderUser) {
      socket.to(targetSocketId).emit("answer", {
        answer,
        sender: senderUser.id,
      });
    }
  });

  socket.on("ice-candidate", ({ target, candidate }) => {
    console.log(`[WebRTC] ICE candidate from ${socket.id} to ${target}`);
    const targetSocketId = findSocketIdByUserId(target);
    const senderUser = findUserBySocketId(socket.id);

    if (targetSocketId && senderUser) {
      socket.to(targetSocketId).emit("ice-candidate", {
        candidate,
        sender: senderUser.id,
      });
    }
  });

  // Обработка audio/video toggle
  socket.on("audio-toggle", ({ roomId, userId, isEnabled }) => {
    console.log(
      `[WebRTC] Audio toggle from ${userId} in room ${roomId}: ${isEnabled}`
    );
    // Отправляем всем в комнате (кроме отправителя)
    socket.to(roomId).emit("audio-toggle", { userId, isEnabled });
  });

  socket.on("video-toggle", ({ roomId, userId, isEnabled }) => {
    console.log(
      `[WebRTC] Video toggle from ${userId} in room ${roomId}: ${isEnabled}`
    );
    // Отправляем всем в комнате (кроме отправителя)
    socket.to(roomId).emit("video-toggle", { userId, isEnabled });
  });

  // Обработка speaking state
  socket.on("speaking-state", ({ roomId, userId, isSpeaking }) => {
    // Отправляем всем в комнате (кроме отправителя)
    socket.to(roomId).emit("speaking-state", { userId, isSpeaking });
  });

  // Обработка сообщений чата
  socket.on(
    "chat-message",
    async ({ roomId, userId, content, senderName, guestId }) => {
      console.log(`[Chat] Received message:`, {
        roomId,
        userId,
        guestId,
        senderName,
        content,
      });

      const user = findUserBySocketId(socket.id);
      if (!user || user.roomId !== roomId) {
        console.warn(`[Chat] User ${socket.id} is not in room ${roomId}`);
        return;
      }

      try {
        // Определяем имя отправителя
        const finalSenderName = senderName || "Гость";

        console.log(
          `[Chat] Preparing to save message with senderName: "${finalSenderName}"`
        );

        // Валидация UUID
        const uuidRegex =
          /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

        if (!uuidRegex.test(roomId)) {
          console.error(`[Chat] Invalid roomId/sessionId format: ${roomId}`);
          throw new Error("Invalid sessionId format");
        }

        if (userId && !uuidRegex.test(userId)) {
          console.error(`[Chat] Invalid userId format: ${userId}`);
          throw new Error("Invalid userId format");
        }

        if (guestId && !uuidRegex.test(guestId)) {
          console.error(`[Chat] Invalid guestId format: ${guestId}`);
          throw new Error("Invalid guestId format");
        }

        // Проверяем существование сессии
        console.log(`[Chat] Checking if session exists: ${roomId}`);
        try {
          const sessionExists = await serverSessionService.findById(roomId);
          if (!sessionExists) {
            console.error(`[Chat] Session not found in database: ${roomId}`);
            console.error(
              `[Chat] This might cause foreign key constraint error`
            );
            // Не возвращаемся - попробуем сохранить и увидим точную ошибку БД
          } else {
            console.log(`[Chat] Session found:`, sessionExists.id);
          }
        } catch (sessionCheckError) {
          console.error(`[Chat] Error checking session:`, sessionCheckError);
          // Продолжаем - увидим точную ошибку при сохранении
        }

        // Сохраняем сообщение в БД
        // Приоритет: сначала userId (если авторизован), затем guestId (если гость)
        const messageData = {
          sessionId: roomId,
          userId: userId || null, // userId для авторизованных пользователей
          guestId: userId ? null : (guestId || null), // guestId только если нет userId
          senderName: finalSenderName, // Имя отправителя
          content,
          type: "text" as const,
        };

        console.log(
          `[Chat] Saving message to DB:`,
          JSON.stringify(messageData, null, 2)
        );
        const savedMessage = await saveChatMessage(messageData);
        console.log(`[Chat] Message saved successfully:`, savedMessage);

        // Формируем сообщение для отправки клиентам
        // senderId - это либо userId (приоритет), либо guestId
        const messageToSend = {
          id: savedMessage.id,
          senderId: savedMessage.userId || savedMessage.guestId,
          senderName: savedMessage.senderName,
          content: savedMessage.content,
          timestamp: savedMessage.createdAt,
          type: savedMessage.type,
        };

        // Отправляем всем в комнате (включая отправителя)
        io.to(roomId).emit("chat-message", messageToSend);
        console.log(`[Chat] Message broadcasted to room ${roomId}`);
      } catch (error) {
        console.error(`[Chat] Error saving message:`, error);
        console.error(`[Chat] Error details:`, JSON.stringify(error, null, 2));

        // Отправляем детальную ошибку на клиент
        const errorMessage =
          error instanceof Error ? error.message : "Failed to save message";
        const errorDetails =
          error && typeof error === "object"
            ? (error as any).detail || (error as any).message || errorMessage
            : errorMessage;

        console.error(`[Chat] Sending error to client:`, errorDetails);

        socket.emit("chat-error", {
          message: errorDetails,
        });
      }
    }
  );

  // Отключение
  socket.on("disconnect", () => {
    console.log(`[WebRTC] User disconnected: ${socket.id}`);

    // Найти пользователя по socket ID
    const disconnectedUser = findUserBySocketId(socket.id);

    if (disconnectedUser) {
      users.delete(disconnectedUser.id);

      if (disconnectedUser.roomId) {
        const room = rooms.get(disconnectedUser.roomId);
        if (room) {
          room.participants.delete(disconnectedUser.id);
          socket
            .to(disconnectedUser.roomId)
            .emit("user-left", disconnectedUser.id);

          // Удалить пустую комнату
          if (room.participants.size === 0) {
            rooms.delete(disconnectedUser.roomId);
            console.log(
              `[WebRTC] Deleted empty room ${disconnectedUser.roomId}`
            );
          }
        }
      }
    }
  });
});

// Запуск фоновой задачи для автоматического назначения серверов
const AUTO_ASSIGN_INTERVAL_MS = parseInt(
  process.env.AUTO_ASSIGN_INTERVAL_MS || "1000",
  10
); // 1 секунда по умолчанию

async function autoAssignServersTask() {
  try {
    const results = await serverSessionService.autoAssignServers();

    if (results.total > 0) {
      console.log(
        `[${new Date().toISOString()}] 🎯 Auto-assign: ${
          results.assigned
        } assigned, ${results.failed} failed из ${results.total}`
      );

      if (results.errors.length > 0) {
        console.error(
          `[${new Date().toISOString()}] ❌ Ошибки назначения:`,
          results.errors
        );
      }
    }
  } catch (error) {
    console.error(
      `[${new Date().toISOString()}] ❌ Ошибка auto-assign:`,
      error instanceof Error ? error.message : error
    );
  }

  // Планируем следующий запуск
  setTimeout(autoAssignServersTask, AUTO_ASSIGN_INTERVAL_MS);
}

// Запускаем через 1 секунду после старта сервера
setTimeout(() => {
  console.log(
    `[${new Date().toISOString()}] 🤖 Запуск фоновой задачи auto-assign (интервал: ${AUTO_ASSIGN_INTERVAL_MS}ms)`
  );
  autoAssignServersTask();
}, 1000);
