# SPDX-License-Identifier: AGPL-3.0-or-later
"""
FreedomBox app for system diagnostics.
"""

import collections
import importlib
import logging
import threading

from django.utils.translation import ugettext_lazy as _

from plinth import app as app_module
from plinth import daemon, menu
from plinth.modules.apache.components import diagnose_url_on_all

from .manifest import backup  # noqa, pylint: disable=unused-import

version = 1

is_essential = True

_description = [
    _('The system diagnostic test will run a number of checks on your '
      'system to confirm that applications and services are working as '
      'expected.')
]

app = None

logger = logging.Logger(__name__)

running_task = None

current_results = {}


class DiagnosticsApp(app_module.App):
    """FreedomBox app for diagnostics."""

    app_id = 'diagnostics'

    def __init__(self):
        """Create components for the app."""
        super().__init__()
        info = app_module.Info(app_id=self.app_id, version=version,
                               is_essential=is_essential,
                               name=_('Diagnostics'), icon='fa-heartbeat',
                               description=_description,
                               manual_page='Diagnostics')
        self.add(info)

        menu_item = menu.Menu('menu-diagnostics', info.name, None, info.icon,
                              'diagnostics:index', parent_url_name='system')
        self.add(menu_item)

    def diagnose(self):
        """Run diagnostics and return the results."""
        results = super().diagnose()
        results.append(daemon.diagnose_port_listening(8000, 'tcp4'))
        results.extend(
            diagnose_url_on_all('http://{host}/plinth/',
                                check_certificate=False))

        return results


def init():
    """Initialize the module"""
    global app
    app = DiagnosticsApp()
    app.set_enabled(True)


def start_task():
    """Start the run task in a separate thread."""
    global running_task
    if running_task:
        raise Exception('Task already running')

    running_task = threading.Thread(target=_run_on_all_enabled_modules_wrapper)
    running_task.start()


def _run_on_all_enabled_modules_wrapper():
    """Wrapper over actual task to catch exceptions."""
    try:
        run_on_all_enabled_modules()
    except Exception as exception:
        logger.exception('Error running diagnostics - %s', exception)
        current_results['error'] = str(exception)

    global running_task
    running_task = None


def run_on_all_enabled_modules():
    """Run diagnostics on all the enabled modules and store the result."""
    global current_results
    current_results = {
        'apps': [],
        'results': collections.OrderedDict(),
        'progress_percentage': 0
    }

    apps = []
    for app in app_module.App.list():
        # XXX: Implement more cleanly.
        # Don't run diagnostics on apps have not been setup yet.
        # However, run on apps that need an upgrade.
        module = importlib.import_module(app.__class__.__module__)
        if module.setup_helper.get_state() == 'needs-setup':
            continue

        if not app.is_enabled():
            continue

        if not app.has_diagnostics():
            continue

        apps.append((app.app_id, app))
        current_results['results'][app.app_id] = None

    current_results['apps'] = apps
    for current_index, (app_id, app) in enumerate(apps):
        current_results['results'][app_id] = app.diagnose()
        current_results['progress_percentage'] = \
            int((current_index + 1) * 100 / len(apps))
