from Models import ProjectModel, GroupMatchingDeclarationModel
from app import db
from Models.UserModel import UserModel
from Models.MatchingPreferencesModel import MatchingPreferencesModel
from Models.LikeModel import LikeModel
from Models.DislikeModel import DislikeModel
from Matching.ConcreteMatcher import PreferenceMatcher, ProjectMatcher, SingleProjectMatcher
from Rest.Answer import Answer, MessageCodes, try_catch_error_answer
from Matching.PreferenceTyps import *


@try_catch_error_answer
class MatchesREST:
    @staticmethod
    def get_matches(user, match_type, project_preference, age_group_preference, interests_preference, project_id, amount):
        answer = Answer()

        match_config = {'type': match_type, 'custom': {}}
        if project_preference:
            match_config['custom']['ProjectPreference'] = project_preference
        if age_group_preference:
            match_config['custom']['AgeGroupPreference'] = age_group_preference
        if interests_preference:
            match_config['custom']['InterestsPreference'] = interests_preference
        if project_id:
            match_config['custom']['project_id'] = project_id

        # activate specified matching type
        if match_config['type'] == 'Projects':
            matcher = ProjectMatcher.ProjectMatcher(user)
        elif match_config['type'] == 'Preference':
            if config := match_config.get('custom', None):
                preference = MatchingPreferencesModel(
                    project_pref=config.get('ProjectPreference', ProjectPreference.DONT_CARE),
                    age_group_pref=config.get('AgeGroupPreference', AgeGroupPreference.DONT_CARE),
                    interests_pref=config.get('InterestsPreference', InterestsPreferences.DONT_CARE)
                )
                matcher = PreferenceMatcher.PreferenceMatcher(preference, user)
            else:
                matcher = PreferenceMatcher.PreferenceMatcher(user.matching_preferences, user)
        elif match_config['type'] == 'SingleProject':
            if match_config['custom'] and match_config['custom']['project_id']:
                project = ProjectModel.query.get(match_config['custom']['project_id'])
                matcher = SingleProjectMatcher.SingleProjectMatcher(user, project)
            else:
                return answer.add_message(MessageCodes.MATCHING_NO_VALID_MATCHER)
        else:
            return answer.add_message(MessageCodes.MATCHING_NO_VALID_MATCHER)

        matches = [user.get_user_card() for user in matcher.get_more_matches(amount)]

        return answer \
            .add_message(MessageCodes.WORKED) \
            .add_result(matches)

    @staticmethod
    def get_confirmed_matches(user):
        answer = Answer()

        matches = user.get_matched_users()

        answer.add_message(MessageCodes.WORKED)
        if not matches:
            answer.add_message(MessageCodes.MATCHING_NO_MATCHES)

        for match in matches:
            if [chat for chat in user.chats if match in chat.users and chat.channel.posts]:
                match.has_chat = True

        return answer.add_result(matches)

    @staticmethod
    def post_like_user(user, likes_id):
        answer = Answer()
        liked_user = UserModel.query.get(likes_id)
        # check is already matched
        if liked_user in user.get_matched_users():
            return answer.add_message(MessageCodes.MATCHING_ALREADY_MATCHED)
        # check if liked already
        if liked_user in user.likes:
            return answer.add_message(MessageCodes.MATCHING_ALREADY_LIKED)
        # check if already disliked
        dislike = DislikeModel.query.filter_by(user=user, dislikes=liked_user).first()
        if dislike:
            db.session.delete(dislike)
            lm = LikeModel(user=user, likes=liked_user)
            db.session.add(lm)
            db.session.commit()
            return answer.add_message(MessageCodes.MATCHING_DISLIKE_REVERTED)
        lm = LikeModel(user=user, likes=liked_user)
        db.session.add(lm)
        db.session.commit()
        return answer.add_message(MessageCodes.WORKED)

    @staticmethod
    def post_dislike(user, dislikes_id):
        answer = Answer()

        disliked_user = UserModel.query.get(dislikes_id)
        # check if already matched
        if disliked_user in user.get_matched_users():
            return answer.add_message(MessageCodes.MATCHING_ALREADY_MATCHED)
        # check if already disliked
        if disliked_user in user.dislikes:
            return answer.add_message(MessageCodes.MATCHING_ALREADY_DISLIKED)
        # check if already liked
        like = LikeModel.query.filter_by(user=user, likes=disliked_user).first()
        if like:
            db.session.delete(like)
            dl = DislikeModel(user=user, dislikes=disliked_user)
            db.session.add(dl)
            db.session.commit()
            return answer.add_message(MessageCodes.MATCHING_LIKE_REVERTED)
        dl = DislikeModel(user=user, dislikes=disliked_user)
        db.session.add(dl)
        db.session.commit()
        return answer.add_message(MessageCodes.WORKED)

    @staticmethod
    def post_group_matching(user, project_id, status):
        answer = Answer()

        if project_id and project_id != "None":
            project = ProjectModel.query.get(project_id)
            if not project:
                return answer.add_message(MessageCodes.PROJECT_NO_SUCH_PROJECT)
        else:
            project = None
        declaration = GroupMatchingDeclarationModel.query.filter_by(declared_project=project).first()

        # create if no declaration in found
        if not declaration:
            declaration = GroupMatchingDeclarationModel(declared_project=project)

        if project and user not in project.attendees:
            return answer.add_message(MessageCodes.PROJECT_ONLY_FOR_ATTENDEES)
        # wants to join
        if status:
            if user in declaration.matchable_users:
                return answer.add_message(MessageCodes.MATCHING_GROUP_ALREADY_REGISTERED)
            else:
                declaration.matchable_users.append(user)
                db.session.add(declaration)
                db.session.commit()
                return answer.add_message(MessageCodes.WORKED)
        # wants to leave
        if not status:
            if user not in declaration.matchable_users:
                return answer.add_message(MessageCodes.MATCHING_GROUP_ALREADY_LEFT)
            else:
                declaration.matchable_users.remove(user)
                db.session.add(declaration)
                db.session.commit()
                return answer.add_message(MessageCodes.WORKED)

    @staticmethod
    def get_group_matching(user, project_id):
        answer = Answer()

        if project_id and project_id != "None":
            project = ProjectModel.query.get(project_id)
            if not project:
                return answer.add_message(MessageCodes.PROJECT_NO_SUCH_PROJECT)
        else:
            project = None
        declaration = GroupMatchingDeclarationModel.query.filter_by(declared_project=project).first()

        # create if no declaration in found
        if not declaration:
            declaration = GroupMatchingDeclarationModel(declared_project=project)
            db.session.add(declaration)
            db.session.commit()

        # lets user know if the user is enrolled
        declaration.declared = user in declaration.matchable_users

        return answer.add_message(MessageCodes.WORKED).add_result(declaration)

    @staticmethod
    def get_check_group_matching(user, project_list):
        answer = Answer()

        if not project_list:
            project_list = [p.id for p in user.personal_information.projects]
            project_list.append("None")
        result = []
        for project_id in project_list:
            if project_id != "None":
                project = ProjectModel.query.get(project_id)
                if not project:
                    return answer.add_message(MessageCodes.PROJECT_NO_SUCH_PROJECT)
            else:
                project = None

            declaration = GroupMatchingDeclarationModel.query.filter_by(declared_project=project).first()
            if not declaration:
                declaration = GroupMatchingDeclarationModel(declared_project=project)
                db.session.add(declaration)
                db.session.commit()
            if user in declaration.matchable_users:
                result.append(project_id)

        return answer.add_message(MessageCodes.WORKED).add_result({"IDs": result})
