package gltronic.voozik.business;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Optional;
import java.util.Random;

import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import gltronic.voozik.model.Server;
import gltronic.voozik.model.ServerStatus;
import gltronic.voozik.model.VRoom;
import gltronic.voozik.model.VUser;

@Service
public class RoomManager implements IRoomManager {
  @Autowired
  Server server;

  @Override
  public void login(WebSocketSession session, String name) throws InterruptedException, IOException {
    if (name == null) {
      SocketUtils.sendMessage(session, "error", "bad command command");
      return;
    }

    if (server.users.stream().anyMatch(user -> user.getSession().equals(session))) {
      SocketUtils.sendMessage(session, "login", "false");
      return;
    }

    if (server.users.stream().anyMatch(user -> user.getName().equals(name))) {
      SocketUtils.sendMessage(session, "login", "false");
      return;
    }

    VUser user = new VUser();
    user.setName(name);
    user.setSession(session);

    server.users.add(user);

    SocketUtils.sendMessage(session, "login", "true");
    sendServerInfos(session);

    System.err.println("[ROOM] Logged " + name);
  }

  @Override
  public void leave(WebSocketSession session) throws InterruptedException, IOException {
    VUser user = UserUtils.getUser(session, server.users);
    if (user == null) return;

    server.users.remove(user);
    Optional<VRoom> oroom = server.rooms.stream().filter(room -> room.getUsers().contains(user)).findFirst();

    if (oroom.isPresent()) {
      oroom.get().getUsers().remove(user);
    }
  }

  @Override
  public void createRoom(WebSocketSession session) throws InterruptedException, IOException {
    VUser user = UserUtils.getUser(session, server.users);
    if (user == null) return;

    if (user.getRoom() != null) {
      SocketUtils.sendMessage(session, "error", "no already in room");
      return;
    }

    Random random = new Random();
    String roomCode = Integer.toString(random.nextInt(9999));
    while (roomCode.length() < 4)
      roomCode += 0 + roomCode;

    VRoom room = new VRoom();

    room.setAdmin(user);
    room.setCode(roomCode);
    room.setPublic(false);
    room.setUsers(new ArrayList<VUser>());

    server.rooms.add(room);
    user.setRoom(room);

    SocketUtils.sendMessage(session, "roomCreate", roomCode);

    System.err.println("[ROOM] Created room " + roomCode + " by " + user.getName());
  }

  @Override
  public void connectRoom(WebSocketSession session, String roomCode) throws InterruptedException, IOException {
    if (roomCode == null) {
      SocketUtils.sendMessage(session, "error", "bad command command");
      return;
    }

    Optional<VRoom> oroom = server.rooms.stream().filter(room -> room.getCode().equals(roomCode)).findFirst();
    if (oroom.isEmpty()) {
      SocketUtils.sendMessage(session, "error", "no room");
      return;
    }

    VRoom room = oroom.get();
    VUser user = UserUtils.getUser(session, server.users);
    if (user == null) return;

    user.setRoom(room);
    room.getUsers().add(user);

    ArrayList<String> usersName = new ArrayList<String>();
    room.getUsers().forEach(userL -> usersName.add(userL.getName()));

    JSONObject roomStatus = room.getRoomStatus();
    roomStatus.put("users", usersName);
    room.setRoomStatus(roomStatus);

    SocketUtils.sendMessage(session, "roomConnect", "true");
    SocketUtils.forwardMessage(session, "roomSettings", room.getRoomSettings());
    SocketUtils.broadcast(room, "roomStatus", room.getRoomStatus());
    SocketUtils.forwardMessage(room.getAdmin().getSession(), "roomStatus", room.getRoomStatus());

    System.err
        .println("[ROOM] Connection to room " + roomCode + " (" + room.getAdmin().getName() + ") by " + user.getName());
  }

  @Override
  public void leaveRoom(WebSocketSession session) throws InterruptedException, IOException {
    VUser user = UserUtils.getUser(session, server.users);
    if (user == null) return;

    VRoom room = UserUtils.getRoom(session, user);
    if (room == null) return;

    room.getUsers().remove(user);
    user.setRoom(null);

    ArrayList<String> usersName = new ArrayList<String>();
    room.getUsers().forEach(userL -> usersName.add(userL.getName()));

    JSONObject roomStatus = room.getRoomStatus();
    roomStatus.put("users", usersName);
    room.setRoomStatus(roomStatus);

    SocketUtils.broadcast(room, "roomStatus", room.getRoomStatus());
    SocketUtils.forwardMessage(room.getAdmin().getSession(), "roomStatus", room.getRoomStatus());
  }

  @Override
  public void setRoomStatus(WebSocketSession session, TextMessage message) throws InterruptedException, IOException {
    VUser user = UserUtils.getUser(session, server.users);
    if (user == null) return;

    VRoom room = UserUtils.getRoom(session, user);
    if (room == null) return;

    if (!UserUtils.checkIsAdmin(session, user)) return;

    String payload = message.getPayload();
    JSONObject jsonObject = new JSONObject(payload);

    ArrayList<String> usersName = new ArrayList<String>();
    room.getUsers().forEach(userL -> usersName.add(userL.getName()));

    JSONObject roomStatus = (JSONObject) jsonObject.get("message");
    roomStatus.put("users", usersName);

    room.setRoomStatus(roomStatus);
    SocketUtils.broadcast(room, "roomStatus", room.getRoomStatus());
    SocketUtils.forwardMessage(session, "roomStatus", room.getRoomStatus());
  }

  @Override
  public void setRoomSettings(WebSocketSession session, TextMessage message) throws InterruptedException, IOException {
    VUser user = UserUtils.getUser(session, server.users);
    if (user == null) return;

    VRoom room = UserUtils.getRoom(session, user);
    if (room == null) return;

    if (!UserUtils.checkIsAdmin(session, user)) return;

    String payload = message.getPayload();
    JSONObject jsonObject = new JSONObject(payload);

    room.setRoomSettings((JSONObject) jsonObject.get("message"));
    SocketUtils.broadcast(room, "roomSettings", room.getRoomSettings());
  }

  @Override
  public void forwardRoomMessage(WebSocketSession session, TextMessage message) throws InterruptedException, IOException {
    VUser user = UserUtils.getUser(session, server.users);
    if (user == null) return;

    VRoom room = UserUtils.getRoom(session, user);
    if (room == null) return;

    String payload = message.getPayload();
    JSONObject jsonObject = new JSONObject(payload);
    String type = (String) jsonObject.get("type");
    JSONObject data = (JSONObject) jsonObject.get("message");

    if (room.getAdmin().equals(user)) SocketUtils.broadcast(room, type, data);
    else SocketUtils.forwardMessage(room.getAdmin().getSession(), type, data);

  }

  @Override
  public void kickUser(WebSocketSession session, String username) throws InterruptedException, IOException {
    VUser user = UserUtils.getUser(session, server.users);
    if (user == null) return;

    VRoom room = UserUtils.getRoom(session, user);
    if (room == null) return;

    if (!UserUtils.checkIsAdmin(session, user)) return;

    if (username.isEmpty()) {
      SocketUtils.sendMessage(session, "error", "bad command command");
      return;
    }

    Optional<VUser> ouserToKick = room.getUsers().stream().filter(userL -> userL.getName().equals(username)).findFirst();

    if (!ouserToKick.isPresent()) return;

    VUser userToKick = ouserToKick.get();

    room.getUsers().remove(userToKick);

    SocketUtils.sendMessage(userToKick.getSession(), "roomUserKick", "true");

    SocketUtils.broadcast(room, "roomStatus", room.getRoomStatus());
    SocketUtils.forwardMessage(session, "roomStatus", room.getRoomStatus());
  }

  public void sendServerInfos(WebSocketSession session) throws InterruptedException, IOException {
    ServerStatus status = new ServerStatus();
    status.setRoomCount(server.rooms.size());
    status.setUserCount(server.users.size());
    status.setRooms(new ArrayList<String>());

    server.rooms.forEach(room -> {
      if(room.isPublic()) status.getRooms().add(room.getCode());
    });

    SocketUtils.forwardMessage(session, "serverInfos", new JSONObject(status));
  }
}