#!/usr/bin/env python3
# -*- coding: utf8 -*-
"""
diagnose du rvp
"""
import subprocess
import shlex
import re
import time
from subprocess import getstatusoutput
from os.path import isfile
from creole import config
from creole.client import CreoleClient
from pyeole.output_parsers.swanctl_raw import parse_raw_output
from pyeole.process import system_out
from IPy import IP

def format_tunnel_data(tunnel_data: dict) -> tuple:
    """Process data in tunnel_data for display.
    :param tunnel_data: data about tunnels as returned by parse_raw_output().get_status()['tunnels']
    :type tunnel_data: dictionnary
    :returns: tuple with data about tunnels formated for display and basic statistics
    :rtype: tuple with three int and a list of dictionnaries
    """
    def test_network(network):
        """Add information on IP accessibility in given network.
        :param network: network specification
        :type network: str parsable by IP class
        :returns: IP with additionnal information on accessibility
        :rtype: str
        """
        ip_list = []
        try:
            test_rvp = open('/usr/share/eole/test-rvp','r').read().split('\n')
            for line in test_rvp:
                if 'fping' in line:
                    ip_list.append(line.split('fping')[1].strip())
        except Exception:
            pass
        try:
            test_rvp = open('/usr/share/eole/test-rvp_more_ip','r').read().split('\n')
            for line in test_rvp:
                if 'fping' in line:
                    ip_list.append(line.split('fping')[1].strip())
        except Exception:
            pass
        try:
            subnet = IP(network)
            untested = True
            for ip in ip_list:
                if IP(ip) in subnet:
                    cmd = ["fping", "-r", "1", "-t", "2000", str(ip)]
                    errcode, _, _ = system_out(cmd)
                    if errcode != 0:
                        formated_network = f"{ip} injoignable"
                    else:
                        formated_network = f"{ip} joignable"
                    untested = False
        except Exception:
            pass
        if untested:
            formated_network = "non testé"
        return formated_network
    
    up = 0
    down = 0
    total = 0
    connections = []

    for sa, sa_data in tunnel_data.items():
        state = sa_data['state'].upper()
        src = sa_data['local-host']
        dst = sa_data['remote-host']
        sa_status = 'On' if state in ['ESTABLISHED', 'PASS'] else 'Off'
        associations = []
        for child_sa, child_sa_data in sa_data['child-sas'].items():
            mode = child_sa_data['mode']
            local_ts = child_sa_data['local-ts']
            remote_ts = child_sa_data['remote-ts']
            child_src = '\n'.join(local_ts) if isinstance(local_ts, list) else local_ts
            child_dst = '\n'.join(remote_ts) if isinstance(remote_ts, list) else remote_ts
            child_dst_status = [test_network(dst) for dst in child_dst.split('\n')]
            tunnels = [(f"{t[0]} — {t[1]}", t[2]) for t in zip(child_src.split('\n'), child_dst.split('\n'), child_dst_status)]
            child_sa_status = 'On' if mode in ['TUNNEL', 'PASS'] else 'Off'
            associations.append({
                'id': child_sa,
                'tunnels': tunnels,
                })
            if child_sa_data['state'] in ('INSTALLED', 'PASS'):
                up += 1
            else:
                down += 1
            total += 1
        connection = {
            'id': f"{src} — {dst}",
            'associations': associations,
            }

        connections.append(connection)

    return connections, total, down, up


dico_eole = CreoleClient().get_creole()
install_rvp = dico_eole.get('install_rvp', 'non')
cmd = [". /usr/lib/eole/diagnose.sh"]
cmd.append('EchoGras "*** Réseau virtuel privé"')

status_colors = (
        (re.compile(r'non testé$'), 'EchoOrange'),
        (re.compile(r'.* injoignable$'), 'EchoRouge'),
        (re.compile(r'.* joignable$'), 'EchoVert'),
        )
# test du RVP si configuré
if install_rvp == "oui":
    tunnels = []

    _, ret_sas, _ = system_out(['swanctl', '--list-sas', '--raw'])
    _, ret_stats, _ = system_out(['swanctl', '--stats', '--raw'])
    sw_status = parse_raw_output(ret_sas + ret_stats).get_status()
    connections, total, down, up = format_tunnel_data(sw_status['tunnels'])

    if dico_eole.get('type_amon') is not None:
        # diagnose Amon
        TEST_RVP = "/usr/share/eole/test-rvp"
        if isfile(TEST_RVP):
            if total == 0:
                cmd.append('printf ".  %${len_pf}s =>" "RVP"')
                cmd.append('EchoOrange " Aucun tunnel actif"')
            else:
                for connection in connections:
                    cmd.append(f"EchoGras \"{connection['id']}\"")
                    for association in connection['associations']:
                        cmd.append(f"echo {association['id']}")
                        for tunnel in association['tunnels']:
                            for status_color in status_colors:
                                if status_color[0].match(tunnel[1]):
                                    color = status_color[1]
                                    break
                            cmd.append(f'printf ". %${{len_pf}}s => " "{tunnel[0]}" \n{color} "{tunnel[1]}"')
        else:
            cmd.append('NoConfig "RVP"')
    else:
        # diagnose Eolebase ou Sphynx
        activer_haute_dispo = dico_eole.get('activer_haute_dispo', 'non')
        local_ipsec = True
        remote_ipsec = False
        if activer_haute_dispo != 'non':
            crm_cmd = ['crm', 'resource', 'show', 'ipsec_rsc']
            crm_code, crm_output, err_output = system_out(crm_cmd)
            if crm_code == 0:
                if isinstance(crm_output, bytes):
                    crm_output = crm_output.decode()
                crm_output = crm_output.split('\n')[0]
                local_node = dico_eole['nom_machine']
                if dico_eole['activer_haute_dispo'] == 'maitre':
                    remote_node = dico_eole['nom_machine_esclave']
                elif dico_eole['activer_haute_dispo'] == 'esclave':
                    remote_node = dico_eole['nom_machine_maitre']
                find_line = "^resource ipsec_rsc is running on: {0} $"
                if re.match(find_line.format(local_node), crm_output):
                    local_ipsec = True
                    remote_ipsec = False
                elif re.match(find_line.format(remote_node), crm_output):
                    local_ipsec = False
                    remote_ipsec = True
            else:
                local_ipsec = False
                remote_ipsec = False
        if local_ipsec:
            cmd.append('printf ".  %${len_pf}s =>" "VPN"')
            if total == 0:
                cmd.append('EchoVert " Aucun tunnel"')
            elif up < total:
                cmd.append('EchoOrange " {0} tunnel{1} actif{1} sur {2}"'.format(up, '' if up < 2 else 's',total))
            else:
                cmd.append('EchoVert " Ok ({0} tunnel{1} actif{1} sur {2})"'.format(up, '' if up < 2 else 's',total))
            cmd.append('TestService ARV $(CreoleGet adresse_ip_eth0):$(CreoleGet arv_port)')
        elif remote_ipsec:
            cmd.append('printf ".  %${len_pf}s =>" ""')
            cmd.append('EchoBleu "  IPsec sur noeud distant"')
        else:
            cmd.append('printf ".  %${len_pf}s =>" ""')
            cmd.append('EchoRouge "  IPsec sur aucun noeud du cluster"')
else:
    cmd.append('Inactif "RVP"')
cmd.append('echo')
# Exécution des commandes
cmd_string = "\n".join(cmd)
err, ret = getstatusoutput(cmd_string)
print(ret)
exit(0)
