import json
from typing import (
    Any,
    Dict,
)

import boto3
from boto3_lambda_invoker import Boto3LambdaInvoker
from lambda_invoker import LambdaInvoker
from .dto import SyncContextRequest
from .dto.exception import JamesClientError


class JamesClient(object):
    def __init__(
        self,
        aws_access_key_id: str = None,
        aws_secret_access_key: str = None,
        region_name: str = None,
        base_name: str = None,
        lambda_invoker: LambdaInvoker = None,
    ):
        if lambda_invoker is not None:
            self.lambda_invoker = lambda_invoker
            self.generate_context_invoker = lambda_invoker
        else:
            if base_name is None:
                raise RuntimeError("base_name is necessary to create JamesClient without an external lambda invoker")
            self.lambda_invoker = Boto3LambdaInvoker(
                lambda_client=boto3.client(
                    "lambda",
                    aws_access_key_id=aws_access_key_id,
                    aws_secret_access_key=aws_secret_access_key,
                    region_name=region_name,
                ),
                function_name=f"{base_name}-james"
            )

            self.generate_context_invoker = Boto3LambdaInvoker(
                lambda_client=boto3.client(
                    "lambda",
                    aws_access_key_id=aws_access_key_id,
                    aws_secret_access_key=aws_secret_access_key,
                    region_name=region_name,
                ),
                function_name=f"{base_name}-james-generate-context"
            )

    def sync_context(self, request: SyncContextRequest):
        event = {
            "command": "sync-context",
            "data": request.to_dict()
        }

        response = self.lambda_invoker.invoke(event=event)

        parts = response.split(b"\0")
        return_code = int(parts[0])
        if return_code != 0:
            raise JamesClientError(
                error_code=return_code,
                error_message=parts[1].decode(),
            )

    def generate_context(self, service_key: str) -> Dict[str, Any]:
        event = {
            "command": "generate-context",
            "data": {
                "serviceKey": service_key,
            }
        }

        response = self.generate_context_invoker.invoke(event=event)
        parts = response.split(b"\0")
        return_code = int(parts[0])
        if return_code != 0:
            raise JamesClientError(
                error_code=return_code,
                error_message=parts[1].decode(),
            )

        return json.loads(parts[1].decode())

    def sync_environment(
        self,
        env_config_dict: Dict[str, Any],
        outputs: Dict[str, str],
    ):
        event = {
            "command": "sync-environment",
            "data": {
                "envConfig": env_config_dict,
                "outputs": outputs,
            },
        }

        response = self.lambda_invoker.invoke(event=event)
        parts = response.split(b"\0")
        return_code = int(parts[0])
        if return_code == 0:
            return

        raise JamesClientError(
            error_code=return_code,
            error_message=parts[1].decode(),
        )
