import express, { Application, Request, Response } from "express";

import { NextFunction } from "express";
import SocketChannels from "@nirvana/core/sockets/channels";
import { UserService } from "./services/user.service";
import { UserStatus } from "@nirvana/core/models";
import cors from "cors";
import getConversationRoutes from "./routes/conversation";
import getRtcRoutes from "./routes/rtc";
import getSearchRoutes from "./routes/search";
import getUserRoutes from "./routes/user";

const app = express();

app.use(cors());
app.use(express.json());

app.use((req: Request, res: Response, next: NextFunction) => {
  console.log("Time: ", Date.now());
  next();
});

app.get("/", (req: Request, res: Response) => {
  res.send("hello world.");
});

app.use("/api/rtc", getRtcRoutes());

app.use("/api/user", getUserRoutes());
app.use("/api/search", getSearchRoutes());
app.use("/api/conversations", getConversationRoutes());

const PORT = 5000;
var server = app.listen(PORT, () =>
  console.log(`express running on port ${PORT}`)
);

// socket IO stuff

const io = require("socket.io")(server, {
  // todo: add protection
  cors: {
    origin: "*",
  },
});

io.on("connection", function (socket: any) {
  // add to map between between googleUserIds and socketId

  console.log("a user connected");

  // ===== JOIN ====
  /** User wants to subscribe to live emissions of a conversation */
  socket.on(SocketChannels.JOIN_ROOM, (relationshipId: string) => {
    // add this user to the room

    console.log(
      `${socket.id} user joined room for relationship ${relationshipId}`
    );

    socket.join(relationshipId);

    console.log(`${socket.id} now in rooms ${socket.rooms}`);
  });

  // ==== UPDATES ====
  // can be a change of:
  // 1. status...later...do short polling for this instead
  // 2. new content: audio clip or link
  // 3. someone is starting to speak
  // send message to everyone in room except sender...

  /** User wants to send some update to a particular room */
  socket.on(
    SocketChannels.SEND_AUDIO_CLIP,
    (relationshipId: string, audioChunks: any) => {
      console.log(`new audio chunks received..routing to appropriate room!`);
      console.log(relationshipId);
      console.log(audioChunks);

      io.in(relationshipId).emit(
        SocketChannels.SEND_AUDIO_CLIP,
        relationshipId,
        audioChunks
      );
    }
  );

  // tell everyone in a room when someone is starting to speak
  socket.on(SocketChannels.SEND_STARTED_SPEAKING, (relationshipId: string) => {
    console.log(`started speaking in ${relationshipId}`);
    io.in(relationshipId).emit(
      SocketChannels.SEND_STARTED_SPEAKING,
      relationshipId
    );
  });

  // tell everyone in a room when someone is stopping to speak
  socket.on(SocketChannels.SEND_STOPPED_SPEAKING, (relationshipId: string) => {
    console.log(`stopped speaking in ${relationshipId}`);
    io.in(relationshipId).emit(
      SocketChannels.SEND_STOPPED_SPEAKING,
      relationshipId
    );
  });

  // change of user status to all of users' rooms and db update
  socket.on(
    SocketChannels.SEND_USER_STATUS_UPDATE,
    async (userGoogleId: string, newStatus: UserStatus) => {
      console.log("new status for user");

      const resultUpdate = await UserService.updateUserStatus(
        userGoogleId,
        newStatus
      );

      // tell all rooms that the user is part of
      // that this user has updated their status
      if (resultUpdate?.modifiedCount) {
        socket.rooms.forEach((roomId: string) => {
          io.in(roomId).emit(
            SocketChannels.SEND_USER_STATUS_UPDATE,
            userGoogleId,
            newStatus
          );
        });
      }
    }
  );

  // ==== DISCONNECT ====
  socket.on("disconnect", () => {
    console.log("user disconnected");
  });
});
