"""
CLI interface for AWS Lambda Builder. It is a very thin wrapper over the library. It is meant to integrate
with tools written in other programming languages that can't import Python libraries directly. The CLI provides
a JSON-RPC interface over stdin/stdout to invoke the builder and get response.

Read the design document for explanation of the JSON-RPC interface
"""

import json
import logging
import os
import re
import sys

from aws_lambda_builders import RPC_PROTOCOL_VERSION as lambda_builders_protocol_version
from aws_lambda_builders.architecture import X86_64
from aws_lambda_builders.builder import LambdaBuilder
from aws_lambda_builders.exceptions import WorkflowFailedError, WorkflowNotFoundError, WorkflowUnknownError

log_level = int(os.environ.get("LAMBDA_BUILDERS_LOG_LEVEL", logging.INFO))

# Write output to stderr because stdout is used for command response
logging.basicConfig(stream=sys.stderr, level=log_level, format="%(message)s")

LOG = logging.getLogger(__name__)

VERSION_REGEX = re.compile("^([0-9])+.([0-9]+)$")


def _success_response(request_id, artifacts_dir):
    return json.dumps({"jsonrpc": "2.0", "id": request_id, "result": {"artifacts_dir": artifacts_dir}})


def _error_response(request_id, http_status_code, message):
    return json.dumps({"jsonrpc": "2.0", "id": request_id, "error": {"code": http_status_code, "message": message}})


def _parse_version(version_string):
    if VERSION_REGEX.match(version_string):
        return float(version_string)
    else:
        ex = "Protocol Version does not match : {}".format(VERSION_REGEX.pattern)
        LOG.debug(ex)
        raise ValueError(ex)


def version_compatibility_check(version):
    # The following check is between current protocol version vs version of the protocol
    # with which aws-lambda-builders is called.
    # Example:
    # 0.2 < 0.2 comparison will fail, don't throw a value Error saying incompatible version.
    # 0.2 < 0.3 comparison will pass, throwing a ValueError
    # 0.2 < 0.1 comparison will fail, don't throw a value Error saying incompatible version

    if _parse_version(lambda_builders_protocol_version) < version:
        ex = "Incompatible Protocol Version : {}, " "Current Protocol Version: {}".format(
            version, lambda_builders_protocol_version
        )
        LOG.error(ex)
        raise ValueError(ex)


def _write_response(response, exit_code):
    sys.stdout.write(response)
    sys.stdout.flush()  # Make sure it is written
    sys.exit(exit_code)


def main():  # pylint: disable=too-many-statements
    """
    Implementation of CLI Interface. Handles only one JSON-RPC method at a time and responds with data

    Input is passed as JSON string either through stdin or as the first argument to the command. Output is always
    printed to stdout.
    """

    # For now the request is not validated
    if len(sys.argv) > 1:
        request_str = sys.argv[1]
        LOG.debug("Using the request object from command line argument")
    else:
        LOG.debug("Reading the request object from stdin")
        request_str = sys.stdin.read()

    request = json.loads(request_str)
    request_id = request["id"]
    params = request["params"]

    # Currently, this is the only supported method
    if request["method"] != "LambdaBuilder.build":
        response = _error_response(request_id, -32601, "Method unavailable")
        return _write_response(response, 1)

    try:
        protocol_version = _parse_version(params.get("__protocol_version"))
        version_compatibility_check(protocol_version)

    except ValueError:
        response = _error_response(request_id, 505, "Unsupported Protocol Version")
        return _write_response(response, 1)

    capabilities = params["capability"]
    supported_workflows = params.get("supported_workflows")

    exit_code = 0
    response = None

    try:
        builder = LambdaBuilder(
            language=capabilities["language"],
            dependency_manager=capabilities["dependency_manager"],
            application_framework=capabilities["application_framework"],
            supported_workflows=supported_workflows,
        )

        artifacts_dir = params["artifacts_dir"]
        builder.build(
            params["source_dir"],
            params["artifacts_dir"],
            params["scratch_dir"],
            params["manifest_path"],
            executable_search_paths=params.get("executable_search_paths", None),
            runtime=params["runtime"],
            optimizations=params["optimizations"],
            options=params["options"],
            mode=params.get("mode", None),
            download_dependencies=params.get("download_dependencies", True),
            dependencies_dir=params.get("dependencies_dir", None),
            combine_dependencies=params.get("combine_dependencies", True),
            architecture=params.get("architecture", X86_64),
            is_building_layer=params.get("is_building_layer", False),
            experimental_flags=params.get("experimental_flags", []),
            build_in_source=params.get("build_in_source", None),
        )

        # Return a success response
        response = _success_response(request_id, artifacts_dir)

    except (WorkflowNotFoundError, WorkflowUnknownError, WorkflowFailedError) as ex:
        LOG.debug("Builder workflow failed", exc_info=ex)
        exit_code = 1
        response = _error_response(request_id, 400, str(ex))

    except Exception as ex:
        LOG.debug("Builder crashed", exc_info=ex)
        exit_code = 1
        response = _error_response(request_id, 500, str(ex))

    _write_response(response, exit_code)


if __name__ == "__main__":
    main()
