#!/usr/bin/env python3
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
#
# February 22 2022, Christian Hopps <chopps@labn.net>
#
# Copyright (c) 2022, LabN Consulting, L.L.C.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import argparse
import logging
import os
import sys

import pytest

CWD = os.path.dirname(os.path.realpath(__file__))

# This is painful but works if you have installed grpc and grpc_tools would be *way*
# better if we actually built and installed these but ... python packaging.
try:
    import grpc
    import grpc_tools

    from micronet import commander

    commander.cmd_raises(f"cp {CWD}/../../../grpc/frr-northbound.proto .")
    commander.cmd_raises(
        f"python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I . frr-northbound.proto"
    )
except Exception as error:
    logging.error("can't create proto definition modules %s", error)
    raise

try:
    sys.path[0:0] = "."
    import frr_northbound_pb2
    import frr_northbound_pb2_grpc

    # Would be nice if compiling the modules internally from the source worked
    # # import grpc_tools.protoc
    # # proto_include = pkg_resources.resource_filename("grpc_tools", "_proto")
    # from grpc_tools.protoc import _proto_file_to_module_name, _protos_and_services
    # try:
    #     frr_northbound_pb2, frr_northbound_pb2_grpc = _protos_and_services(
    #         "frr_northbound.proto"
    #     )
    # finally:
    #     os.chdir(CWD)
except Exception as error:
    logging.error("can't import proto definition modules %s", error)
    raise


class GRPCClient:
    def __init__(self, server, port):
        self.channel = grpc.insecure_channel("{}:{}".format(server, port))
        self.stub = frr_northbound_pb2_grpc.NorthboundStub(self.channel)

    def get_capabilities(self):
        request = frr_northbound_pb2.GetCapabilitiesRequest()
        response = "NONE"
        try:
            response = self.stub.GetCapabilities(request)
        except Exception as error:
            logging.error("Got exception from stub: %s", error)

        logging.debug("GRPC Capabilities: %s", response)
        return response

    def get(self, xpath):
        request = frr_northbound_pb2.GetRequest()
        request.path.append(xpath)
        request.type = frr_northbound_pb2.GetRequest.ALL
        request.encoding = frr_northbound_pb2.XML
        xml = ""
        for r in self.stub.Get(request):
            logging.info('GRPC Get path: "%s" value: %s', request.path, r)
            xml += str(r.data.data)
        return xml


def next_action(action_list=None):
    "Get next action from list or STDIN"
    if action_list:
        for action in action_list:
            yield action
    else:
        while True:
            try:
                action = input("")
                if not action:
                    break
                yield action.strip()
            except EOFError:
                break


def main(*args):
    parser = argparse.ArgumentParser(description="gRPC Client")
    parser.add_argument(
        "-s", "--server", default="localhost", help="gRPC Server Address"
    )
    parser.add_argument(
        "-p", "--port", type=int, default=50051, help="gRPC Server TCP Port"
    )
    parser.add_argument("-v", "--verbose", action="store_true", help="be verbose")
    parser.add_argument("--check", action="store_true", help="check runable")
    parser.add_argument("actions", nargs="*", help="GETCAP|GET,xpath")
    args = parser.parse_args(*args)

    level = logging.DEBUG if args.verbose else logging.INFO
    logging.basicConfig(
        level=level,
        format="%(asctime)s %(levelname)s: GRPC-CLI-CLIENT: %(name)s %(message)s",
    )

    if args.check:
        sys.exit(0)

    c = GRPCClient(args.server, args.port)

    for action in next_action(args.actions):
        action = action.casefold()
        logging.info("GOT ACTION: %s", action)
        if action == "getcap":
            caps = c.get_capabilities()
            print("Capabilities:", caps)
        elif action.startswith("get,"):
            # Print Interface State and Config
            _, xpath = action.split(",", 1)
            print("Get XPath: ", xpath)
            xml = c.get(xpath)
            print("{}: {}".format(xpath, xml))
            # for _ in range(0, 1):


if __name__ == "__main__":
    main()
