#!/usr/bin/env python3
# Copyright 2018-2019 The Wazo Authors  (see the AUTHORS file)
# SPDX-License-Identifier: GPL-3.0-or-later

import argparse
import os
import platform
import requests
import sys
import urllib3
from distutils.version import LooseVersion

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


class WazoDistUpgradeException(Exception):
    pass


class WizardNotConfigured(WazoDistUpgradeException):

    _message = 'The Wazo wizard is not configured.'

    def __init__(self):
        super().__init__(self._message)


class ConfdError(WazoDistUpgradeException):

    _message = 'xivo-confd is not reachable. You should check /var/log/xivo-confd.log for errors.'

    def __init__(self):
        super().__init__(self._message)


class WrongWazoVersion(WazoDistUpgradeException):

    _message = 'wazo-dist-upgrade should not be run on Wazo {version}. You should run wazo-upgrade instead.'

    def __init__(self, wazo_version):
        super().__init__(self._message.format(version=wazo_version))


class KernelNotFound(WazoDistUpgradeException):

    _message = 'No kernel found in /lib/modules. Please install linux-image-amd64 or linux-image-686'

    def __init__(self):
        super().__init__(self._message)


class WrongKernelVersion(WazoDistUpgradeException):

    _message = """The running Linux kernel is not recent enough. You need to reboot before running wazo-dist-upgrade again.
  Kernel minimum: {minimum_version}
  Kernel running: {running_version}"""

    def __init__(self, minimum_version, running_version):
        super().__init__(self._message.format(minimum_version=minimum_version, running_version=running_version))


class UpgradeAborted(WazoDistUpgradeException):

    _message = 'Aborting.'

    def __init__(self):
        super().__init__(self._message)


def main():
    args = parse_args()
    try:
        check_wazo_version()
        check_wizard_is_run()
        check_running_kernel_is_up_to_date()
        if not args.force:
            confirm_upgrade()
    except WazoDistUpgradeException as e:
        print(e, file=sys.stderr)
        sys.exit(1)


def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument('-f',
                        '--force',
                        action='store_true',
                        help="Disable interactive prompts. Default: %(default)s")
    return parser.parse_args()


def check_running_kernel_is_up_to_date():
    running_kernel = LooseVersion(platform.release())
    minimum_kernel = LooseVersion('4.9.0')

    if running_kernel < minimum_kernel:
        raise WrongKernelVersion(minimum_kernel, running_kernel)


def check_wazo_version():
    # 19.12 = latest prod version, 19.13 = latest dev version
    if installed_wazo_version() not in ('19.12', '19.13'):
        raise WrongWazoVersion(installed_wazo_version())


def installed_wazo_version():
    with open('/usr/share/wazo/WAZO-VERSION', 'r') as version_file:
        return str(version_file.read()).strip()


def check_wizard_is_run():
    port = os.environ.get('WAZO_CONFD_PORT', '9486')
    try:
        r = requests.get('https://localhost:{port}/1.1/wizard'.format(port=port), verify=False)
    except requests.RequestException:
        raise ConfdError()

    try:
        if r.json()['configured'] is False:
            raise WizardNotConfigured()
    except (ValueError, KeyError):
        raise ConfdError()


def confirm_upgrade():
    while True:
        answer = input('Would you like to upgrade your system (all services will be restarted) [y/n]? ').lower()
        if answer not in ('y', 'n'):
            print('Invalid answer.')
            continue
        else:
            break

    if answer == 'n':
        raise UpgradeAborted()


if __name__ == '__main__':
    main()
