#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (C) 2015 Avencall
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>


import argparse
import configparser
import sys

import netaddr
from jinja2 import Template
from pathlib import Path
from xivo_dao import init_db_from_config, default_config
from xivo_dao.alchemy.dhcp import Dhcp
from xivo_dao.alchemy.general import General
from xivo_dao.alchemy.mail import Mail
from xivo_dao.alchemy.monitoring import Monitoring
from xivo_dao.alchemy.netiface import Netiface
from xivo_dao.alchemy.provisioning import Provisioning
from xivo_dao.alchemy.resolvconf import Resolvconf
from xivo_dao.helpers.db_utils import session_scope

XIVO_COMMON_CONF_TEMPLATE = Template("""\
### AUTOMATICALLY GENERATED BY xivo-create-config. DO NOT EDIT ###

{%- for key, value in config.items() %}
XIVO_{{ key.upper() }}="{{ value if value != None}}"
{%- endfor %}

""")

ARI_CONF = "/etc/asterisk/ari.conf"


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-o',
                        '--output',
                        action='store',
                        default='/etc/xivo/common.conf',
                        help="File where to write the output. Use '-' for stdout. Default: %(default)s")
    args = parser.parse_args()
    init_db_from_config(default_config())
    config = load_config()
    with (sys.stdout if args.output == '-' else open(args.output, 'w')) as output:
        output.write(XIVO_COMMON_CONF_TEMPLATE.render(config=config))


def load_config():
    with session_scope() as session:
        result = {}
        result.update(load_config_dhcp(session))
        result.update(load_config_smtp(session))
        result.update(load_config_provisioning(session))
        result.update(load_config_monitoring(session))
        result.update(load_config_resolvconf(session))
        result.update(load_config_netiface(session))
        result.update(load_config_general(session))
        result.update(load_config_docker())
        return result


def load_config_dhcp(session):
    dhcp = session.query(
        Dhcp.active,
        Dhcp.pool_start,
        Dhcp.pool_end,
        Dhcp.extra_ifaces,
    ).first()

    return {
        'dhcp_active': dhcp.active,
        'dhcp_extra_ifaces': dhcp.extra_ifaces,
        'dhcp_pool': '{start} {end}'.format(start=dhcp.pool_start, end=dhcp.pool_end),
    }


def load_config_smtp(session):
    smtp = session.query(
        Mail.mydomain,
        Mail.origin,
        Mail.relayhost,
        Mail.fallback_relayhost,
        Mail.canonical
    ).first()

    return {
        'smtp_mydomain': smtp.mydomain or None,
        'smtp_origin': smtp.origin,
        'smtp_relayhost': smtp.relayhost,
        'smtp_fallback_relayhost': smtp.fallback_relayhost,
        'smtp_canonical': smtp.canonical,
    }


def load_config_provisioning(session):
    provd = session.query(
        Provisioning.net4_ip,
        Provisioning.http_port,
        Provisioning.username,
        Provisioning.password,
        Provisioning.net4_ip_rest,
        Provisioning.rest_port,
        Provisioning.private,
        Provisioning.secure,
        Provisioning.dhcp_integration
    ).first()

    config = configparser.RawConfigParser()
    config.read(ARI_CONF)

    return {
        'provd_net4_ip': provd.net4_ip,
        'provd_http_port': str(provd.http_port),
        'provd_username': provd.username,
        'provd_password': provd.password,
        'provd_rest_port': str(provd.rest_port),
        'provd_rest_net4_ip': provd.net4_ip_rest,
        'provd_rest_authentication': provd.private,
        'provd_rest_ssl': provd.secure,
        'provd_dhcp_integration': provd.dhcp_integration,
        'provd_asterisk_ari_password': config.get("xivo", "password", fallback=""),
    }


def load_config_monitoring(session):
    monitoring = session.query(
        Monitoring.maintenance,
        Monitoring.alert_emails,
        Monitoring.dahdi_monitor_ports,
        Monitoring.max_call_duration,
    ).first()

    return {
        'maintenance': bool(monitoring.maintenance),
        'alert_emails': (monitoring.alert_emails.replace('\r\n', ' ')
                         if monitoring.alert_emails
                         else None),
        'dahdi_monitor_ports': monitoring.dahdi_monitor_ports or None,
        'max_call_duration': (
            None if monitoring.max_call_duration is None or monitoring.max_call_duration < 1
            else str(monitoring.max_call_duration)),
    }


def load_config_resolvconf(session):
    resolvconf = session.query(
        Resolvconf.hostname,
        Resolvconf.domain,
        Resolvconf.nameserver1,
        Resolvconf.nameserver2,
        Resolvconf.nameserver3,
    ).first()

    return {
        'hostname': resolvconf.hostname,
        'domain': resolvconf.domain,
        'extra_dns_search': '',
        'nameservers': ' '.join(nameserver
                                for nameserver in (resolvconf.nameserver1,
                                                   resolvconf.nameserver2,
                                                   resolvconf.nameserver3)
                                if nameserver)
    }


def load_config_netiface(session):
    netiface = session.query(
        Netiface.ifname,
        Netiface.address,
        Netiface.netmask,
    ).filter(
        Netiface.networktype == 'voip'
    ).first()

    return {
        'voip_ifaces': netiface.ifname,
        'net4_ip': netiface.address,
        'net4_netmask': netiface.netmask,
        'net4_subnet': str(netaddr.IPNetwork('{n.address}/{n.netmask}'.format(n=netiface)).network)
    }


def load_config_general(session):
    general = session.query(
        General.handle_system_conf
    ).first()
    return {
        'handle_system_conf': '1' if general.handle_system_conf else '0'
    }


def load_config_docker():
    mds_enabled_file_path = "/var/lib/xivo/mds_enabled"
    mds_enabled_file = Path(mds_enabled_file_path)
    if mds_enabled_file.exists():
        docker_config = {
            'docker_net': load_docker_configuration_item("DOCKER_NET", "mds")
        }
    else:
        docker_config = {
            'docker_net': load_docker_configuration_item("DOCKER_NET", "xivo")
        }

    return docker_config


def load_docker_configuration_item(config_item, mds_type):
    docker_config = docker_config_factory(mds_type)
    is_docker_config_item_overridden = text_in_file(config_item, docker_config['custom_env'])
    if is_docker_config_item_overridden:
        docker_config_item = get_docker_config_item(config_item, docker_config['custom_env'])
    else:
        docker_config_item = get_docker_config_item(config_item, docker_config['factory_env'])
    return docker_config_item


def docker_config_factory(mds_type):
    config_object = {
        'docker_folder':  Path("/etc/docker/{}/".format(mds_type)),
        'custom_env':     Path("/etc/docker/{}/custom.env".format(mds_type)),
        'factory_env':    Path("/etc/docker/{}/factory.env".format(mds_type)),
    }

    return config_object


def text_in_file(text, file):
    result = False
    with open(file) as f:
        if text in f.read():
            result = True

    return result


def get_docker_config_item(config_item, file):
    with open(file, "r") as f:
        for line in f:
            if config_item in line: return format_docker_config_item(line)


def format_docker_config_item(line):
    return "{}".format(line.strip().split('=')[1])


if __name__ == '__main__':
    main()
