"""
The Docker Container "database" needs to run to execute this tests properly
"""

import unittest
from app import db
from Models.ProjectModel import ProjectModel
from Models.UserModel import UserModel
from Models.ChannelModel import ChannelModel, get_main_channel, create_main_channel
from Models.PostModel import PostModel
from Rest.ChannelsREST import ChannelsREST
from Rest.Answer import MessageCodes
from datetime import datetime, timedelta


def prepare_db():
    db.session.commit()
    db.drop_all()
    db.create_all()
    db.session.commit()


def setup_one():
    """
    One project with channel, one user that is not attending the project of the channel.
    One Post, with user one as author, in the only channel.
    """
    prepare_db()
    # create project with channel
    channel_one = ChannelModel()
    db.session.add(channel_one)
    db.session.commit()
    project_one = ProjectModel(channel=channel_one, name="project_one")
    db.session.add(project_one)
    db.session.commit()
    # create user
    user_one = UserModel.create("user_1", "password", "random@mail.de")
    # create post
    post_one = PostModel(channel=channel_one, author=user_one, content="Hi")
    return {"user_one": user_one, "channel_one": channel_one, "project_one": project_one, "post_one": post_one}


def setup_two():
    """
    One project with channel. Two users. User one is project leader of the project.
    Both users attend the project. Two posts, one by user one, one by user two.
    Post one was made before post two.
    The content of the posts is also passed.
    """
    prepare_db()
    content_one = "Hello how are u doing?"
    content_two = "good"
    # create users
    user_one = UserModel.create("user_1", "password", "random1@mail.de")
    user_two = UserModel.create("user_1", "password", "random2@mail.de")
    # create project with channel
    channel_one = ChannelModel()
    db.session.add(channel_one)
    db.session.commit()
    project_one = ProjectModel(channel=channel_one, name="project_one", project_leader=[user_one])
    db.session.add(project_one)
    # let users attend
    project_one.attendees.extend([user_one, user_two])
    # create posts
    post_one = PostModel(channel=channel_one, author=user_one, content=content_one, date_created=datetime(1999, 2, 12))
    post_two = PostModel(channel=channel_one, author=user_two, content=content_two, date_created=datetime(2020, 5, 17))
    [db.session.add(post) for post in [post_one, post_two]]
    db.session.commit()

    return {"user_one": user_one, "user_two": user_two, "project_one": project_one,
            "post_one": post_one, "post_two": post_two, "channel_one": channel_one,
            "content_one": content_one, "content_two": content_two}


def setup_three():
    """global main channel is initialized. Two users. Two posts, one by user one, one by user two.
    Post one was made before post two. The content of the posts is also passed."""
    prepare_db()
    content_one = "Hello how are u doing?"
    content_two = "good"
    # create users
    user_one = UserModel.create("user_1", "password", "random1@mail.de")
    user_two = UserModel.create("user_1", "password", "random2@mail.de")
    # initialize main channel
    create_main_channel()
    main_channel = get_main_channel()
    # create posts
    post_one = PostModel(channel=main_channel, author=user_one, content=content_one, date_created=datetime(1999, 2, 12))
    post_two = PostModel(channel=main_channel, author=user_two, content=content_two, date_created=datetime(2020, 5, 17))
    [db.session.add(post) for post in [post_one, post_two]]
    db.session.commit()

    return {"user_one": user_one, "user_two": user_two, "post_one": post_one, "post_two": post_two,
            "content_one": content_one, "content_two": content_two}


class TestProjectRest(unittest.TestCase):

    def test_setup_one(self):
        setup = setup_one()
        user_one = setup['user_one']
        project_one = setup['project_one']
        self.assertTrue(user_one not in project_one.attendees)
        self.assertEqual(1, len(PostModel.query.all()))

    def test_setup_two(self):
        setup = setup_two()
        user_one = setup['user_one']
        user_two = setup['user_two']
        project_one = setup['project_one']
        channel_one = setup['channel_one']
        post_one = setup['post_one']
        post_two = setup['post_two']
        self.assertTrue([user_one] == project_one.project_leader)
        [self.assertTrue(user in project_one.attendees) for user in [user_one, user_two]]
        self.assertEqual(project_one.channel, channel_one)
        self.assertEqual(2, len(channel_one.posts))
        [self.assertTrue(post in channel_one.posts) for post in [post_one, post_two]]

    def test_get_posts_not_in_project(self):
        # SETUP
        setup = setup_one()
        user_one = setup['user_one']
        channel_one = setup['channel_one']

        # ACTION
        try_get_posts = ChannelsREST.get_channel_posts(user_one.id, channel_one.id, None, 40)

        # RESULT
        # right message code
        self.assertEqual(MessageCodes.CHANNEL_NOT_AN_ATTENDEE.value, try_get_posts.message)
        # no data is returned
        self.assertEqual(None, try_get_posts.result)

    def test_post_post_not_in_project(self):
        # SETUP
        setup = setup_one()
        user_one = setup['user_one']
        channel_one = setup['channel_one']

        # ACTION
        try_post_post = ChannelsREST.post_to_channel(user_one.id, channel_one.id, "Hello")

        # RESULT
        # right message code
        self.assertEqual(MessageCodes.CHANNEL_NOT_AN_ATTENDEE.value, try_post_post.message)
        # no post is made, there is still only one
        self.assertEqual(1, len(PostModel.query.filter_by(channel=channel_one).all()))

    def test_delete_post_not_leader(self):
        # SETUP
        setup = setup_one()
        user_one = setup['user_one']
        post_one = setup['post_one']

        # ACTION
        try_delete_post = ChannelsREST.delete_channel_post(user_one.id, post_one.id)

        # RESULT
        # right message code
        self.assertEqual(MessageCodes.CHANNEL_ONLY_FOR_PROJECT_LEADERS.value, try_delete_post.message)
        # post still exists
        self.assertTrue(PostModel.query.get(post_one.id))

    def test_get_posts_success(self):
        # SETUP
        setup = setup_two()
        user_two = setup['user_two']
        user_one = setup['user_one']
        channel_one = setup['channel_one']
        content_one = setup['content_one']
        content_two = setup['content_two']

        # ACTION
        get_posts_result = ChannelsREST.get_channel_posts(user_two.id, channel_one.id, None, 40)

        # RESULT
        # right message code
        self.assertEqual(MessageCodes.WORKED.value, get_posts_result.message)
        # two posts returned are the right ones
        self.assertEqual(2, len(get_posts_result.result))
        for post in get_posts_result.result:
            if post.author_id == user_one.id:
                self.assertEqual(content_one, post.content)
                # user that didn't call the get method has unmarked posts
                self.assertFalse(hasattr(post, 'is_self'))
            elif post.author_id == user_two.id:
                self.assertEqual(content_two, post.content)
                # user that called the get method has marked posts
                self.assertTrue(hasattr(post, 'is_self'))
                self.assertTrue(post.is_self)
            else:
                self.assertTrue(False)

    def test_get_posts_success_load_more(self):
        # SETUP
        setup = setup_two()
        user_two = setup['user_two']
        channel_one = setup['channel_one']
        post_one = setup['post_one']

        # ACTION
        get_posts_result = ChannelsREST.get_channel_posts(user_two.id, channel_one.id, post_one.id, 40)

        # RESULT
        # right_message code
        self.assertEqual(MessageCodes.WORKED.value, get_posts_result.message)
        # returned post is only one, post_one, self is not set
        self.assertEqual(1, len(get_posts_result.result))
        self.assertEqual(post_one.id, get_posts_result.result[0].id)
        self.assertFalse(hasattr(get_posts_result.result[0], 'is_self'))

    def test_post_post_success(self):
        # SETUP
        setup = setup_two()
        user_two = setup['user_two']
        channel_one = setup['channel_one']

        # ACTION
        new_content = "Hello im new"
        post_to_channel_result = ChannelsREST.post_to_channel(user_two.id, channel_one.id, new_content)

        # RESULT
        # right message code
        self.assertEqual(MessageCodes.WORKED.value, post_to_channel_result.message)
        # a new post was created, with right content
        self.assertEqual(3, len(channel_one.posts))
        new_post = PostModel.query.filter_by(content=new_content).first()
        self.assertTrue(new_post)
        self.assertEqual(new_post.author_id, user_two.id)

    def test_post_post_muted(self):
        # SETUP
        setup = setup_two()
        user_two = setup['user_two']
        channel_one = setup['channel_one']
        # extra: user two is muted
        user_two.moderation_flags.muted_till = datetime.utcnow() + timedelta(minutes=10)

        # ACTION
        post_muted_to_channel_result = ChannelsREST.post_to_channel(user_two.id, channel_one.id, "Hi")

        # RESULT
        # right message code
        self.assertEqual(MessageCodes.USER_YOUR_MUTED.value, post_muted_to_channel_result.message)
        # no post is added
        self.assertEqual(2, len(ChannelModel.query.get(channel_one.id).posts))

    def test_get_posts_success_global(self):
        setup = setup_three()
        user_two = setup['user_two']
        user_one = setup['user_one']
        content_one = setup['content_one']
        content_two = setup['content_two']

        # ACTION
        get_posts_result = ChannelsREST.get_main_channel_posts(user_two.id, None, 40)

        # RESULT
        # right message code
        self.assertEqual(MessageCodes.WORKED.value, get_posts_result.message)
        # two posts returned are the right ones
        self.assertEqual(2, len(get_posts_result.result))
        for post in get_posts_result.result:
            if post.author_id == user_one.id:
                self.assertEqual(content_one, post.content)
                # user that didn't call the get method has unmarked posts
                self.assertFalse(hasattr(post, 'is_self'))
            elif post.author_id == user_two.id:
                self.assertEqual(content_two, post.content)
                # user that called the get method has marked posts
                self.assertTrue(hasattr(post, 'is_self'))
                self.assertTrue(post.is_self)
            else:
                self.assertTrue(False)

    def test_get_posts_success_load_more_global(self):
        # SETUP
        setup = setup_three()
        user_two = setup['user_two']
        post_one = setup['post_one']

        # ACTION
        get_posts_result = ChannelsREST.get_main_channel_posts(user_two.id, post_one.id, 40)

        # RESULT
        # right_message code
        self.assertEqual(MessageCodes.WORKED.value, get_posts_result.message)
        # returned post is only one, post_one, self is not set
        self.assertEqual(1, len(get_posts_result.result))
        self.assertEqual(post_one.id, get_posts_result.result[0].id)
        self.assertFalse(hasattr(get_posts_result.result[0], 'is_self'))

    def test_post_post_success_global(self):
        # SETUP
        setup = setup_three()
        user_two = setup['user_two']

        # ACTION
        new_content = "Hello im new"
        post_to_main_channel_result = ChannelsREST.post_to_main_channel(user_two.id, new_content)

        # RESULT
        # right message code
        self.assertEqual(MessageCodes.WORKED.value, post_to_main_channel_result.message)
        # a new post was created, with right content
        self.assertEqual(3, len(get_main_channel().posts))
        new_post = PostModel.query.filter_by(content=new_content).first()
        self.assertTrue(new_post)
        self.assertEqual(new_post.author_id, user_two.id)

    def test_post_post_muted_global(self):
        # SETUP
        setup = setup_two()
        user_two = setup['user_two']
        # extra: user two is muted
        user_two.moderation_flags.muted_till = datetime.utcnow() + timedelta(minutes=10)
        # extra: initialize main channel
        create_main_channel()

        # ACTION
        post_muted_to_main_channel_result = ChannelsREST.post_to_main_channel(user_two.id, "test")

        # RESULT
        # right message code
        self.assertEqual(MessageCodes.USER_YOUR_MUTED.value, post_muted_to_main_channel_result.message)
        # no message is created
        self.assertFalse(get_main_channel().posts)

    def test_delete_post_success_leader(self):
        # SETUP
        setup = setup_two()
        user_one = setup['user_one']
        channel_one = setup['channel_one']
        post_two = setup['post_two']

        # ACTION
        delete_post_result = ChannelsREST.delete_channel_post(user_one.id, post_two.id)

        # RESULT
        # right message code
        self.assertEqual(MessageCodes.CHANNEL_POST_DELETED.value, delete_post_result.message)
        # post no longer exists
        self.assertEqual(1, len(channel_one.posts))
        self.assertFalse(PostModel.query.get(post_two.id))

    def test_get_channels_success(self):
        # SETUP
        setup = setup_two()
        user_one = setup['user_one']
        channel_one = setup['channel_one']

        # ACTION
        get_channels_result = ChannelsREST.get_channels(user_one.id, [str(channel_one.id)])

        # RESULT
        # right message code
        self.assertEqual(MessageCodes.WORKED.value, get_channels_result.message)
        # channel is returned
        print(get_channels_result.result)
        self.assertTrue(get_channels_result.result[0])
