class Server:
    def __init__(self):
        self.__port = 8765
        self.__address = "127.0.0.1"
        self.__manager = Manager()
        self.__observers = {}

    async def __signUp(self, data, websocket):
        username = data["username"]
        password = data["password"]

        if self.__manager.isExistedUsername(username):
            await websocket.send(json.dumps({"response": "existedUsername"}))
            return

        self.__manager.createAccount(username, password)

        await self.__sendResponseToClient("createdAccount", None, websocket)

    async def __login(self, data, websocket):
        username = data["username"]
        password = data["password"]

        if not self.__manager.validateAccount(username, password):
            await websocket.send(json.dumps({"response": "loginFail"}))
            return

        memberObserver = MemberObserver(username, websocket)
        self.__addObserver(memberObserver)

        await self.__sendResponseToClient("loginSuccessful", None, websocket)

        boardTitlesAndIds = self.__manager.getUserBoardTitlesAndIds(username)

        await self.__sendResponseToClient("userBoardTitlesAndIds",
                                          boardTitlesAndIds, websocket)

    async def __sendBoardDetail(self, data, websocket):
        boardId = data["boardId"]
        detail = self.__manager.getBoardDetail(boardId)

        self.__changeObserverCurrentBoardId(websocket, boardId)

        await self.__sendResponseToClient("boardDetail", {
            "boardId": boardId,
            "boardDetail": detail
        }, websocket)

    async def __createBoard(self, data, websocket):
        boardTitle = data["boardTitle"]
        username = data["username"]

        boardId = str(self.__manager.createBoard(boardTitle, username))

        await self.__sendResponseToClient("createdBoard", {
            'boardTitle': boardTitle,
            'boardId': boardId
        }, websocket)

    async def __createSection(self, data, websocket):
        boardId = data["boardId"]
        sectionTitle = data["sectionTitle"]

        sectionId = str(self.__manager.createSection(boardId, sectionTitle))

        await self.__sendResponseToClient(
            "createdSection", {
                "boardId": boardId,
                "sectionTitle": sectionTitle,
                "sectionId": sectionId
            }, websocket)

    async def __createTask(self, data, websocket):
        boardId = data["boardId"]
        sectionId = data["sectionId"]
        taskTitle = data["taskTitle"]
        taskOrder = data["taskOrder"]

        taskId = str(self.__manager.createTask(sectionId, taskTitle,
                                               taskOrder))

        await self.__sendResponseToClient(
            "createdTask", {
                "boardId": boardId,
                "sectionId": sectionId,
                "taskId": taskId,
                "taskTitle": taskTitle,
                "taskOrder": taskOrder
            }, websocket)

    async def __editSectionTitle(self, data, websocket):
        sectionId = data["sectionId"]
        sectionTitle = data["sectionTitle"]

        self.__manager.editSectionTitle(sectionId, sectionTitle)

    async def __editTaskTitle(self, data, websocket):
        taskId = data["taskId"]
        taskTitle = data["taskTitle"]

        self.__manager.editTaskTitle(taskId, taskTitle)

    async def __deleteBoard(self, data, websocket):
        boardId = data["boardId"]
        members = self.__manager.getBoardMembers(boardId)

        self.__manager.deleteBoard(boardId)
        await self.__updateDeleteBoard(boardId, members, websocket)

    async def __deleteSection(self, data, websocket):
        boardId = data["boardId"]
        sectionId = data["sectionId"]

        self.__manager.deleteSection(boardId, sectionId)

    async def __deleteTask(self, data, websocket):
        sectionId = data["sectionId"]
        taskId = data["taskId"]

        self.__manager.deleteTask(sectionId, taskId)

    async def __deleteTaskComment(self, data, websocket):
        taskId = data["taskId"]
        taskCommentOrder = data["taskCommentOrder"]

        self.__manager.deleteTaskComment(taskId, taskCommentOrder)

    async def __deleteTaskTag(self, data, websocket):
        taskId = data["taskId"]
        taskTag = data["taskTag"]

        self.__manager.deleteTaskTag(taskId, taskTag)

    async def __reorderTaskInSameSection(self, data, websocket):
        sectionId = data["sectionId"]
        taskId = data["taskId"]
        taskOrder = data["taskOrder"]

        self.__manager.reorderTaskInSameSection(sectionId, taskId, taskOrder)

    async def __reorderTaskInDifferentSection(self, data, websocket):
        sectionId = data["sectionId"]
        newSectionId = data["newSectionId"]
        taskId = data["taskId"]
        taskOrder = data["taskOrder"]

        self.__manager.reorderTaskInDifferentSection(sectionId, newSectionId,
                                                     taskId, taskOrder)

    async def __addTaskComment(self, data, websocket):
        taskId = data["taskId"]
        taskComment = data["taskComment"]
        memberUsername = data["memberUsername"]
        taskCommentOrder = data["taskCommentOrder"]

        self.__manager.addTaskComment(taskId, taskComment, memberUsername,
                                      taskCommentOrder)

    async def __addTaskTag(self, data, websocket):
        taskId = data["taskId"]
        taskTag = data["taskTag"]
        taskTagColor = data["taskTagColor"]

        self.__manager.addTaskTag(taskId, taskTag, taskTagColor)

    async def __addMemberToBoard(self, data, websocket):
        boardId = data["boardId"]
        memberUsername = data["memberUsername"]

        if not self.__manager.isExistedUsername(memberUsername):
            await self.__sendResponseToClient("memberUsernameDoesNotExist",
                                              None, websocket)
            return

        if self.__manager.isMemberInBoard(boardId, memberUsername):
            return

        self.__manager.addMemberToBoard(boardId, memberUsername)

        boardTitlesAndIds = self.__manager.getUserBoardTitlesAndIds(
            memberUsername)

        await self.__sendResponseToClient("addedMemberToBoard", None,
                                          websocket)

        if self.__isOnline(memberUsername):
            try:
                memberObserver = self.__observers[memberUsername]
                memberWebsocket = memberObserver.getClientWebsocket()

                await self.__sendResponseToClient("updateBoardTitlesAndIds",
                                                  boardTitlesAndIds,
                                                  memberWebsocket)
            except:
                return

    async def __setTaskResponsibleMember(self, data, websocket):
        taskId = data["taskId"]
        memberUsername = data["memberUsername"]

        self.__manager.setTaskResponsibleMember(taskId, memberUsername)

    async def __setTaskDueDate(self, data, websocket):
        taskId = data["taskId"]
        taskDueDate = data["taskDueDate"]

        self.__manager.setTaskDueDate(taskId, taskDueDate)

    async def __setTaskFinishState(self, data, websocket):
        taskId = data["taskId"]
        taskFinishState = data["taskFinishState"]

        self.__manager.setTaskFinishState(taskId, taskFinishState)

    async def __updateBoardDetail(self, websocket):
        username = self.__getUsernameFromWebsocket(websocket)
        observer = self.__observers[username]
        currentBoardId = observer.getCurrentBoardId()
        boardDetail = self.__manager.getBoardDetail(currentBoardId)
        members = self.__manager.getBoardMembers(currentBoardId)
        data = {"boardId": currentBoardId, "boardDetail": boardDetail}

        members.remove(username)

        updateMembers = self.__getOnlineBoardMembersOpeningCurrentBoard(
            members, currentBoardId)

        await self.__notifyObservers(updateMembers, data, "updateBoard")

    async def __updateDeleteBoard(self, boardId, members, websocket):
        username = self.__getUsernameFromWebsocket(websocket)
        observer = self.__observers[username]
        data = {"deletedBoardId": boardId}

        members.remove(username)

        membersOpeningDeletedBoard = self.__getOnlineBoardMembersOpeningCurrentBoard(
            members, boardId)
        membersNotOpeningDeletedBoard = self.__getOnlineBoardMembersNotOpeningCurrentBoard(
            members, boardId)

        await self.__notifyObservers(membersOpeningDeletedBoard, data,
                                     "deletedBoardError")
        await self.__notifyObservers(membersNotOpeningDeletedBoard, data,
                                     "deletedBoard")

    async def __notifyObservers(self, members, data, response):
        for member in members:
            memberObserver = self.__observers[member]

            await memberObserver.update(data, response)

    async def __sendResponseToClient(self, response, data, websocket):
        await websocket.send(json.dumps({"response": response, "data": data}))

    def __addObserver(self, memberObserver):
        self.__observers[memberObserver.getUsername()] = memberObserver

    def __changeObserverCurrentBoardId(self, websocket, boardId):
        username = self.__getUsernameFromWebsocket(websocket)
        observer = self.__observers[username]

        observer.changeCurrentBoardId(boardId)

    def __getUsernameFromWebsocket(self, websocket):
        for username, observer in self.__observers.items():
            if observer.getClientWebsocket() == websocket:
                return username

    def __getOnlineBoardMembersOpeningCurrentBoard(self, members, boardId):
        onlineMembers = self.__getOnlineMembers(members)

        return list(
            filter(
                lambda member: self.__isOpeningCurrentBoard(member, boardId),
                onlineMembers))

    def __getOnlineBoardMembersNotOpeningCurrentBoard(self, members, boardId):
        onlineMembers = self.__getOnlineMembers(members)

        return list(
            filter(
                lambda member: not self.__isOpeningCurrentBoard(
                    member, boardId), onlineMembers))

    def __getOnlineMembers(self, members):
        return list(filter(lambda member: self.__isOnline(member), members))

    def __isOnline(self, username):
        return username in self.__observers

    def __isOpeningCurrentBoard(self, member, boardId):
        observer = self.__observers[member]

        return observer.getCurrentBoardId() == boardId

    async def __handleMessage(self, message, websocket):
        action = message["action"]

        if action == 'signUp':
            await self.__signUp(message["data"], websocket)

        elif action == 'login':
            await self.__login(message["data"], websocket)

        elif action == 'createBoard':
            await self.__createBoard(message["data"], websocket)

        elif action == 'createSection':
            await self.__createSection(message["data"], websocket)
            await self.__updateBoardDetail(websocket)

        elif action == 'createTask':
            await self.__createTask(message["data"], websocket)
            await self.__updateBoardDetail(websocket)

        elif action == 'requestBoardDetail':
            await self.__sendBoardDetail(message["data"], websocket)

        elif action == 'editSectionTitle':
            await self.__editSectionTitle(message["data"], websocket)
            await self.__updateBoardDetail(websocket)

        elif action == 'editTaskTitle':
            await self.__editTaskTitle(message["data"], websocket)
            await self.__updateBoardDetail(websocket)

        elif action == 'deleteBoard':
            await self.__deleteBoard(message["data"], websocket)

        elif action == 'deleteSection':
            await self.__deleteSection(message["data"], websocket)
            await self.__updateBoardDetail(websocket)

        elif action == 'deleteTask':
            await self.__deleteTask(message["data"], websocket)
            await self.__updateBoardDetail(websocket)

        elif action == 'deleteTaskComment':
            await self.__deleteTaskComment(message["data"], websocket)
            await self.__updateBoardDetail(websocket)

        elif action == 'deleteTaskTag':
            await self.__deleteTaskTag(message["data"], websocket)
            await self.__updateBoardDetail(websocket)

        elif action == 'reorderTaskInSameSection':
            await self.__reorderTaskInSameSection(message["data"], websocket)
            await self.__updateBoardDetail(websocket)

        elif action == 'reorderTaskInDifferentSection':
            await self.__reorderTaskInDifferentSection(message["data"],
                                                       websocket)
            await self.__updateBoardDetail(websocket)

        elif action == 'addTaskComment':
            await self.__addTaskComment(message["data"], websocket)
            await self.__updateBoardDetail(websocket)

        elif action == 'addTaskTag':
            await self.__addTaskTag(message["data"], websocket)
            await self.__updateBoardDetail(websocket)

        elif action == 'addMemberToBoard':
            await self.__addMemberToBoard(message["data"], websocket)
            await self.__updateBoardDetail(websocket)

        elif action == 'setTaskResponsibleMember':
            await self.__setTaskResponsibleMember(message["data"], websocket)
            await self.__updateBoardDetail(websocket)

        elif action == 'setTaskDueDate':
            await self.__setTaskDueDate(message["data"], websocket)
            await self.__updateBoardDetail(websocket)

        elif action == 'setTaskFinishState':
            await self.__setTaskFinishState(message["data"], websocket)
            await self.__updateBoardDetail(websocket)

        else:
            return

    def getPort(self):
        return self.__port

    def getAddress(self):
        return self.__address

    async def handleClient(self, websocket, path):
        async for message in websocket:
            message = json.loads(message)

            await self.__handleMessage(message, websocket)