# SPDX-License-Identifier: AGPL-3.0-or-later
"""
FreedomBox app for upgrades.
"""
import subprocess

from apt.cache import Cache
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from django.views.generic import TemplateView
from django.views.generic.edit import FormView

from plinth import __version__, actions, package
from plinth.errors import ActionError
from plinth.modules import first_boot, upgrades
from plinth.views import AppView

from .forms import BackportsFirstbootForm, ConfigureForm, UpdateFirstbootForm


class UpgradesConfigurationView(AppView):
    """Serve configuration page."""
    form_class = ConfigureForm
    success_url = reverse_lazy('upgrades:index')
    template_name = "upgrades_configure.html"
    app_id = 'upgrades'

    def get_initial(self):
        return {
            'auto_upgrades_enabled': upgrades.is_enabled(),
            'dist_upgrade_enabled': upgrades.is_dist_upgrade_enabled()
        }

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        context['can_activate_backports'] = upgrades.can_activate_backports()
        context['is_backports_requested'] = upgrades.is_backports_requested()
        context['is_busy'] = (_is_updating()
                              or package.is_package_manager_busy())
        context['log'] = get_log()
        context['refresh_page_sec'] = 3 if context['is_busy'] else None
        context['version'] = __version__
        context['new_version'] = is_newer_version_available()
        context['os_release'] = get_os_release()
        return context

    def form_valid(self, form):
        """Apply the form changes."""
        old_status = form.initial
        new_status = form.cleaned_data

        if old_status['auto_upgrades_enabled'] \
           != new_status['auto_upgrades_enabled']:

            try:
                if new_status['auto_upgrades_enabled']:
                    upgrades.enable()
                else:
                    upgrades.disable()
            except ActionError as exception:
                error = exception.args[2]
                messages.error(
                    self.request,
                    _('Error when configuring unattended-upgrades: {error}').
                    format(error=error))

            if new_status['auto_upgrades_enabled']:
                messages.success(self.request, _('Automatic upgrades enabled'))
            else:
                messages.success(self.request,
                                 _('Automatic upgrades disabled'))

        if old_status['dist_upgrade_enabled'] \
           != new_status['dist_upgrade_enabled']:
            upgrades.set_dist_upgrade_enabled(
                new_status['dist_upgrade_enabled'])
            if new_status['dist_upgrade_enabled']:
                messages.success(self.request,
                                 _('Distribution upgrade enabled'))
            else:
                messages.success(self.request,
                                 _('Distribution upgrade disabled'))

        return super().form_valid(form)


def is_newer_version_available():
    """Returns whether a newer Freedombox version is available."""
    cache = Cache()
    freedombox = cache['freedombox']
    return not freedombox.candidate.is_installed


def get_os_release():
    """Returns the Debian release number and name."""
    output = 'Error: Cannot read PRETTY_NAME in /etc/os-release.'
    with open('/etc/os-release', 'r', encoding='utf-8') as release_file:
        for line in release_file:
            if 'PRETTY_NAME=' in line:
                line = line.replace('"', '').strip()
                line = line.split('=')
                output = line[1]
    return output


def get_log():
    """Return the current log for unattended upgrades."""
    return actions.superuser_run('upgrades', ['get-log'])


def _is_updating():
    """Check if manually triggered update is running."""
    command = ['systemctl', 'is-active', 'freedombox-manual-upgrade']
    result = subprocess.run(command, capture_output=True, text=True,
                            check=False)
    return str(result.stdout).startswith('activ')  # 'active' or 'activating'


def upgrade(request):
    """Serve the upgrade page."""
    if request.method == 'POST':
        try:
            actions.superuser_run('upgrades', ['run'])
            messages.success(request, _('Upgrade process started.'))
        except ActionError:
            messages.error(request, _('Starting upgrade failed.'))

    return redirect(reverse_lazy('upgrades:index'))


def activate_backports(request):
    """Activate backports."""
    if request.method == 'POST':
        upgrades.set_backports_requested(True)
        upgrades.setup_repositories(None)
        messages.success(request, _('Frequent feature updates activated.'))

    return redirect(reverse_lazy('upgrades:index'))


class BackportsFirstbootView(FormView):
    """View to configure backports during first boot wizard."""
    template_name = 'backports-firstboot.html'
    form_class = BackportsFirstbootForm

    def dispatch(self, request, *args, **kwargs):
        """Show backports configuration form only if it can be activated."""
        if upgrades.is_backports_enabled():
            # Backports is already enabled. Record this preference and
            # skip first boot step.
            upgrades.set_backports_requested(True)
            first_boot.mark_step_done('backports_wizard')
            return HttpResponseRedirect(reverse_lazy(first_boot.next_step()))

        if not upgrades.can_activate_backports():
            # Skip first boot step.
            upgrades.set_backports_requested(False)
            first_boot.mark_step_done('backports_wizard')
            return HttpResponseRedirect(reverse_lazy(first_boot.next_step()))

        return super().dispatch(request, *args, *kwargs)

    def get_success_url(self):
        """Return next firstboot step."""
        return reverse_lazy(first_boot.next_step())

    def form_valid(self, form):
        """Mark the first wizard step as done, save value and redirect."""
        enabled = form.cleaned_data['backports_enabled']
        upgrades.set_backports_requested(enabled)
        upgrades.setup_repositories(None)
        first_boot.mark_step_done('backports_wizard')
        return super().form_valid(form)


class UpdateFirstbootView(FormView):
    """View to run initial update during first boot wizard."""
    template_name = 'update-firstboot.html'
    form_class = UpdateFirstbootForm

    def __init__(self):
        """Define instance attribute."""
        self.update = True

    def get_success_url(self):
        """Return next firstboot step."""
        if self.update:
            return reverse_lazy('upgrades:update-firstboot-progress')

        return reverse_lazy(first_boot.next_step())

    def form_valid(self, form):
        """Run update if selected, and mark step as done."""
        self.update = form.cleaned_data['update_now']
        if self.update:
            actions.superuser_run('upgrades', ['run'])

        first_boot.mark_step_done('initial_update')
        return super().form_valid(form)


class UpdateFirstbootProgressView(TemplateView):
    """View to show initial update progress."""
    template_name = 'update-firstboot-progress.html'

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        context['is_busy'] = (_is_updating()
                              or package.is_package_manager_busy())
        context['next_step'] = first_boot.next_step()
        context['refresh_page_sec'] = 3 if context['is_busy'] else None
        return context
