from unittest.mock import MagicMock
from cephadm.service_discovery import Root


class FakeDaemonDescription:
    def __init__(self, ip, ports, hostname, service_name='', daemon_type=''):
        self.ip = ip
        self.ports = ports
        self.hostname = hostname
        self._service_name = service_name
        self.daemon_type = daemon_type

    def service_name(self):
        return self._service_name


class FakeCache:
    def get_daemons_by_service(self, service_type):
        if service_type == 'ceph-exporter':
            return [FakeDaemonDescription('1.2.3.4', [9926], 'node0'),
                    FakeDaemonDescription('1.2.3.5', [9926], 'node1')]
        if service_type == 'nvmeof':
            return [FakeDaemonDescription('1.2.3.4', [10008], 'node0'),
                    FakeDaemonDescription('1.2.3.5', [10008], 'node1')]

        if service_type == 'nfs':
            return [FakeDaemonDescription('1.2.3.4', [9587], 'node0'),
                    FakeDaemonDescription('1.2.3.5', [9587], 'node1')]

        return [FakeDaemonDescription('1.2.3.4', [9100], 'node0'),
                FakeDaemonDescription('1.2.3.5', [9200], 'node1')]

    def get_daemons_by_type(self, daemon_type):
        return [FakeDaemonDescription('1.2.3.4', [9100], 'node0', 'ingress', 'haproxy'),
                FakeDaemonDescription('1.2.3.5', [9200], 'node1', 'ingress', 'haproxy')]


class FakeInventory:
    def get_addr(self, name: str):
        return '1.2.3.4'


class FakeServiceSpec:
    def __init__(self, port):
        self.monitor_port = port


class FakeSpecDescription:
    def __init__(self, port):
        self.spec = FakeServiceSpec(port)


class FakeSpecStore():
    def __init__(self, mgr):
        self.mgr = mgr
        self._specs = {'ingress': FakeSpecDescription(9049)}

    def __contains__(self, name):
        return name in self._specs

    def __getitem__(self, name):
        return self._specs['ingress']


class FakeMgr:
    def __init__(self):
        self.config = ''
        self.check_mon_command = MagicMock(side_effect=self._check_mon_command)
        self.mon_command = MagicMock(side_effect=self._check_mon_command)
        self.template = MagicMock()
        self.log = MagicMock()
        self.inventory = FakeInventory()
        self.cache = FakeCache()
        self.spec_store = FakeSpecStore(self)

    def get_mgr_id(self):
        return 'mgr-1'

    def list_servers(self):

        servers = [
            {'hostname': 'node0',
             'ceph_version': '16.2',
             'services': [{'type': 'mgr', 'id': 'mgr-1'}, {'type': 'mon'}]},
            {'hostname': 'node1',
             'ceph_version': '16.2',
             'services': [{'type': 'mgr', 'id': 'mgr-2'}, {'type': 'mon'}]}
        ]

        return servers

    def _check_mon_command(self, cmd_dict, inbuf=None):
        prefix = cmd_dict.get('prefix')
        if prefix == 'get-cmd':
            return 0, self.config, ''
        if prefix == 'set-cmd':
            self.config = cmd_dict.get('value')
            return 0, 'value set', ''
        return -1, '', 'error'

    def get_module_option_ex(self, module, option, default_value):
        return "9283"


class TestServiceDiscovery:

    def test_get_sd_config_prometheus(self):
        mgr = FakeMgr()
        root = Root(mgr, 5000, '0.0.0.0')
        cfg = root.get_sd_config('mgr-prometheus')

        # check response structure
        assert cfg
        for entry in cfg:
            assert 'labels' in entry
            assert 'targets' in entry

        # check content
        assert cfg[0]['targets'] == ['node0:9283']

    def test_get_sd_config_node_exporter(self):
        mgr = FakeMgr()
        root = Root(mgr, 5000, '0.0.0.0')
        cfg = root.get_sd_config('node-exporter')

        # check response structure
        assert cfg
        for entry in cfg:
            assert 'labels' in entry
            assert 'targets' in entry

        # check content
        assert cfg[0]['targets'] == ['1.2.3.4:9100']
        assert cfg[0]['labels'] == {'instance': 'node0'}
        assert cfg[1]['targets'] == ['1.2.3.5:9200']
        assert cfg[1]['labels'] == {'instance': 'node1'}

    def test_get_sd_config_alertmgr(self):
        mgr = FakeMgr()
        root = Root(mgr, 5000, '0.0.0.0')
        cfg = root.get_sd_config('alertmanager')

        # check response structure
        assert cfg
        for entry in cfg:
            assert 'labels' in entry
            assert 'targets' in entry

        # check content
        assert cfg[0]['targets'] == ['1.2.3.4:9100', '1.2.3.5:9200']

    def test_get_sd_config_haproxy(self):
        mgr = FakeMgr()
        root = Root(mgr, 5000, '0.0.0.0')
        cfg = root.get_sd_config('haproxy')

        # check response structure
        assert cfg
        for entry in cfg:
            assert 'labels' in entry
            assert 'targets' in entry

        # check content
        assert cfg[0]['targets'] == ['1.2.3.4:9049']
        assert cfg[0]['labels'] == {'instance': 'ingress'}

    def test_get_sd_config_ceph_exporter(self):
        mgr = FakeMgr()
        root = Root(mgr, 5000, '0.0.0.0')
        cfg = root.get_sd_config('ceph-exporter')

        # check response structure
        assert cfg
        for entry in cfg:
            assert 'labels' in entry
            assert 'targets' in entry

        # check content
        assert cfg[0]['targets'] == ['1.2.3.4:9926']

    def test_get_sd_config_nvmeof(self):
        mgr = FakeMgr()
        root = Root(mgr, 5000, '0.0.0.0')
        cfg = root.get_sd_config('nvmeof')

        # check response structure
        assert cfg
        for entry in cfg:
            assert 'labels' in entry
            assert 'targets' in entry

        # check content
        assert cfg[0]['targets'] == ['1.2.3.4:10008']

    def test_get_sd_config_nfs(self):
        mgr = FakeMgr()
        root = Root(mgr, 5000, '0.0.0.0')
        cfg = root.get_sd_config('nfs')

        # check response structure
        assert cfg
        for entry in cfg:
            assert 'labels' in entry
            assert 'targets' in entry

        # check content
        assert cfg[0]['targets'] == ['1.2.3.4:9587']

    def test_get_sd_config_invalid_service(self):
        mgr = FakeMgr()
        root = Root(mgr, 5000, '0.0.0.0')
        cfg = root.get_sd_config('invalid-service')
        assert cfg == []
