#!/usr/bin/env python

import os
import sys

sys.path.append(os.path.abspath(
    os.path.join(__file__, '../../lib/python')))

import argparse
import re
from ssl import SSLError

from ovsdb_plugin.common import CERT_BEG, CERT_END, SEPARATOR
from ovsdb_plugin.mixins.loggable import LoggableMixin
from ovsdb_plugin.models.jmx import JMXPasswordFile
from ovsdb_plugin.models.keypair import Keypair
from ovsdb_plugin.utils import sdt
from ovsdb_plugin.utils.logger_setup import setup_logging


class Send(object, LoggableMixin):

    def __init__(self, addr):
        self.addr = addr
        self.keypair = Keypair()
        self.jmx_file = JMXPasswordFile()

    def execute(self):
        cacert = self._get_ca_cert()
        pattern = '({}[\n\S]+{})'.format(CERT_BEG, CERT_END)
        matches = re.search(pattern, cacert, re.MULTILINE)

        if matches is None or len(matches.groups()) == 0:
            self._error("You did not provide a valid CA cert. Please make "
                        "sure that you include '{}', '{}' and everything in "
                        "between".format(CERT_BEG, CERT_END))
            self._error("Received: {}".format(cacert))
            return 1

        cacert = matches.group(0)
        cacert_path = '/tmp/ovsdb_cacert'

        # Save the CA cert for use below.
        with open(cacert_path, 'w') as cacert_file:
            cacert_file.write(cacert)

        local = sdt.SimpleDataTransferPeer(key_path=self.keypair.key_path,
                                           cert_path=self.keypair.crt_path,
                                           cacert_path=cacert_path,
                                           role=sdt.ROLE_SENDER)

        # Build the data to be sent
        data = []
        paths = [
            self.keypair.key_path,
            self.keypair.crt_path,
            self.jmx_file.path
        ]

        for path in paths:
            with open(path, 'r') as thefile:
                data.append(thefile.read())

        data = SEPARATOR.join(data)

        try:
            # Send!
            self._debug("Sending data to {}".format(self.addr))
            local.send(self.addr, data)
            self._debug("Send completed")
        except SSLError:
            self._error("Failed to verify remote host. Check that 1) you "
                        "provided the correct CA cert and 2) the remote host "
                        "doesn't have a time that is far ahead of localhost.")
            if self.cacert_was_piped:
                self._error("Received: {}".format(cacert))
            return 1
        except Exception as e:
            if hasattr(e, 'errno') and e.errno == 61:
                self._error("Connection refused: the remote host ssl:{} did "
                            "not accept our connection request."
                            .format(self.addr))
            else:
                self._error("Unexpected error occurred: {}".format(e))
            return 1

        self._info("Keypair sent to ssl:{}".format(self.addr))

        return 0

    def _get_ca_cert(self):
        self.cacert_was_piped = not sys.stdin.isatty()

        # If the CA cert is not piped through, prompt the user to
        # paste in the receiver's CA cert.
        if not self.cacert_was_piped:
            # We need the remote receiver's CA cert so that we can
            # be sure that it's the correct host and not some malicious
            # man in the middle.
            print "\nPlease paste the receiver's CA cert and press ENTER:"

        cacert = ""
        while CERT_END not in cacert:
            cacert += sys.stdin.readline()

        return re.sub('(\r\n)+', '\n', cacert)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('addr')
    parser.add_argument('--verbose', action='store_true')

    args = parser.parse_args()
    setup_logging(args.verbose, output='stdout')
    sys.exit(Send(args.addr).execute())
