import json
from urllib.error import HTTPError
from google.oauth2 import service_account
from google.auth.transport.urllib3 import AuthorizedHttp

from Models import UserModel

SCOPES = ['https://www.googleapis.com/auth/firebase.messaging']
credentials = service_account.Credentials.from_service_account_file('Notify/key.json').with_scopes(SCOPES)
encoding = "utf-8"
project_id = "matchinit-notification-service"
firebase_url = f"https://fcm.googleapis.com/v1/projects/{project_id}/messages:send"
"""
message_template = {  # https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages
    "name": None,  # string
    "data": {
        # "key": "value"
    },
    "notification": {
        "title": None,  # string
        "body": None,  # string
        "image": None,  # string
    }
    "android": None,  # android_template
    "webpush": None,  # webpush_template
    "apns": None,  # apns_template
    "fcm_options": {
        "analytics_label": None,  # string
    },
    # Union field target can be only one of the following:
    "token": None,  # string
    "topic": None,  # string
    "condition": None,  # string
    # End of list of possible types for union field target.
}
android_template = {
    "collapse_key": None,  # string
    "priority": None,  # enum(AndroidMessagePriority) = "normal" or "high"
    "ttl": None,  # A duration in seconds with up to nine fractional digits, terminated by 's'. Example: "3.5s"
    "restricted_package_name": None,  # string
    "data": {
        # "key": "value"    will overwrite message_template["data"]
    },
    "notification": {
        "title": None,  # string
        "body": None,  # string
        "icon": None,  # string
        "color": None,  # string
        "sound": None,  # string
        "tag": None,  # string
        "click_action": None,  # string
        "body_loc_key": None,  # string
        "body_loc_args": [
            # string
        ],
        "title_loc_key": None,  # string
        "title_loc_args": [
            # string
        ],
        "channel_id": None,  # string
        "ticker": None,  # string
        "sticky": None,  # boolean
        "event_time": None,  # string
        "local_only": None,  # boolean
        "notification_priority": None,  # enum(NotificationPriority):
        # PRIORITY_UNSPECIFIED, PRIORITY_MIN, PRIORITY_LOW, PRIORITY_DEFAULT,
        # PRIORITY_HIGH, PRIORITY_MAX
        "default_sound": None,  # boolean
        "default_vibrate_timings": None,  # boolean
        "default_light_settings": None,  # boolean
        "vibrate_timings": [
            # string
        ],
        "visibility": None,  # enum(Visibility): VISIBILITY_UNSPECIFIED, PRIVATE, PUBLIC, SECRET
        "notification_count": None,  # integer
        "light_settings": {
            "color": {
                "red": None,  # float
                "green": None,  # float
                "blue": None,  # float
                "alpha": None,  # float
            },
            "light_on_duration": None,  # string
            "light_off_duration": None,  # string
        },
        "image": None,  # string
    },
    "fcm_options": {
        "analytics_label": None,  # string
    },
    "direct_boot_ok": None,  # boolean
}
webpush_template = {
    "headers": {
        # "key": "value",
    },
    "data": {
        # "key": "value"    will overwrite message_template["data"]
    },
    "notification": {
        # object https://developer.mozilla.org/en-US/docs/Web/API/Notification
    },
    "fcm_options": {
        "link": None,  # string
        "analytics_label": None,  # string
    }
}
apns_template = {
    "headers": {
        # "key": "value"
        # HTTP request headers defined in Apple Push Notification Service
        # https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns
    },
    "payload": {
        # object https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification
    },
    "fcm_options": {
        "analytics_label": None,  # string
        "image": None,  # string
    }
}
"""


def send_notification(message, device):
    payload = {
        "validate_only": False,
        "message": message
    }
    payload_json = bytes(json.dumps(payload), encoding)
    try:
        authed_http = AuthorizedHttp(credentials)
        response = authed_http.request("POST", firebase_url, body=payload_json)
        if response.status == 404 and '"errorCode": "UNREGISTERED"' in str(response.data, "utf-8"):
            device.delete()
        elif response.status != 200:
            print(f"[ERROR] failed to send notification: {response.data}", flush=True)
    except HTTPError as error:
        if error.code == 404 and hasattr(error, 'details') and error.details["errorCode"] == "UNREGISTERED":
            device.delete()
        else:
            print(f"[ERROR] failed to send notification: {error}", flush=True)


def notify(notification=None, data=None, user=None, post=None):
    message = {
        "notification": notification,
        "data": data,
        "android": {
            "priority": "high"
        },
        "apns": {
            "headers": {
                "apns-priority": "10"
            },
            "payload": {
                "aps": {
                    "mutable-content": 1,
                }
            }
        }
    }
    if not notification and post:
        message["apns"]["payload"]["aps"]["alert"] = create_notification(post)
    if user:
        for device in user.devices:
            if not device.is_stale():
                message["token"] = device.device_id
                send_notification(message, device)


def notify_member(post=None):
    if post:
        group = set([])
        data = {
            'type': 'message',
            'id': str(post.id),
            'channel_id': str(post.channel.id)
        }
        if post.channel.chat:
            group = set(post.channel.chat.users)
        if post.channel.project:
            group = set(post.channel.project.attendees)
        group = group.union(set([UserModel.query.get(i) for i in post.linked_user]))

        users = [user for user in group if user != post.author]

        for user in users:
            notify(
                data=data,
                user=user,
                post=post
            )


def create_notification(message):
    title = ""
    body = ""
    if message.channel.chat:
        if message.anonymous:
            title = "anon"
        else:
            title = message.author.display_name
        body = shorten(message.content, 80)
    elif message.channel.project:
        title = message.channel.project.name
        if message.anonymous:
            body = "anon: "
        else:
            body = message.author.display_name + ": "
        body += shorten(message.content, 80 - len(message.author.display_name))
    else:
        if message.anonymous:
            title = "anon"
        else:
            title = message.author.display_name
        title += " mentioned you:"
        body = shorten(message.content, 80)

    return {
        "title": title,
        "body": body
    }


def notify_match(chat=None):
    if chat:
        for user in chat.users:
            data = {
                'type': 'match',
                'id': str([u.id for u in chat.users if u != user][0]),
                'channel_id': str(chat.channel.id)
            }
            notify(
                data=data,
                user=user
            )


def notify_study_group(project=None):
    if project:
        data = {
            'type': 'group_match',
            'id': str(project.id),
            'channel_id': str(project.channel.id)
        }
        for user in project.attendees:
            notify(
                data=data,
                user=user
            )


def notify_all(title, content):
    data = {
        'type': 'global_message'
    }
    notification = {
        'title': title,
        'body': content
    }
    for user in UserModel.query.all():
        notify(
            data=data,
            notification=notification,
            user=user
        )


def shorten(string, length):
    if len(string) > length:
        return string[:length - 4] + "..."
    else:
        return string
