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.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

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

@Service
public class RoomManager implements IRoomManager {
  private volatile ArrayList<VUser> users = new ArrayList<VUser>();
  private volatile ArrayList<VRoom> rooms = new ArrayList<VRoom>();

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

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

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

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

    users.add(user);

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

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

  public void leave(WebSocketSession session) throws InterruptedException, IOException {
    Optional<VUser> ouser = users.stream().filter(user -> user.getSession().equals(session)).findFirst();

    if (ouser.isEmpty()) {
      sendMessage(session, "error", "need login");
      return;
    }

    VUser user = ouser.get();

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

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

  public void createRoom(WebSocketSession session) throws InterruptedException, IOException {
    if (!users.stream().anyMatch(user -> user.getSession().equals(session))) {
      sendMessage(session, "error", "need login");
      return;
    }

    VUser user = users.stream().filter(suser -> suser.getSession().equals(session)).findFirst().get();

    if (user.getRoom() != null) {
      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>());

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

    sendMessage(session, "roomCreate", roomCode);

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

  public void connectRoom(WebSocketSession session, String roomCode) throws InterruptedException, IOException {
    if (roomCode == null) {
      sendMessage(session, "error", "bad command command");
      return;
    }
    if (!users.stream().anyMatch(user -> user.getSession().equals(session))) {
      sendMessage(session, "error", "need login");
      return;
    }

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

    VRoom room = oroom.get();
    VUser user = users.stream().filter(suser -> suser.getSession().equals(session)).findFirst().get();

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

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

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

  @Override
  public void leaveRoom(WebSocketSession session) throws InterruptedException, IOException {
    Optional<VUser> ouser = users.stream().filter(user -> user.getSession().equals(session)).findFirst();

    if (ouser.isEmpty()) {
      sendMessage(session, "error", "need login");
      return;
    }

    VUser user = ouser.get();

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

    user.getRoom().getUsers().remove(user);
    user.setRoom(null);
  }

  @Override
  public void setRoomStatus(WebSocketSession session, TextMessage message) throws InterruptedException, IOException {
    Optional<VUser> ouser = users.stream().filter(user -> user.getSession().equals(session)).findFirst();

    if (ouser.isEmpty()) {
      sendMessage(session, "error", "need login");
      return;
    }

    VUser user = ouser.get();

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

    if (!user.getRoom().getAdmin().equals(user)) {
      sendMessage(session, "error", "not admin");
      return;
    }

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

    user.getRoom().setRoomStatus((JSONObject) jsonObject.get("message"));
    broadcast(user.getRoom(), "roomStatus", user.getRoom().getRoomStatus());
  }

  @Override
  public void setRoomSettings(WebSocketSession session, TextMessage message) throws InterruptedException, IOException {
    Optional<VUser> ouser = users.stream().filter(user -> user.getSession().equals(session)).findFirst();

    if (ouser.isEmpty()) {
      sendMessage(session, "error", "need login");
      return;
    }

    VUser user = ouser.get();

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

    if (!user.getRoom().getAdmin().equals(user)) {
      sendMessage(session, "error", "not admin");
      return;
    }

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

    user.getRoom().setRoomSettings((JSONObject) jsonObject.get("message"));
    broadcast(user.getRoom(), "roomSettings", user.getRoom().getRoomSettings());
  }

  public void sendMessage(WebSocketSession session, String type, String message) throws InterruptedException, IOException {
    session.sendMessage(new TextMessage("{\"type\":\"" + type + "\",\"message\": \"" + message.toString() + "\" }"));
  }

  public void forwardMessage(WebSocketSession session, String type, JSONObject message) throws InterruptedException, IOException {
    session.sendMessage(new TextMessage("{\"type\":\"" + type + "\",\"message\": " + message.toString() + " }"));
  }

  public void broadcast(VRoom room, String type, JSONObject message) {
    room.getUsers().forEach(user -> {
      try {
        forwardMessage(user.getSession(), type, message);
      } catch (InterruptedException | IOException e) {
        e.printStackTrace();
      }
    });
  }

  public void sendServerInfos(WebSocketSession session) throws InterruptedException, IOException {
    session.sendMessage(new TextMessage(
        "{\"type\":\"serverInfos\",\"userCount\":\"" + users.size() + "\",\"roomCount\":\"" + rooms.size() + "\"}"));
  }

  @Override
  public void forwardRoomMessage(WebSocketSession session, TextMessage message) throws InterruptedException, IOException {
    Optional<VUser> ouser = users.stream().filter(user -> user.getSession().equals(session)).findFirst();

    if (ouser.isEmpty()) {
      sendMessage(session, "error", "need login");
      return;
    }

    VUser user = ouser.get();

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

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

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

  }

}