class Test_users_manager_get(unittest.TestCase):
    def setUp(self):
        init_DB(status="development", folder=db_path)
        self.assertTrue(os.path.exists(db_path))

        self.db_users_manager = UsersController(db_path)
        self.assertIsNotNone(self.db_users_manager)

        load_env_vars()

    def tearDown(self):
        self.db_users_manager = None
        shutil.rmtree(db_path)
        self.assertFalse(os.path.exists(db_path))

    def test_get_user_existing(self):
        user = self.db_users_manager.get_user("Mathieu")
        self.assertTrue("Mathieu", user)

    def test_get_user_non_existing(self):
        user = self.db_users_manager.get_user("Mathie2u")
        self.assertIsNone(user)

    def test_get_all_users(self):
        existing_users = [(u'David Lawrence'), (u'Mathieu'), (u'Nuno')]

        all_users = self.db_users_manager.get_users()
        for usr in all_users:
            self.assertIn(usr, existing_users)
class ChallengesController(object):
    '''
        Databases to handle challenges
    '''

    def __init__(self, folder="db"):
        '''
            Constructor
        '''

        if not os.path.exists(folder):
            os.mkdir(folder)

        init_DB_session(self, folder)

        self.db_users_manager = UsersController(folder)
        self.db_games_manager = GamesController(folder)

    @db_session_setup
    def get_challenge_details(self, challenge_name):
        '''
            Return a challenge details given its name
        '''
        challenge = self.session.query(Challenge).filter(
                Challenge.title == challenge_name).first()

        if challenge is not None:
            data = challenge
        else:
            data = None
        return data

    @db_session_setup
    def get_last_n_challenges(self, n):
        '''
            Retrieve the last n challenges
        '''
        challenges = []

        for challenge in self.session.query(Challenge).all()[:n]:
            game_data = self.db_games_manager.get_game_details(challenge.game)

            # Possible that the connection to the game API cannot be done
            # In that case
            data = (challenge, game_data)

            challenges.append(data)

        return challenges

    @db_session_setup
    def get_your_challenges(self, username):
        '''
            Retrieve a user challenges given its username
        '''
        challenges_q = self.session.query(Challenge).filter(
                or_(Challenge.user1 == username, Challenge.user2 == username))
        challenges = challenges_q.all()
        challenges_with_games = []

        for challenge in challenges:
            game_data = self.db_games_manager.get_game_details(challenge.game)
            challenges_with_games.append((challenge, game_data))

        return challenges_with_games

    @db_session_setup
    def get_ranking(self):
        '''
            Retrieve a user challenges given its username
            TODO: improve the ranking
        '''

        users = self.db_users_manager.get_users()

        # Index by the number of wins
        ranking = {}
        for user in users:
            query_user = self.session.query(Challenge).filter(
                    Challenge.user2 == user)
            query_user_status = query_user.filter(
                    Challenge.status == 'won')

            nb_wins = len(query_user_status.all())

            tmp = ranking.get(nb_wins, [])
            tmp.append(user)
            ranking[nb_wins] = tmp

        # Sort by number of wins
        for nb_wins in ranking:
            ranking[nb_wins].sort()

        overall_ranking = []

        for rank in ranking:
            overall_ranking.append(ranking[rank])

        return overall_ranking

    @db_session_setup
    def get_all_challenges(self):
        '''
            Retrieve all challenges
        '''
        challenges = self.session.query(Challenge).all()
        challenges_with_games = []

        for challenge in challenges:
            game_data = self.db_games_manager.get_game_details(challenge.game)

            challenges_with_games.append((challenge, game_data))

        return challenges_with_games

    @db_session_setup
    def get_number_unseen_challenges(self, username):
        '''
            Return the number of new challenges
        '''
        challenges_q = self.session.query(Challenge)
        challenges_received = challenges_q.filter(Challenge.user2 == username)
        challenges_accept_or_new = challenges_received.filter(
                or_(Challenge.status == 'new', Challenge.status == 'accepted'))
        nb_unseen = len(challenges_accept_or_new.all())

        return nb_unseen

    @db_session_setup
    def new_challenge(self,
                      title,
                      challenger,
                      contestant,
                      game_name,
                      description,
                      time_limit):
        '''
            Add a new challenge
        '''
        challenge = Challenge(title=title,
                              content=description,
                              user1=challenger, user2=contestant,
                              game=game_name,
                              time_limit=time_limit)
        self.session.add(challenge)
        self.session.commit()

        return True

    def _get_challenge(self, title):
        challenge_unfiltered = self.session.query(Challenge)
        filtered = challenge_unfiltered.filter(Challenge.title == title)
        return filtered.first()

    @db_session_setup
    def accept_challenge(self, title, username):
        '''
            Update the status of a challenge to accepted
        '''
        try:
            challenge = self._get_challenge(title)
            if challenge.user2 == username:
                challenge.status = "accepted"
                self.session.commit()

                return (True, challenge.id)
            else:
                return (False, None)
        except:
            return (False, None)

    def get_challenge_in_progress_status(self, id):
        curr_time = self.challenges_curr_time.get(id, -1)

        if curr_time == -1:
            # TODO: raise a better exception
            raise Exception()

        return curr_time

    @db_session_setup
    def decline_challenge(self, title, username):
        '''
            Update the status of a challenge to declined
        '''
        try:
            challenge = self._get_challenge(title)

            if challenge.user2 == username:
                challenge.status = "declined"
                self.session.commit()
                return True
            else:
                return False
        except:
            return False

    @db_session_setup
    def succeed_challenge(self, title, username):
        '''
            Update the status of a challenge to won
        '''
        try:
            challenge = self._get_challenge(title)

            if challenge.user1 == username and challenge.status != "declined":
                challenge.status = "won"
                self.session.commit()
                return True
            else:
                return False

        except:
            return False

    @db_session_setup
    def fail_challenge(self, title, username):
        '''
            Update the status of a challenge to lost
        '''
        try:
            challenge = self._get_challenge(title)

            if challenge.user1 == username and challenge.status != "declined":
                challenge.status = "lost"
                self.session.commit()
                return True
            else:
                return False
        except:
            return False