from __future__ import absolute_import
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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 time
import sys
import json
from .common import Test, SkipTest, TestServer, free_tcp_port, ensureCanTestExtendedSASL
from proton import SSLDomain
from proton.reactor import Container
from proton.handlers import MessagingHandler
from .ssl import _testpath


def write_connect_conf(obj):
    with open('connect.json', 'w') as outfile:
        json.dump(obj, outfile)


class Server(MessagingHandler):
    def __init__(self, expected_user=None, scheme='amqps'):
        super(Server, self).__init__()
        self.port = free_tcp_port()
        self.scheme = scheme
        self.url = '%s://localhost:%i' % (self.scheme, self.port)
        self.expected_user = expected_user
        self.verified_user = False

    def on_start(self, event):
        self.listener = event.container.listen(self.url)

    def on_connection_opening(self, event):
        if self.expected_user:
            assert event.connection.transport.user == self.expected_user
            self.verified_user = True

    def on_connection_closing(self, event):
        event.connection.close()
        self.listener.close()


class Client(MessagingHandler):
    def __init__(self):
        super(Client, self).__init__()
        self.opened = False

    def on_connection_opened(self, event):
        self.opened = True
        event.connection.close()


class ConnectConfigTest(Test):
    def test_port(self):
        ensureCanTestExtendedSASL()
        server = Server(scheme='amqp')
        container = Container(server)
        client = Client()
        write_connect_conf({'port': server.port, 'scheme': 'amqp'})
        container.connect(handler=client, reconnect=False)
        container.run()
        assert client.opened

    def test_user(self):
        ensureCanTestExtendedSASL()
        user = 'user@proton'
        password = 'password'
        server = Server(user)
        container = Container(server)
        container.ssl.server.set_credentials(_testpath('server-certificate.pem'),
                                             _testpath('server-private-key.pem'),
                                             'server-password')
        client = Client()
        config = {
            'user': user,
            'password': password,
            'port': server.port,
            'tls': {
                'verify': False,
                'ca': _testpath('ca-certificate.pem')
            }
        }
        write_connect_conf(config)
        container.connect(handler=client, reconnect=False)
        container.run()
        assert client.opened
        assert server.verified_user

    def test_ssl(self):
        ensureCanTestExtendedSASL()
        server = Server(scheme='amqps')
        container = Container(server)
        container.ssl.server.set_credentials(_testpath('server-certificate-lh.pem'),
                                             _testpath('server-private-key-lh.pem'),
                                             'server-password')
        client = Client()
        config = {
            'scheme': 'amqps',
            'port': server.port,
            'tls': {
                'verify': True,
                'ca': _testpath('ca-certificate.pem')
            }
        }
        write_connect_conf(config)
        container.connect(handler=client, reconnect=False)
        container.run()
        assert client.opened

    def test_ssl_external(self):
        ensureCanTestExtendedSASL()
        server = Server(scheme='amqps')
        container = Container(server)
        container.ssl.server.set_credentials(_testpath('server-certificate-lh.pem'),
                                             _testpath('server-private-key-lh.pem'),
                                             'server-password')
        container.ssl.server.set_trusted_ca_db(_testpath('ca-certificate.pem'))
        container.ssl.server.set_peer_authentication(SSLDomain.VERIFY_PEER,
                                                     _testpath('ca-certificate.pem'))

        client = Client()
        config = {
            'scheme': 'amqps',
            'port': server.port,
            'sasl': {
                'mechanisms': 'EXTERNAL'
            },
            'tls': {
                'cert': _testpath('client-certificate.pem'),
                'key': _testpath('client-private-key-no-password.pem'),
                'ca': _testpath('ca-certificate.pem'),
                'verify': True
            }
        }
        write_connect_conf(config)
        container.connect(handler=client, reconnect=False)
        container.run()
        assert client.opened

    def test_ssl_plain(self):
        ensureCanTestExtendedSASL()
        user = 'user@proton'
        password = 'password'
        server = Server(expected_user=user, scheme='amqps')
        container = Container(server)
        container.ssl.server.set_credentials(_testpath('server-certificate-lh.pem'),
                                             _testpath('server-private-key-lh.pem'),
                                             'server-password')
        container.ssl.server.set_trusted_ca_db(_testpath('ca-certificate.pem'))
        container.ssl.server.set_peer_authentication(SSLDomain.VERIFY_PEER,
                                                     _testpath('ca-certificate.pem'))

        client = Client()
        config = {
            'scheme': 'amqps',
            'port': server.port,
            'user': user,
            'password': password,
            'sasl': {
                'mechanisms': 'PLAIN'
            },
            'tls': {
                'cert': _testpath('client-certificate.pem'),
                'key': _testpath('client-private-key-no-password.pem'),
                'ca': _testpath('ca-certificate.pem'),
                'verify': True
            }
        }
        write_connect_conf(config)
        container.connect(handler=client, reconnect=False)
        container.run()
        assert client.opened
