#!/usr/bin/env python3

# Copyright 2021 Canonical Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import copy
import json
import unittest
import sys
sys.path.append('lib')  # noqa
sys.path.append('src')  # noqa
from ops.testing import Harness
from ops.charm import CharmBase

from unit_tests.manage_test_relations import (
    add_loadbalancer_relation,
    add_loadbalancer_response,
    add_requesting_dash_relation,
    add_requesting_glance_relation,
    loadbalancer_data,
)
from interface_openstack_loadbalancer.loadbalancer import (
    OSLoadbalancerRequires,
    OSLoadbalancerProvides)


class TestOSLoadbalancerRequires(unittest.TestCase):

    class MyCharm(CharmBase):

        def __init__(self, *args):
            super().__init__(*args)
            self.seen_events = []
            self.ingress = OSLoadbalancerRequires(
                self,
                'loadbalancer')
            self.framework.observe(
                self.ingress.on.lb_requested,
                self._log_event)
            self.framework.observe(
                self.ingress.on.lb_configured,
                self._log_event)
            self.framework.observe(
                self.ingress.on.lb_relation_ready,
                self._register_ep)

        def _log_event(self, event):
            self.seen_events.append(type(event).__name__)

        def _register_ep(self, event):
            self._log_event(event)
            self.seen_events.append(type(event).__name__)
            self.ingress.request_loadbalancer(
                'ceph-dashboard',
                8443,
                8443,
                '10.0.0.10',
                lb_check_type='https')

    def setUp(self):
        super().setUp()
        self.harness = Harness(
            self.MyCharm,
            meta='''
name: my-charm
requires:
  loadbalancer:
    interface: api-endpoints
'''
        )
        self.eps = [{
            'service-name': 'ceph-dashboard',
            'frontend-port': 8443,
            'backend-port': 8443,
            'backend-ip': '10.0.0.10',
            'check-type': 'https'}]

    def test_init(self):
        self.harness.begin()
        self.assertEqual(
            self.harness.charm.ingress.relation_name,
            'loadbalancer')

    def test__on_relation_changed(self):
        self.harness.begin()
        self.harness.set_leader()
        rel_id = add_loadbalancer_relation(self.harness)
        unit_rel_data = self.harness.get_relation_data(
            rel_id,
            'my-charm/0')
        app_rel_data = self.harness.get_relation_data(
            rel_id,
            'my-charm')
        self.assertEqual(
            json.loads(unit_rel_data['endpoints']),
            [{
                'service-name': 'ceph-dashboard',
                'backend-port': 8443,
                'backend-ip': '10.0.0.10'}])
        self.assertEqual(
            json.loads(app_rel_data['endpoints']),
            [{
                'service-name': 'ceph-dashboard',
                'frontend-port': 8443,
                'check-type': 'https'}])

    def test_register_second_ep(self):
        self.harness.begin()
        self.harness.set_leader()
        rel_id = add_loadbalancer_relation(self.harness)
        new_eps = copy.deepcopy(self.eps)
        new_eps.append({
            'service-name': 'ceph-api',
            'frontend-port': 9443,
            'backend-port': 9443,
            'backend-ip': '10.0.0.10',
            'check-type': 'https'})
        self.harness.charm.ingress.request_loadbalancer(
            'ceph-api',
            9443,
            9443,
            '10.0.0.10',
            lb_check_type='https')
        unit_rel_data = self.harness.get_relation_data(
            rel_id,
            'my-charm/0')
        app_rel_data = self.harness.get_relation_data(
            rel_id,
            'my-charm')
        self.assertEqual(
            json.loads(unit_rel_data['endpoints']),
            [
                {
                    'service-name': 'ceph-dashboard',
                    'backend-port': 8443,
                    'backend-ip': '10.0.0.10'},
                {
                    'service-name': 'ceph-api',
                    'backend-port': 9443,
                    'backend-ip': '10.0.0.10'}])
        self.assertEqual(
            json.loads(app_rel_data['endpoints']),
            [
                {
                    'service-name': 'ceph-dashboard',
                    'frontend-port': 8443,
                    'check-type': 'https'},
                {
                    'service-name': 'ceph-api',
                    'frontend-port': 9443,
                    'check-type': 'https'}])

    def test_update_existing_request(self):
        self.harness.begin()
        self.harness.set_leader()
        rel_id = add_loadbalancer_relation(self.harness)
        self.harness.charm.ingress.request_loadbalancer(
            'ceph-dashboard',
            9443,
            9443,
            '10.0.0.20',
            lb_check_type='http')
        unit_rel_data = self.harness.get_relation_data(
            rel_id,
            'my-charm/0')
        app_rel_data = self.harness.get_relation_data(
            rel_id,
            'my-charm')
        self.assertEqual(
            json.loads(unit_rel_data['endpoints']),
            [
                {
                    'service-name': 'ceph-dashboard',
                    'backend-port': 9443,
                    'backend-ip': '10.0.0.20'}])
        self.assertEqual(
            json.loads(app_rel_data['endpoints']),
            [
                {
                    'service-name': 'ceph-dashboard',
                    'frontend-port': 9443,
                    'check-type': 'http'}])

    def test_get_frontend_data(self):
        self.harness.begin()
        self.harness.set_leader()
        rel_id = add_loadbalancer_relation(self.harness)
        add_loadbalancer_response(self.harness, rel_id)
        self.assertEqual(
            self.harness.charm.ingress.get_frontend_data(),
            loadbalancer_data)

    def test__process_response(self):
        self.harness.begin()
        self.harness.set_leader()
        rel_id = add_loadbalancer_relation(self.harness)
        self.assertNotIn(
            'LoadbalancerConfiguredEvent',
            self.harness.charm.seen_events)
        add_loadbalancer_response(self.harness, rel_id)
        self.assertIn(
            'LoadbalancerConfiguredEvent',
            self.harness.charm.seen_events)

    def test_retrieving_endpoints(self):
        self.harness.begin()
        self.harness.set_leader()
        rel_id = add_loadbalancer_relation(self.harness)
        self.assertIsNone(
            self.harness.charm.ingress.get_lb_endpoint(
                'ceph-dashboard',
                'public'))
        add_loadbalancer_response(self.harness, rel_id)
        self.assertEqual(
            self.harness.charm.ingress.get_lb_endpoint(
                'ceph-dashboard',
                'public'),
            {
                'ip': ['10.10.0.101'],
                'port': 8443,
                'protocol': 'http'})
        self.assertEqual(
            self.harness.charm.ingress.get_lb_public_endpoint(
                'ceph-dashboard'),
            {
                'ip': ['10.10.0.101'],
                'port': 8443,
                'protocol': 'http'})
        self.assertEqual(
            self.harness.charm.ingress.get_lb_internal_endpoint(
                'ceph-dashboard'),
            {
                'ip': ['10.30.0.101'],
                'port': 8443,
                'protocol': 'http'})
        self.assertEqual(
            self.harness.charm.ingress.get_lb_admin_endpoint(
                'ceph-dashboard'),
            {
                'ip': ['10.20.0.101'],
                'port': 8443,
                'protocol': 'http'})


class TestOSLoadbalancerProvides(unittest.TestCase):

    class MyCharm(CharmBase):

        def __init__(self, *args):
            super().__init__(*args)
            self.seen_events = []
            self.api_eps = OSLoadbalancerProvides(self)
            self.framework.observe(
                self.api_eps.on.lb_requested,
                self._log_event)
            self.framework.observe(
                self.api_eps.on.lb_configured,
                self._log_event)

        def _log_event(self, event):
            self.seen_events.append(type(event).__name__)

    def setUp(self):
        super().setUp()
        self.harness = Harness(
            self.MyCharm,
            meta='''
name: my-charm
provides:
  loadbalancer:
    interface: api-endpoints
'''
        )

    def test_on_changed(self):
        self.harness.begin()
        # No EndpointDataEvent as relation is absent
        self.assertEqual(
            self.harness.charm.seen_events,
            [])
        rel_id = self.harness.add_relation('loadbalancer', 'ceph-dashboard')
        self.harness.add_relation_unit(
            rel_id,
            'ceph-dashboard/0')
        self.harness.update_relation_data(
            rel_id,
            'ceph-dashboard/0',
            {'ingress-address': '10.0.0.3'})
        self.assertIn(
            'LoadbalancerRequestsEvent',
            self.harness.charm.seen_events)

    def test__get_frontends(self):
        self.harness.begin()
        add_requesting_dash_relation(self.harness)
        add_requesting_glance_relation(self.harness)
        self.assertEqual(
            self.harness.charm.api_eps._get_frontends(),
            {
                'endpoints': {
                    'ceph-dashboard': {
                        'check_type': 'https',
                        'frontend_port': 8443},
                    'ceph-api': {
                        'check_type': 'https',
                        'frontend_port': 9443},
                    'glance-api': {
                        'check_type': 'http',
                        'frontend_port': 9292}}})

    def test__get_backends(self):
        self.harness.begin()
        add_requesting_dash_relation(self.harness)
        add_requesting_glance_relation(self.harness)
        self.assertEqual(
            self.harness.charm.api_eps._get_backends(),
            {
                'ceph-dashboard': [
                    {
                        'unit_name': 'ceph-dashboard_0',
                        'backend_ip': '10.0.0.10',
                        'backend_port': 8443},
                    {
                        'unit_name': 'ceph-dashboard_1',
                        'backend_ip': '10.0.0.11',
                        'backend_port': 8443}],
                'ceph-api': [
                    {
                        'unit_name': 'ceph-dashboard_0',
                        'backend_ip': '10.0.0.10',
                        'backend_port': 9443},
                    {
                        'unit_name': 'ceph-dashboard_1',
                        'backend_ip': '10.0.0.11',
                        'backend_port': 9443}],
                'glance-api': [
                    {
                        'unit_name': 'glance_0',
                        'backend_ip': '10.0.0.50',
                        'backend_port': 9292}]})

    def test_get_loadbalancer_requests(self):
        self.harness.begin()
        add_requesting_dash_relation(self.harness)
        add_requesting_glance_relation(self.harness)
        self.assertEqual(
            self.harness.charm.api_eps.get_loadbalancer_requests(),
            {
                'endpoints': {
                    'ceph-dashboard': {
                        'check_type': 'https',
                        'frontend_port': 8443,
                        'members': [
                            {
                                'backend_ip': '10.0.0.10',
                                'backend_port': 8443,
                                'unit_name': 'ceph-dashboard_0'},
                            {
                                'backend_ip': '10.0.0.11',
                                'backend_port': 8443,
                                'unit_name': 'ceph-dashboard_1'}]},
                    'ceph-api': {
                        'check_type': 'https',
                        'frontend_port': 9443,
                        'members': [
                            {
                                'backend_ip': '10.0.0.10',
                                'backend_port': 9443,
                                'unit_name': 'ceph-dashboard_0'},
                            {
                                'backend_ip': '10.0.0.11',
                                'backend_port': 9443,
                                'unit_name': 'ceph-dashboard_1'}]},
                    'glance-api': {
                        'check_type': 'http',
                        'frontend_port': 9292,
                        'members': [
                            {
                                'backend_ip': '10.0.0.50',
                                'backend_port': 9292,
                                'unit_name': 'glance_0'}]}}})

    def test_send_loadbalancer_response(self):
        self.harness.begin()
        self.harness.set_leader()
        dash_rel_id = add_requesting_dash_relation(self.harness)
        glance_rel_id = add_requesting_glance_relation(self.harness)
        self.harness.charm.api_eps.loadbalancer_ready(
            'ceph-dashboard',
            'admin',
            ['10.20.0.101'],
            8443,
            'http')
        self.harness.charm.api_eps.loadbalancer_ready(
            'ceph-dashboard',
            'internal',
            ['10.30.0.101'],
            8443,
            'http')
        self.harness.charm.api_eps.loadbalancer_ready(
            'ceph-dashboard',
            'public',
            ['10.10.0.101'],
            8443,
            'http')
        self.harness.charm.api_eps.loadbalancer_ready(
            'ceph-api',
            'admin',
            ['10.20.0.101'],
            9443,
            'http')
        self.harness.charm.api_eps.loadbalancer_ready(
            'ceph-api',
            'internal',
            ['10.30.0.101'],
            9443,
            'http')
        self.harness.charm.api_eps.loadbalancer_ready(
            'ceph-api',
            'public',
            ['10.10.0.101'],
            9443,
            'http')
        self.harness.charm.api_eps.loadbalancer_ready(
            'glance-api',
            'admin',
            ['10.20.0.101'],
            9292,
            'http')
        self.harness.charm.api_eps.loadbalancer_ready(
            'glance-api',
            'internal',
            ['10.30.0.101'],
            9292,
            'http')
        self.harness.charm.api_eps.loadbalancer_ready(
            'glance-api',
            'public',
            ['10.10.0.101'],
            9292,
            'http')
        self.harness.charm.api_eps.advertise_loadbalancers()
        dash_rel_data = self.harness.get_relation_data(
            dash_rel_id,
            'my-charm')
        self.assertEqual(
            json.loads(dash_rel_data['frontends']),
            {
                'ceph-dashboard': {
                    'admin': {
                        'ip': ['10.20.0.101'],
                        'port': 8443,
                        'protocol': 'http'},
                    'internal': {
                        'ip': ['10.30.0.101'],
                        'port': 8443,
                        'protocol': 'http'},
                    'public': {
                        'ip': ['10.10.0.101'],
                        'port': 8443,
                        'protocol': 'http'}},
                'ceph-api': {
                    'admin': {
                        'ip': ['10.20.0.101'],
                        'port': 9443,
                        'protocol': 'http'},
                    'internal': {
                        'ip': ['10.30.0.101'],
                        'port': 9443,
                        'protocol': 'http'},
                    'public': {
                        'ip': ['10.10.0.101'],
                        'port': 9443,
                        'protocol': 'http'}}})
        glance_rel_data = self.harness.get_relation_data(
            glance_rel_id,
            'my-charm')
        self.assertEqual(
            json.loads(glance_rel_data['frontends']),
            {
                'glance-api': {
                    'admin': {
                        'ip': ['10.20.0.101'],
                        'port': 9292,
                        'protocol': 'http'},
                    'internal': {
                        'ip': ['10.30.0.101'],
                        'port': 9292,
                        'protocol': 'http'},
                    'public': {
                        'ip': ['10.10.0.101'],
                        'port': 9292,
                        'protocol': 'http'}}})