# -*- coding: utf-8 -*-

# Copyright (C) 2008-2014 Avencall
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>

import logging
import datetime

import firebase_admin
from firebase_admin import messaging, credentials
from firebase_admin.exceptions import FirebaseError

import asyncio
from pathlib import Path
from kalyke import ApnsConfig, ApnsPushType, VoIPClient
from kalyke.exceptions import ApnsProviderException

from os import path

from xivo_agid import agid
from xivo_agid import dialplan_variables
from xivo_agid.handlers.userfeatures import UserFeatures

logger = logging.getLogger(__name__)

INIT_FB=False
INIT_APNS=False

def configuration_notification_firebase_server():
    """ Configuration the connection with the Androïd server (Firebase)
    """
    webpush_config = messaging.WebpushConfig(
        headers={'Urgency': 'high'}
    )
    now = datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")

    return {"webpush_config": webpush_config, "now": now}

def configuration_notification_apple_server(caller):
    """Configuration the connection with the Apple server (APNs)
    """
    client = VoIPClient(
        use_sandbox=False,
        auth_key_filepath=Path("/etc/xivo/apns/xivo-mobile-apns.pem")
    )
    now = datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")

    payload = {
                "aps": { "content-available" : 1 },
                "title": "Incoming call",
                "time": now,
                "caller": caller
               }
    config = ApnsConfig(topic="fr.wisper.xivo.voip", push_type=ApnsPushType.VOIP, expiration=0)
    return {"client": client, "payload": payload, "config": config}


def android_push_notification_server(agi, token, now, caller, webpush_config, dry_run):
    """Send request to the Firebase server for send a notification on the android Mobile
    """
    android_config = messaging.AndroidConfig(
        ttl=datetime.timedelta(seconds=20),
        priority='high'
    )
    agi.verbose(f'Android - push notification - sending with token: {token}')
    message = messaging.Message(data={'title': 'Incoming call', 'time': now, 'caller': caller}, token=token,
                                android=android_config, webpush=webpush_config)
    agi.verbose(f'Android - push notification - message: {message}')
    try:
        response = messaging.send(message, dry_run=dry_run)
        agi.verbose(f'Android - push notification - answer: {response}')
    except FirebaseError as e:
        agi.verbose(f'Android - push notification - Error: {str(e)}')
        logger.error(f'Android - push notification - Error: {str(e)}')
    except ValueError as e:
        agi.verbose(f'Android - push notification - Invalid input arguments: {str(e)}')
        logger.error(f'Android - push notification - Invalid input arguments: {str(e)}')


def ios_push_notification_server(agi, client, token, payload, config):
    """Send request to the APNs server for send a notification on the Apple Mobile
    """
    try:
        agi.verbose(f'iOS - push notification - sending with token: {token}')
        agi.verbose(f'iOS - push notification - message: {payload}')
        response = asyncio.run(client.send_message(device_token=token, payload=payload, apns_config=config))
        agi.verbose(f'iOS - push notification - answer: {response}')
    except ApnsProviderException as e:
        agi.verbose(f'iOS - push notification - Error: {str(e)}')
        logger.error(f'iOS - push notification - Error: {str(e)}')
    except ValueError as e:
        agi.verbose(f'iOS - push notification - Invalid input arguments: {str(e)}')
        logger.error(f'iOS - push notification - Invalid input arguments: {str(e)}')

def get_init_FB():
    """Check if Firebase is free to send the request
    """
    INIT_FB = False
    try:
        if (path.exists("/etc/xivo/xivo-mobile-firebase-adminsdk.json")):
            cred = credentials.Certificate("/etc/xivo/xivo-mobile-firebase-adminsdk.json")
            firebase_admin.initialize_app(cred)
            INIT_FB = True
            logger.debug('Firebase credentials file found in /etc/xivo/xivo-mobile-firebase-adminsdk.json')
            logger.debug('Ready to send push notification to Firebase server')
        else:
            INIT_FB = False
            logger.warning('Firebase credentials file not found in /etc/xivo/xivo-mobile-firebase-adminsdk.json')
            logger.warning('Mobile App will not work for Android phones on this XiVO server')
    except FirebaseError as e:
        logger.error('Error during mobile_push_notification Firebase initialization: %s', e)
    except Exception as e:
        logger.error('Error during mobile_push_notification initialization: %s', e)
    return INIT_FB

def get_INIT_APNS():
    """Check if APNs is free to send the request
    """
    INIT_APNS = False
    try:
        if (path.exists("/etc/xivo/apns/xivo-mobile-apns.pem")):
            INIT_APNS = True
            logger.debug('APNS credentials file found in /etc/xivo/apns/xivo-mobile-apns.pem')
            logger.debug('Ready to send push notification to APN server')
        else:
            INIT_APNS = False
            logger.warning('APNS credentials file not found in /etc/xivo/apns/xivo-mobile-apns.pem')
            logger.warning('Mobile App will not work for iOS phones on this XiVO server')
    except Exception as e:
        logger.error(f'Error during mobile_push_notification initialization: {e}')
    return INIT_APNS


def get_server_and_token(token):
    """Split the token to now which type of mobile is call.
    Uses android server by default

    Args:
        token (str): ios-<token>, android-<token> or <token>

    Returns:
        json: {"server": "android/ios", "token": "<token>"}
    """
    mobile_token = token.split("-", 1)
    if mobile_token[0] == "ios" or mobile_token[0] == "android":
        return {"server": mobile_token[0], "token": mobile_token[1]}
    return {"server": "android", "token": token}


def mobile_push_notification(agi, cursor, args, dry_run=False):
    """Push mobile notification for android/ios respectively
    Defaults to android
    """
    token = agi.get_variable(dialplan_variables.MOBILE_PUSH_TOKEN)
    caller = agi.get_variable(dialplan_variables.SRCNUM)

    if token != UserFeatures.NOT_REGISTERED:
        server_token = get_server_and_token(token)
        if server_token["server"] == "android":
            if INIT_FB is False:
                agi.verbose("Firebase certificate file not found. Android Push notification won't be sent")
                agi.verbose("Exiting")
                return
            config = configuration_notification_firebase_server()
            return android_push_notification_server(agi, server_token["token"], config["now"], caller, config["webpush_config"], dry_run)
        elif server_token["server"] == "ios":
            if INIT_APNS is False:
                agi.verbose("APNs certificate file not found. Apple Push notification won't be sent")
                agi.verbose("Exiting")
                return
            config = configuration_notification_apple_server(caller)
            return ios_push_notification_server(agi, config["client"], server_token["token"], config["payload"], config["config"])
    else:
        agi.verbose("No mobile push token registered for this user.")
        agi.verbose("Not sending the push notification.")
        return

INIT_FB=get_init_FB()
INIT_APNS=get_INIT_APNS()
agid.register(mobile_push_notification)
