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

import argparse
import os
import platform
import subprocess

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 = 'wazo-confd is not reachable. You should check /var/log/wazo-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 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_has_run()
        check_running_kernel_is_up_to_date()
        check_gnome_not_installed()
        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.19.0')

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


def check_wazo_version():
    # 23.05 = latest prod version, 23.06 = latest dev version
    if installed_wazo_version() not in ('23.05', '23.06'):
        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_has_run():
    port = os.environ.get('WAZO_CONFD_PORT', '9486')
    try:
        r = requests.get(f'http://localhost:{port}/1.1/wizard')
    except requests.RequestException:
        raise ConfdError()

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


def check_gnome_not_installed():
    command = ['dpkg-query', '--show', '--showformat', "${Status}", 'gnome-core']
    output = subprocess.run(command, capture_output=True, text=True)
    if output.returncode == 0 and output.stdout == "install ok installed":
        raise WazoDistUpgradeException(
            "Upgrading a distribution with gnome installed is not supported."
        )


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

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


if __name__ == '__main__':
    main()
