import time
from flask_restx import fields

from Blueprints import api
from Rest.Answer import Wrapper


def show_or_not(condition_function, value_function):
    def value(o):
        if not o:
            return None
        if condition_function(o):
            return value_function(o)
        else:
            return None

    return value


def get_rank(user):
    flags = user.moderation_flags
    if flags.is_developer: return "dev"
    if flags.is_moderator: return "mod"
    return None


# TODO add more userdata
description = show_or_not(lambda u: u.privacy_settings.show_description, lambda u: u.personal_information.description)
email = show_or_not(lambda u: u.privacy_settings.show_email, lambda u: u.email)
project_groups = show_or_not(lambda u: u.privacy_settings.show_project_groups,
                             lambda u: "".join([str(p.name) + ', ' for p in u.personal_information.projects])[:-2])
matriculation_date = show_or_not(lambda u: u.privacy_settings.show_matriculation_date,
                                 lambda u: time.mktime(u.personal_information.matriculation_date.timetuple()))
rank = show_or_not(lambda u: u.privacy_settings.show_rank, get_rank)
moderation_flags = show_or_not(lambda u: u.moderation_flags.is_developer, lambda u: u.moderation_flags)
course_of_study = show_or_not(lambda u: u.privacy_settings.show_course_of_study,
                              lambda u: u.personal_information.course_of_study)

# POST

post_a_image_string = api.model("Base64ImageString", {'image': fields.String})

post_submit_a_media = api.model("SubmitMedia",
                                {"title": fields.String,
                                 "description": fields.String,
                                 "link": fields.String,
                                 "icon": fields.String})

post_a_media = api.model("PostMedia",
                         {"id": fields.String,
                          "reviewed": fields.Boolean,
                          "title": fields.String,
                          "description": fields.String,
                          "link": fields.String,
                          "icon": fields.String})

post_a_post_content = api.model("PostContent", {'content': fields.String(example="example message")})

post_channels_ids = api.model("ChannelsIds", {'ids': fields.List(fields.String)})

post_a_project = api.model("Pushable_Project",
                           {"id": fields.String, "has_exam": fields.Boolean, "is_private": fields.Boolean,
                            "is_visible": fields.Boolean, "name": fields.String(example="Beispiel Project"),
                            "project_type": fields.String(example="other"), "description": fields.String,
                            "show_only_reviewed_media": fields.Boolean,
                            "project_leader_id": fields.List(fields.String),
                            "media": fields.List(fields.Nested(post_a_media)),
                            "delete_media": fields.List(fields.Nested(post_a_media))})

post_a_password = api.model("Password", {"password": fields.String})

post_a_qrcode = api.model("QR_Data",
                          {"user": fields.String(required=True),
                           "code": fields.String(required=True)})

post_a_reaction = api.model("PostReaction",
                            {"emoji": fields.String(required=True),
                             "remove": fields.Boolean})

post_a_bubble = api.model("PostBubble",
                          {'id': fields.String(required=True),
                           'text': fields.String(required=True)})

post_a_panel = api.model("PostPanel",
                         {'type': fields.String(required=True),
                          'image_id': fields.String,
                          'bubbles': fields.List(fields.Nested(post_a_bubble)),
                          'question_id': fields.String,
                          'question': fields.String,
                          'answer': fields.String,
                          'text': fields.String})

post_a_card = api.model("PostCard",
                        {'image_id': fields.String(required=True),
                         'panels': fields.List(fields.Nested(post_a_panel))})

# GET

a_rights = api.model("ModerationFlags",
                     {"can_post": fields.Boolean(attribute=lambda x: x.data.can_post if x.data else None),
                      "can_delete": fields.Boolean(attribute=lambda x: x.data.can_delete if x.data else None),
                      "can_ban": fields.Boolean(attribute=lambda x: x.data.can_ban if x.data else None),
                      "can_match": fields.Boolean(attribute=lambda x: x.data.can_match if x.data else None),
                      "message_anyone": fields.Boolean(attribute=lambda x: x.data.message_anyone if x.data else None),
                      "is_moderator": fields.Boolean(attribute=lambda x: x.data.is_moderator if x.data else None),
                      "is_developer": fields.Boolean(attribute=lambda x: x.data.is_developer if x.data else None)})

a_matching_preferences = api.model("MatchingPreferences",
                                   {"project_pref": fields.String(attribute=lambda x: x.data.project_pref),
                                    "age_group_pref": fields.String(attribute=lambda x: x.data.age_group_pref),
                                    "interests_pref": fields.String(attribute=lambda x: x.data.interests_pref)})

a_privacy_settings = api.model("PrivacySettings",
                               {"show_profile_picture": fields.Boolean(attribute=lambda x: x.data.show_profile_picture),
                                "show_matriculation_date": fields.Boolean(
                                    attribute=lambda x: x.data.show_matriculation_date),
                                "show_project_groups": fields.Boolean(attribute=lambda x: x.data.show_project_groups),
                                "show_email": fields.Boolean(attribute=lambda x: x.data.show_email),
                                "show_description": fields.Boolean(attribute=lambda x: x.data.show_description),
                                "show_matching_preferences": fields.Boolean(
                                    attribute=lambda x: x.data.show_matching_preferences),
                                "show_course_of_study": fields.Boolean(attribute=lambda x: x.data.show_course_of_study),
                                "accept_direct_messages": fields.Boolean(
                                    attribute=lambda x: x.data.accept_direct_messages),
                                "show_rank": fields.Boolean(attribute=lambda x: x.data.show_rank)})

a_own_user = api.model("OwnUser",
                       {"username": fields.String(attribute=lambda x: x.data.username),
                        "id": fields.String(attribute=lambda x: x.data.id),
                        "display_name": fields.String(attribute=lambda x: x.data.display_name),
                        "theme": fields.String(attribute=lambda x: x.data.theme),
                        "description": fields.String(attribute=lambda x: x.data.personal_information.description),
                        "course_of_study": fields.String(
                            attribute=lambda x: x.data.personal_information.course_of_study),
                        "email": fields.String(attribute=lambda x: x.data.email),
                        "profile_pic_id": fields.String(attribute=lambda x: x.data.profile_pic_id),
                        "score": fields.Integer(attribute=lambda x: x.data.score),
                        "matching_preferences": fields.Nested(a_matching_preferences,
                                                              attribute=lambda x: Wrapper(x.user,
                                                                                          x.data.matching_preferences,
                                                                                          x.lang),
                                                              skip_none=True),
                        "interest_list": fields.List(fields.String,
                                                     attribute=lambda x:
                                                     x.data.personal_information.get_interests(x.lang)),
                        "privacy_settings": fields.Nested(a_privacy_settings,
                                                          attribute=lambda x: Wrapper(x.user,
                                                                                      x.data.privacy_settings,
                                                                                      x.lang),
                                                          skip_none=True),
                        "rank": fields.String(attribute=lambda x: rank(x.data)),
                        "moderation_flags": fields.Nested(a_rights,
                                                          attribute=lambda x: Wrapper(x.user,
                                                                                      moderation_flags(x.data),
                                                                                      x.lang),
                                                          skip_none=True),
                        "matriculation_date": fields.Integer(attribute=lambda x: time.mktime(
                            x.data.personal_information.matriculation_date.timetuple()))})

a_user = api.model("User",
                   {"username": fields.String(attribute=lambda x: x.data.username),
                    "id": fields.String(attribute=lambda x: x.data.id),
                    "display_name": fields.String(attribute=lambda x: x.data.display_name),
                    "description": fields.String(attribute=lambda x: description(x.data)),
                    "course_of_study": fields.String(attribute=lambda x: course_of_study(x.data)),
                    "email": fields.String(attribute=lambda x: email(x.data)),
                    "project_groups": fields.String(attribute=lambda x: project_groups(x.data)),
                    'profile_pic_id': fields.String(attribute=lambda x: x.data.profile_pic_id),
                    "interest_list": fields.List(fields.String,
                                                 attribute=lambda x:
                                                 x.data.personal_information.get_interests(x.lang)),
                    "rank": fields.String(attribute=lambda x: rank(x.data)),
                    "matriculation_date": fields.Integer(attribute=lambda x: matriculation_date(x.data)),
                    "score": fields.Integer(attribute=lambda x: x.data.score),
                    "blocked": fields.Boolean(attribute=lambda x: x.data in x.user.blocked_user)})

a_device = api.model("Device",
                     {'device_id': fields.String(attribute=lambda x: x.data.device_id),
                      'device_name': fields.String(attribute=lambda x: x.data.device_name)})

a_image = api.model("Image", {'id': fields.String(attribute=lambda x: x.data.id)})

a_gmdm = api.model("GroupMatchingDeclaration",
                   {'user_count': fields.Integer(attribute=lambda x: len(x.data.matchable_users)),
                    'declared': fields.Boolean(attribute=lambda x: x.data.declared)})

a_join_request = api.model("JoinRequest",
                           {"id": fields.String(attribute=lambda x: x.data.id),
                            "denied": fields.Boolean(attribute=lambda x: x.data.denied),
                            "user_display_name": fields.String(attribute=lambda x: x.data.user.display_name),
                            "user_id": fields.String(attribute=lambda x: x.data.user.id),
                            "user_username": fields.String(attribute=lambda x: x.data.user.username)})

a_invite = api.model("Invite",
                     {"id": fields.String(attribute=lambda x: x.data.id),
                      "ignored": fields.Boolean(attribute=lambda x: x.data.ignored),
                      "project_id": fields.String(attribute=lambda x: x.data.project.id),
                      "project_name": fields.String(attribute=lambda x: x.data.project.name),
                      "user_display_name": fields.String(attribute=lambda x: x.data.invitee.display_name),
                      "user_id": fields.String(attribute=lambda x: x.data.invitee.id),
                      "user_username": fields.String(attribute=lambda x: x.data.invitee.username),
                      "user_profile_pic_id": fields.String(attribute=lambda x: x.data.invitee.profile_pic_id)})

a_media = api.model("Media",
                    {"id": fields.String(attribute=lambda x: x.data.id),
                     "reviewed": fields.Boolean(attribute=lambda x: x.data.reviewed),
                     "title": fields.String(attribute=lambda x: x.data.title),
                     "description": fields.String(attribute=lambda x: x.data.description),
                     "link": fields.String(attribute=lambda x: x.data.link),
                     "icon": fields.String(attribute=lambda x: x.data.icon),
                     "likes": fields.Integer(attribute=lambda x: x.data.likes),
                     "own_media": fields.Boolean(attribute=lambda x: x.data.submitter == x.user),
                     "already_liked": fields.Boolean(attribute=lambda x: x.data.is_liked_by(x.user))})

a_project = api.model("Project",
                      {"id": fields.String(attribute=lambda x: x.data.id),
                       "has_exam": fields.Boolean(attribute=lambda x: x.data.has_exam),
                       "is_private": fields.Boolean(attribute=lambda x: x.data.is_private),
                       "is_visible": fields.Boolean(attribute=lambda x: x.data.is_visible),
                       "name": fields.String(attribute=lambda x: x.data.name, example="Beispiel Project"),
                       "channel_id": fields.String(attribute=lambda x: x.data.channel_id),
                       "description": fields.String(attribute=lambda x: x.data.description),
                       "show_only_reviewed_media": fields.Boolean(attribute=lambda x: x.data.show_only_reviewed_media),
                       "project_type": fields.String(attribute=lambda x: x.data.get_project_type(x.lang)),
                       "project_leader": fields.List(fields.Nested(a_user),
                                                     attribute=lambda x: [Wrapper(x.user, lead, x.lang) for lead in
                                                                          x.data.project_leader]),
                       "number_of_users": fields.Integer(attribute=lambda x: x.data.len_attendees if x.data else None),
                       "member": fields.List(fields.Nested(a_user),
                                             attribute=lambda x: [Wrapper(x.user, attendee, x.lang) for attendee in
                                                                  x.data.attendees]),
                       "banned_user": fields.List(fields.Nested(a_user),
                                                  attribute=lambda x: [Wrapper(x.user, banned_user, x.lang) for
                                                                       banned_user in x.data.black_list]),
                       "invites": fields.List(fields.Nested(a_invite),
                                              attribute=lambda x: [Wrapper(x.user, invite, x.lang) for invite in
                                                                   x.data.invites]),
                       "join_requests": fields.List(fields.Nested(a_join_request),
                                                    attribute=lambda x: [Wrapper(x.user, join_request, x.lang)
                                                                         for join_request in x.data.join_requests]),
                       "media": fields.List(fields.Nested(a_media),
                                            attribute=lambda x:
                                            [Wrapper(x.user, media, x.lang) for media in x.data.media if media.reviewed]
                                            if x.data.show_only_reviewed_media
                                               and x.user not in x.data.project_leader else
                                            [Wrapper(x.user, media, x.lang) for media in x.data.media])})

a_reactions = api.model("Reactions",
                        {'emoji': fields.String,
                         'count': fields.Integer,
                         'self': fields.Boolean})

a_post = api.model("Post",
                   {'id': fields.String(attribute=lambda x: x.data.id if x.data else None),
                    'content': fields.String(attribute=lambda x: x.data.content if x.data else None),
                    'image_id': fields.String(attribute=lambda x: x.data.image_id if x.data else None),
                    'is_self': fields.Boolean(attribute=lambda x: x.user == x.data.author if x.data else None),
                    'anonymous': fields.Boolean(attribute=lambda x: x.data.anonymous if x.data else None),
                    'date_created': fields.String(
                        attribute=lambda x: x.data.date_created.strftime("%Y/%m/%d/%H/%M/%S/%f")[
                                            :-3] if x.data else None),
                    'linked_user': fields.List(fields.String,
                                               attribute=lambda x: x.data.linked_user if x.data else None),
                    'project': fields.Nested(a_project, attribute=lambda x: Wrapper(x.user,
                                                                                    x.data.channel.project,
                                                                                    x.lang)
                    if x.data and x.data.channel.project else None, skip_none=True, allow_null=True),
                    'other_user': fields.Nested(a_user, attribute=lambda x: Wrapper(x.user,
                                                                                    x.data.author,
                                                                                    x.lang)
                    if x.data and x.data.author else None, skip_none=True, allow_null=True),

                    'reactions': fields.List(fields.Nested(a_reactions),
                                             attribute=lambda x: x.data.get_reactions(x.user) if x.data else None),

                    "tags": fields.List(fields.String, attribute=lambda
                        x: x.data.tags if x.data and x.data.type == 'main_chat_post' else None),
                    "comment_channel_id": fields.String(attribute=lambda
                        x: x.data.comment_channel_id if x.data and x.data.type == 'main_chat_post' else None),
                    "comments": fields.Integer(attribute=lambda
                        x: x.data.comments if x.data and x.data.type == 'main_chat_post' else None),

                    'author_display_name': fields.String(
                        attribute=lambda x: None if not x.data else (
                            x.data.author.display_name if not x.data.anonymous else "anonymous")),
                    'author_profile_pic_id': fields.String(
                        attribute=lambda x:
                        x.data.author.profile_pic_id if x.data and not x.data.anonymous else None),
                    'author_username': fields.String(
                        attribute=lambda x: x.data.author.username if x.data and not x.data.anonymous else None),
                    'rank': fields.String(attribute=lambda x:
                    rank(x.data.author) if x.data and not x.data.anonymous else None),

                    'author_id': fields.String(
                        attribute=lambda x:
                        x.data.author_id if x.data and (
                                not x.data.anonymous
                                or x.user.moderation_flags.is_moderator
                                or x.user.moderation_flags.is_developer) else None),

                    "blocked": fields.List(fields.String,
                                           attribute=lambda x: x.data.get_is_blocked(x.user) if x.data else None)})

a_own_project = api.inherit("Own_Project", a_project, {
    "last_post": fields.Nested(a_post, attribute=lambda x: Wrapper(x.user, x.data.channel.last_post, x.lang),
                               skip_none=True, allow_null=True),
    "last_post_read_id": fields.String(attribute=lambda x: x.data.channel.get_last_read_message(x.user).id if
    x.data.channel.get_last_read_message(x.user) else None)})

a_chat = api.model("Chat",
                   {"id": fields.String(attribute=lambda x: x.data.id),
                    "channel_id": fields.String(attribute=lambda x: x.data.channel_id),
                    "last_post": fields.Nested(a_post,
                                               attribute=lambda x: Wrapper(
                                                   x.user, x.data.channel.last_post if
                                                   x.data and x.data.channel else None, x.lang),
                                               skip_none=True, allow_null=True),
                    "last_post_read_id": fields.String(
                        attribute=lambda x: x.data.channel.get_last_read_message(x.user).id if
                        x.data.channel.get_last_read_message(x.user) else None)
                    })

a_chat_with_other_user = api.inherit("ChatWithOtherUser", a_chat,
                                     {'other_user': fields.Nested(
                                         a_user, attribute=lambda x: Wrapper(x.user, x.data.other_user, x.lang))})

a_channel = api.model("Channel",
                      {'id': fields.String(attribute=lambda x: x.data.id),
                       'last_post_sent': fields.String(
                           attribute=lambda x: x.data.last_post.date_created.strftime("%Y/%m/%d/%H/%M/%S/%f")[
                                               :-3] if x.data and x.data.last_post else None),
                       "last_post_read_id": fields.String(
                           attribute=lambda x: x.data.get_last_read_message(x.user).id if
                           x.data.get_last_read_message(x.user) else None)
                       })

a_bubble = api.model("Bubble",
                     {'id': fields.String(attribute=lambda x: x.data.id),
                      'icon': fields.String(attribute=lambda x: x.data.icon),
                      'text': fields.String(attribute=lambda x: x.data.text)
                      })

a_panel = api.model("Panel",
                    {'type': fields.String(attribute=lambda x: x.data.type),
                     'image_id': fields.String(
                         attribute=lambda x: x.data.image_id if x.data.type == "image_panel" else None),
                     'bubbles': fields.List(fields.Nested(a_bubble), attribute=
                     lambda x: [Wrapper(x.user, nugget, x.lang) for nugget in
                                x.data.get_bubbles(x.lang)] if
                     (x.data.type == "info_nuggets_panel" or x.data.type == "interests_panel") else None),

                     'question_id': fields.String(
                         attribute=lambda x: x.data.question.id if x.data.type == "question_panel" else None),
                     'question': fields.String(
                         attribute=lambda x: x.data.question.get_question(
                             x.lang) if x.data.type == "question_panel" else None),
                     'answer': fields.String(
                         attribute=lambda x: x.data.answer if x.data.type == "question_panel" else None),
                     'text': fields.String(attribute=lambda
                         x: x.data.text if x.data.type == "text_panel" or x.data.type == "description_panel" else None)
                     })

a_card = api.model("Card",
                   {'type': fields.String(attribute=lambda x: x.data.type),
                    'image_id': fields.String(attribute=lambda x: x.data.image_id),
                    'user_id': fields.String(attribute=lambda x: x.data.user.id),
                    'image_caption': fields.String(attribute=lambda x: x.data.image_caption(x.lang)),
                    'image_tag': fields.String(attribute=lambda x: x.data.image_tag(x.lang)),
                    'image_text': fields.String(attribute=lambda x: x.data.image_text(x.lang)),
                    'panels': fields.List(fields.Nested(a_panel, skip_none=True),
                                          attribute=lambda x: [Wrapper(x.user, panel, x.lang) for panel in
                                                               x.data.panels] if x.data.panels else None)})

a_main_channel_id = api.model("MainChannelID",
                              {"main_channel_id": fields.String(attribute=lambda x: x.data["main_channel_id"])})
a_theme = api.model("Theme",
                    {"theme": fields.String(attribute=lambda x: x.data["theme"])})

a_regex = api.model("Regex",
                    {"username_regex": fields.String(attribute=lambda x: x.data["username_regex"]),
                     "display_name_regex": fields.String(attribute=lambda x: x.data["display_name_regex"])})

a_options = api.model("Options",
                      {"options": fields.List(fields.String, attribute=lambda x: [y[x.lang]
                                                                                  for y in x.data["options"]])})

a_themes = api.model("Themes",
                     {"options": fields.List(fields.String, attribute=lambda x: x.data["options"])})

a_tag = api.model("Tag", {
    "name": fields.String(attribute=lambda x: x.data["name"][x.lang]),
    "color": fields.String(attribute=lambda x: x.data["color"]),
    "id": fields.String(attribute=lambda x: x.data["id"])
})

a_all_options = api.model("AllOptions", {
    "main_channel_id": fields.String(attribute=lambda x: x.data["main_channel_id"]),
    "course_of_study_options": fields.List(fields.String,
                                           attribute=lambda x: [y[x.lang] for y in x.data["course_of_study_options"]]),
    "project_type_options": fields.List(fields.String,
                                        attribute=lambda x: [y[x.lang] for y in x.data["project_type_options"]]),
    "interest_options": fields.List(fields.String,
                                    attribute=lambda x: [y[x.lang] for y in x.data["interest_options"]]),
    "username_regex": fields.String(attribute=lambda x: x.data["username_regex"]),
    "display_name_regex": fields.String(attribute=lambda x: x.data["display_name_regex"]),
    "themes": fields.List(fields.String, attribute=lambda x: x.data["themes"]),
    "selected_theme": fields.String(attribute=lambda x: x.data["selected_theme"]),
    "tags": fields.List(fields.Nested(a_tag), attribute=lambda x: [Wrapper(x.user, tag, x.lang)
                                                                   for tag in x.data["tags"]])})

a_id_list = api.model("ID_List",
                      {"IDs": fields.List(fields.String, attribute=lambda x: x.data["IDs"])})

a_info_nugget = api.model("InfoNugget", {
    "id": fields.String(attribute=lambda x: x.data.id),
    "name": fields.String(attribute=lambda x: x.data.name),
    "answers": fields.List(fields.String, attribute=lambda x: x.data.get_all_answers(x.lang))})

a_question = api.model("Question", {
    "id": fields.String(attribute=lambda x: x.data.id),
    "question": fields.String(attribute=lambda x: x.data.get_question(x.lang)),
    "placeholder": fields.String(attribute=lambda x: x.data.get_placeholder(x.lang))})

a_card_vars = api.model("CardVars", {
    "info_nuggets": fields.List(fields.Nested(a_info_nugget), attribute=lambda x:
    [Wrapper(x.user, nugget, x.lang) for nugget in x.data["info_nuggets"]]),
    "questions": fields.List(fields.Nested(a_question), attribute=lambda x:
    [Wrapper(x.user, question, x.lang) for question in x.data["questions"]])})
