#!/usr/bin/env python3

import datetime
import time
import yaml

import balancer.production.x.perf_tests.run as run


def has_attr(c, name):
    try:
        getattr(c, name)
        return True
    except AttributeError:
        return False


class Host(object):
    def __init__(self, d):
        for k, v in d.items():
            setattr(self, k, v)

    cont_name: str
    name: str


class Hosts(object):
    def __init__(self, d):
        for k, v in d.items():
            if not isinstance(v, dict):
                setattr(self, k, v)
            else:
                setattr(self, k, Host(v))

    def has_new(self):
        return has_attr(self, 'new')

    def set_new(self, cont_name, name):
        if self.has_new():
            self.new.cont_name = cont_name
            self.new.name = name
        else:
            setattr(self, 'new', Host({'cont_name': cont_name, 'name': name}))

    def has_old(self):
        return has_attr(self, 'old')

    def set_old(self, cont_name, name):
        if self.has_old():
            self.old.cont_name = cont_name
            self.old.name = name
        else:
            setattr(self, 'old', Host({'cont_name': cont_name, 'name': name}))

    new: Host
    old: Host
    port: int           # TODO_L fix for awacslet - use call to admin like in instancectl.conf
    admin_port: int
    family: str
    gencfg: bool


class Options(object):
    def __init__(self, d):
        for k, v in d.items():
            if not isinstance(v, dict):
                setattr(self, k, v)
            else:
                setattr(self, k, Hosts(v))
        self.report = True

    def has_ticket(self):
        return has_attr(self, 'ticket')

    def has_warden(self):
        return has_attr(self, 'warden_service')

    def has_target_rp5s(self):
        return has_attr(self, 'target_rp5s')

    hosts: Hosts
    prj: str
    monitoring_path: str
    monitoring_params: str
    weight_file: str
    controls_dir: str
    cont_user_group: str
    is_term: bool
    target_rp5s: int
    report: bool
    ticket: str
    warden_service: str


def print_completed_config(opts):
    print('\nSelected host name:', opts.hosts.new.name, ', container name', opts.hosts.new.cont_name)
    if opts.hosts.has_old():
        print('Selected old host name:', opts.hosts.old.name, ', container name', opts.hosts.old.cont_name)
    print("\nConfig: ", opts.hosts.family, opts.hosts.gencfg,
          opts.monitoring_path, opts.monitoring_params, opts.weight_file, opts.cont_user_group,
          opts.controls_dir, opts.is_term, opts.report)
    if opts.has_target_rp5s():
        print('Target rp5s:', opts.target_rp5s)
    if has_attr(opts, 'ticket'):
        print('Ticket:', opts.ticket)
    if has_attr(opts, 'warden_service'):
        print('Warden service:', opts.warden_service)


def check_host(host):
    assert host.cont_name
    assert host.name


def check_hosts(hosts):
    if hosts.has_new():
        check_host(hosts.new)
    if hosts.has_old():
        check_host(hosts.old)
    assert hosts.admin_port
    assert hosts.family
    assert has_attr(hosts, 'gencfg')


def check_options(opts):
    check_hosts(opts.hosts)
    assert opts.prj
    assert opts.weight_file
    assert has_attr(opts, 'is_term')


def set_family(hosts):
    families = hosts.family.split(',')
    assert len(families)
    date = '{0:%d}'.format(datetime.datetime.now())
    index = int(date) % len(families)
    hosts.family = families[index].rstrip()


def get_hosts(hosts, with_old=False):
    if hosts.gencfg:
        cont_names = run.local_script('sky list M@' + hosts.family + ':ACTIVE')
        names = run.local_script('sky list f@' + hosts.family + ':ACTIVE')
        print('Names:', len(names), ', cont_names:', len(cont_names))
        assert len(cont_names)
        assert len(cont_names) == len(names), "cont_names size is {0}, names is {1}".format(len(cont_names), len(names))
        if with_old:
            assert len(cont_names) >= 2
            hosts.set_new(cont_names[0], names[0])
            hosts.set_old(cont_names[1], names[1])
        else:
            index = int(time.time()) % len(cont_names)
            hosts.set_new(cont_names[index], names[index])
    else:
        links = run.local_script('sky list Y@' + hosts.family + ':transient')
        cont_names = run.local_script('sky list Y@' + hosts.family + ':persistent')
        names = run.local_script('sky list Y@' + hosts.family + ':node')
        print('Links:', len(links), ', names:', len(names), ', cont_names:', len(cont_names))
        assert len(links)
        # Can't get only active hosts and containers, but links are only active
        assert len(links) <= len(cont_names), "links size is {0}, cont_names is {1}".format(len(links), len(cont_names))
        assert len(links) <= len(names), "links size is {0}, names is {1}".format(len(links), len(names))
        # Find name in link
        index = int(time.time()) % len(links)
        link = links[index]  # sas2-3135-6.balancer-knoss-morda-yp-sas-35.sas.yp-c.yandex.net
        name_delim = link.find('-')
        name_delim = link.find('-', name_delim + 1)
        assert name_delim != -1
        name_id = link[:name_delim]
        for n in names:
            if n.find(name_id) == 0:
                name = n
                break
        assert name
        # Find cont name in link
        cont_delim = link.find('.')
        assert cont_delim
        cont_name = link[cont_delim + 1:]
        assert cont_name in cont_names

        hosts.set_new(cont_name, name)


def set_defaults(opts):
    if not has_attr(opts, 'controls_dir'):
        setattr(opts, 'controls_dir', 'controls')
    if not has_attr(opts, 'cont_user_group'):
        setattr(opts, 'cont_user_group', 'loadbase:loadbase')


def set_monitoring_params(opts):
    opts.monitoring_params = 'itype=balancer;ctype=prestable,prod;prj=' + opts.prj + ';'

    opts.monitoring_path = '/template/panel/balancer_load_panel/prj=' + opts.prj + ';host='
    if opts.is_term:
        opts.monitoring_path += opts.hosts.new.name + ';'
        if opts.hosts.has_old():
            opts.monitoring_path += 'host_old=' + opts.hosts.old.name + ';'
    else:
        opts.monitoring_path += opts.hosts.new.cont_name + ';'
    if has_attr(opts, 'target_rp5s'):
        opts.monitoring_path += 'max_requests=' + str(opts.target_rp5s) + ';'


def get_options(config_file):
    with open(config_file, 'r') as f:
        d = yaml.safe_load(f)
        opts = Options(d)
        check_options(opts)

    set_family(opts.hosts)

    if not opts.hosts.has_new():
        assert not opts.hosts.has_old()
        get_hosts(opts.hosts, has_attr(opts, 'warden_service') and opts.warden_service == 'kubr_exp')

    set_defaults(opts)

    set_monitoring_params(opts)

    return opts
