# Copyright (c) 2015 iXsystems, Inc.
# All rights reserved.
# This file is a part of TrueNAS
# and may not be copied and/or distributed
# without the express permission of iXsystems.

import logging
import os
import sys

from django.core.urlresolvers import reverse
from django.shortcuts import render
from django.utils.html import escapejs
from django.utils.translation import ugettext as _

from freenasUI.common.system import get_sw_login_version, get_sw_name
from freenasUI.freeadmin.hook import AppHook
from freenasUI.failover.mixins import FailoverAlert
from freenasUI.failover.notifier import FailoverNotifier
from freenasUI.middleware.client import client

log = logging.getLogger('failover.hook')


class FailoverHook(AppHook):

    name = 'failover'

    def _common_failover_view(self, request):
        from freenasUI.failover.forms import TakeoverForm, UnlockForm
        from freenasUI.failover.models import Failover
        from freenasUI.middleware.notifier import notifier
        from freenasUI.storage.models import Volume
        status = notifier().failover_status()
        if status not in ('SINGLE', 'MASTER'):
            with client as c:
                ips = c.call('failover.get_ips')

            try:
                failover = Failover.objects.order_by('-id')[0]
            except Exception:
                failover = Failover.objects.create()

            ask_passphrase = False
            if status == 'ERROR' and os.path.exists('/tmp/.failover_needop'):
                encrypted = [v for v in Volume.objects.filter(vol_encrypt__gt=0) if not v.is_decrypted()]
                if encrypted:
                    ask_passphrase = True

            return render(request, "failover/failover.html", {
                'ips': ips,
                'status': status,
                'sw_login_version': get_sw_login_version(),
                'sw_name': get_sw_name(),
                'form': TakeoverForm(),
                'unlock_form': UnlockForm(),
                'failover': failover,
                'node': notifier().failover_node(),
                'ask_passphrase': ask_passphrase,
            })

    def _common_ha_abort(self):
        from freenasUI.middleware.notifier import notifier
        """
        This method detects whether this unit is part of a HA pair
        and if so whether it is active or passive.  If this is
        HA and passive returns True.
        If this is currently the master of an HA pair or not
        in an HA pair it returns False.
        """

        status = notifier().failover_status()
        if status not in ("SINGLE", "MASTER"):
            return True
        return False

    def base_js(self, request):
        return [
            'lib/js/failover.js',
        ]

    def base_css(self, request):
        return [
            'css/failover.css',
        ]

    def hook_app_index_freeadmin(self, request):
        return self._common_failover_view(request)

    def hook_app_index_account_login(self, request):
        return self._common_failover_view(request)

    def hook_app_tabs_system(self, request):
        from freenasUI.failover.models import Failover
        from freenasUI.middleware.notifier import notifier
        try:
            oid = Failover.objects.order_by('-id')[0].id
        except IndexError:
            oid = Failover.objects.create().id
        if not notifier().failover_licensed():
            return []
        return [
            {
                'name': 'Failover',
                'focus': 'system.Failover',
                'verbose_name': _('Failover'),
                'url': reverse('freeadmin_failover_failover_edit', kwargs={
                    'oid': oid,
                }) + '?deletable=false&inline=true',
                'order': 100,
            }
        ]

    def hook_class_new_VolumeStatusAlert(self, bases, attrs):
        bases.insert(0, FailoverAlert)

    def hook_class_new_notifier(self, bases, attrs):
        bases.insert(0, FailoverNotifier)

    def hook_form_buttons_AdvancedForm(self, form, action, *args, **kwargs):
        from freenasUI.middleware.notifier import notifier
        btns = []
        status = notifier().failover_status()
        if status not in ('SINGLE', 'MASTER'):
            btns.append({
                'name': 'ForceMaster',
                'verbose_name': _('Failover Force Master'),
                'onclick': 'failoverForceMaster(this, "%s")' % (
                    escapejs(reverse('failover_force_master'))
                ),
            })
        return btns

    def _virtual_ip_configured(self):
        from freenasUI.network.models import Interfaces
        from django.db.models import Q
        return Interfaces.objects.exclude(
            Q(int_vip=None) | Q(int_vip='')
        ).count() > 0

    def _volume_configured(self):
        from freenasUI.storage.models import Volume
        return Volume.objects.all().count() > 0

    def hook_form_init_AutoImportWizard(self, form, *args, **kwargs):
        from freenasUI.middleware.notifier import notifier
        if not notifier().failover_licensed():
            return
        form._volume_configured = self._volume_configured()

    def hook_form_init_InterfacesForm(self, form, *args, **kwargs):
        from freenasUI.middleware.notifier import notifier
        if not notifier().failover_licensed():
            return
        form._vip_configured = self._virtual_ip_configured()

    def hook_form_done_InterfacesForm(self, form, request, events, **kwargs):
        """
        Sync to peer the first time a Virtual IP is configured
        This will sync the database and /data files plus restart services
        required for HA to work
        """
        from freenasUI.middleware.notifier import notifier
        if not notifier().failover_licensed():
            notifier().start("network")
            return
        notifier().start("ix-devd")
        notifier().restart("devd")
        # Configure HA for the first time once a Virtual IP is set
        vip_configured = self._virtual_ip_configured()
        if not form._vip_configured and vip_configured:
            _n = notifier()
            _n.failover_sync_peer('to')
            with client as c:
                c.call('failover.call_remote', 'service.start', ['ix-devd'])
                c.call('failover.call_remote', 'service.restart', ['devd'])
                c.call('failover.call_remote', 'service.start', ['network'])
            if self._volume_configured():
                events.append('window.location="/";')
        elif form._vip_configured and not vip_configured:
            if self._volume_configured():
                events.append('window.location="/";')
        if vip_configured and form.instance.int_vip:
            notifier().stop("devd", wait=True)
            notifier().start("network", wait=True)
            notifier().start("ix-devd", wait=True)
            notifier().start("devd", wait=True)
        else:
            notifier().start("network", wait=True)

    def hook_form_done_LicenseUpdateForm(self, form, request, events, **kwargs):
        from freenasUI.support.utils import get_license
        license, error = get_license()
        if license and license.system_serial_ha:
            events.append('dojo.byId("failoveren_menuitem").style["display"] = "";')
            events.append('checkDisabled()')

    def hook_form_done_VolumeExport(self, form, request, events, **kwargs):
        from freenasUI.middleware.notifier import notifier
        if not notifier().failover_licensed():
            return
        notifier().start("ix-devd")
        if not self._volume_configured():
            _n = notifier()
            _n.failover_sync_peer('to')
            with client as c:
                c.call('failover.call_remote', 'service.start', ['ix-devd'])
            events.append('window.location="/";')

    def hook_form_init_VolumeManagerForm(self, form, *args, **kwargs):
        from freenasUI.middleware.notifier import notifier
        if not notifier().failover_licensed():
            return
        form._volume_configured = self._volume_configured()

    def hook_form_done_VolumeManagerForm(self, form, request, events, **kwargs):
        from freenasUI.middleware.notifier import notifier
        if not notifier().failover_licensed():
            return
        notifier().start("ix-devd")
        if (
            not form._volume_configured and self._volume_configured()
        ) and self._virtual_ip_configured():
            _n = notifier()
            _n.failover_sync_peer('to')
            events.append('window.location="/";')

    def hook_form_init_ZFSVolumeWizardForm(self, form, *args, **kwargs):
        from freenasUI.middleware.notifier import notifier
        if not notifier().failover_licensed():
            return
        form._volume_configured = self._volume_configured()

    def hook_form_done_ZFSVolumeWizardForm(self, form, request, events, **kwargs):
        from freenasUI.middleware.notifier import notifier
        if not notifier().failover_licensed():
            return
        notifier().start("ix-devd")
        if (
            not form._volume_configured and self._volume_configured()
        ) and self._virtual_ip_configured():
            _n = notifier()
            _n.failover_sync_peer('to')
            with client as c:
                c.call('failover.call_remote', 'service.start', ['ix-devd'])
            events.append('window.location="/";')

    def hook_tool_run_autorepl(self):
        if self._common_ha_abort():
            sys.exit(0)

    def hook_tool_run_autosnap(self):
        if self._common_ha_abort():
            sys.exit(0)

    def top_menu(self, request):
        from freenasUI.middleware.notifier import notifier

        rv = []

        node = notifier().failover_node()
        if not node:
            return rv

        licensed = notifier().failover_licensed()

        status = notifier().failover_status()
        if status == 'SINGLE':
            name = 'HA Disabled'
        else:
            name = 'HA Enabled'

        rv.append({
            'id': 'failoveren_menuitem',
            'name': (
                '<span id="failover_disabled_text">%s</span>' % (
                    name,
                )
            ),
            'img': (
                '<div data-dojo-type="dijit.layout.ContentPane" '
                'data-node="%s" id="failover_disabled"></div>' % (
                    node,

                )
            ),
            'onclick': 'haStatus();',
            'align': 'right',
            'weight': 0,
            'style': None if licensed else 'display: none;',
        })

        return rv
