
from typing import Any, Dict, Optional, Text, Union, cast

from django.http import HttpRequest, HttpResponse
from django.utils import timezone
from django.utils.translation import ugettext as _

from zerver.lib.exceptions import JsonableError
from zerver.lib.push_notifications import send_android_push_notification, \
    send_apple_push_notification
from zerver.lib.request import REQ, has_request_variables
from zerver.lib.response import json_error, json_success
from zerver.lib.validator import check_int
from zerver.models import UserProfile
from zerver.views.push_notifications import validate_token
from zilencer.models import RemotePushDeviceToken, RemoteZulipServer

def validate_entity(entity: Union[UserProfile, RemoteZulipServer]) -> None:
    if not isinstance(entity, RemoteZulipServer):
        raise JsonableError(_("Must validate with valid Zulip server API key"))

def validate_bouncer_token_request(entity: Union[UserProfile, RemoteZulipServer],
                                   token: bytes, kind: int) -> None:
    if kind not in [RemotePushDeviceToken.APNS, RemotePushDeviceToken.GCM]:
        raise JsonableError(_("Invalid token type"))
    validate_entity(entity)
    validate_token(token, kind)

@has_request_variables
def remote_server_register_push(request, entity, user_id=REQ(),
                                token=REQ(), token_kind=REQ(validator=check_int), ios_app_id=None):
    # type: (HttpRequest, Union[UserProfile, RemoteZulipServer], int, bytes, int, Optional[Text]) -> HttpResponse
    validate_bouncer_token_request(entity, token, token_kind)
    server = cast(RemoteZulipServer, entity)

    # If a user logged out on a device and failed to unregister,
    # we should delete any other user associations for this token
    # & RemoteServer pair
    RemotePushDeviceToken.objects.filter(
        token=token, kind=token_kind, server=server).exclude(user_id=user_id).delete()

    # Save or update
    remote_token, created = RemotePushDeviceToken.objects.update_or_create(
        user_id=user_id,
        server=server,
        kind=token_kind,
        token=token,
        defaults=dict(
            ios_app_id=ios_app_id,
            last_updated=timezone.now()))

    return json_success()

@has_request_variables
def remote_server_unregister_push(request, entity, token=REQ(),
                                  token_kind=REQ(validator=check_int), ios_app_id=None):
    # type: (HttpRequest, Union[UserProfile, RemoteZulipServer], bytes, int, Optional[Text]) -> HttpResponse
    validate_bouncer_token_request(entity, token, token_kind)
    server = cast(RemoteZulipServer, entity)
    deleted = RemotePushDeviceToken.objects.filter(token=token,
                                                   kind=token_kind,
                                                   server=server).delete()
    if deleted[0] == 0:
        return json_error(_("Token does not exist"))

    return json_success()

@has_request_variables
def remote_server_notify_push(request,  # type: HttpRequest
                              entity,  # type: Union[UserProfile, RemoteZulipServer]
                              payload=REQ(argument_type='body')  # type: Dict[str, Any]
                              ):
    # type: (...) -> HttpResponse
    validate_entity(entity)
    server = cast(RemoteZulipServer, entity)

    user_id = payload['user_id']
    gcm_payload = payload['gcm_payload']
    apns_payload = payload['apns_payload']

    android_devices = list(RemotePushDeviceToken.objects.filter(
        user_id=user_id,
        kind=RemotePushDeviceToken.GCM,
        server=server
    ))

    apple_devices = list(RemotePushDeviceToken.objects.filter(
        user_id=user_id,
        kind=RemotePushDeviceToken.APNS,
        server=server
    ))

    if android_devices:
        send_android_push_notification(android_devices, gcm_payload, remote=True)

    if apple_devices:
        send_apple_push_notification(user_id, apple_devices, apns_payload)

    return json_success()

@@ -1285,77 +1285,173 @@
 uest
-, entity, user_id=REQ(),%0A                                token=REQ(),
+: HttpRequest, entity: Union%5BUserProfile, RemoteZulipServer%5D,%0A                                user_id: int=REQ(), token: bytes=REQ(),%0A                               
  tok
@@ -1449,32 +1449,37 @@
       token_kind
+: int
 =REQ(validator=c
@@ -1492,108 +1492,52 @@
 nt),
- ios_app_id=None):%0A    # type: (HttpRequest, Union%5BUserProfile, RemoteZulipServer%5D, int, bytes, int,
+%0A                                ios_app_id:
  Opt
@@ -1539,32 +1539,37 @@
 : Optional%5BText%5D
+=None
 ) -%3E HttpRespons
@@ -1561,32 +1561,33 @@
  -%3E HttpResponse
+:
 %0A    validate_bo
@@ -2353,23 +2353,116 @@
 uest
-, entity, token
+: HttpRequest, entity: Union%5BUserProfile, RemoteZulipServer%5D,%0A                                  token: bytes
 =REQ
@@ -2509,16 +2509,21 @@
 ken_kind
+: int
 =REQ(val
@@ -2544,103 +2544,54 @@
 nt),
- ios_app_id=None):%0A    # type: (HttpRequest, Union%5BUserProfile, RemoteZulipServer%5D, bytes, int,
+%0A                                  ios_app_id:
  Opt
@@ -2601,16 +2601,21 @@
 al%5BText%5D
+=None
 ) -%3E Htt
@@ -2615,32 +2615,33 @@
  -%3E HttpResponse
+:
 %0A    validate_bo
@@ -3104,25 +3104,16 @@
 (request
-,  # type
 : HttpRe
@@ -3121,54 +3121,16 @@
 uest
-%0A                              entity,  # type
+, entity
 : Un
@@ -3164,16 +3164,17 @@
 pServer%5D
+,
 %0A       
@@ -3203,16 +3203,32 @@
  payload
+: Dict%5Bstr, Any%5D
 =REQ(arg
@@ -3249,82 +3249,8 @@
 dy')
-  # type: Dict%5Bstr, Any%5D%0A                              ):%0A    # type: (...
 ) -%3E
@@ -3262,16 +3262,17 @@
 Response
+:
 %0A    val

Use Python 3 syntax for typing in `zilencer/views.py`.