# Copyright (c) 2020 CloudZero - ALL RIGHTS RESERVED - PROPRIETARY AND CONFIDENTIAL
# Unauthorized copying of this file and/or project, via any medium is strictly prohibited.
# Direct all questions to legal@cloudzero.com

import uuid
from decimal import Decimal
from typing import Callable, Mapping, Optional

import pyfaaster.aws.handlers_decorators_v2 as faaster
from aws_lambda_powertools.logging import Logger
from aws_lambda_powertools.tracing import Tracer
from aws_lambda_powertools.utilities.data_classes.api_gateway_proxy_event import \
    APIGatewayProxyEvent
from toolz.dicttoolz import get_in
from voluptuous import REMOVE_EXTRA, Any, Required, Schema

from czc import event_bridge
from czc import json
from czc.constants import PROJECT_NAME
from czc.date import get_current_timestamp
from czc.schema_validators import UUID

logger = Logger()
tracer = Tracer()


def create_event(schema: Schema, data_fragment: Mapping) -> Mapping:
    return schema({'data': data_fragment})


def create_schema(namespace: str, feature: str, event_name: str, data_schema: Mapping,
                  correlation_id: str = None, version: int = None,
                  timestamp: Decimal = None, event_id: str = None):
    if not correlation_id:
        correlation_id = str(uuid.uuid4())
    if not version:
        version = 1
    if not timestamp:
        timestamp = get_current_timestamp()
    if not event_id:
        event_id = str(uuid.uuid4())
    return Schema({
        Required('correlationId', default=correlation_id): UUID,
        Required('feature', default=feature): str,
        Required('namespace', default=namespace): str,
        Required('eventName', default=event_name): str,
        Required('data'): data_schema,
        Required('id', default=event_id): str,
        Required('timestamp', default=str(timestamp)): str,
        Required('version', default=version): int,
    })


@faaster.namespace_aware
def _publish_event(detail_type, detail, NAMESPACE='alfa'):
    entries = event_bridge.publish_event(NAMESPACE, detail_type, PROJECT_NAME, detail)

    return {
        'event': detail,
        'event_ids': [x['EventId'] for x in entries]
    }


def _default_id_fn_v1(event: Mapping) -> Optional[str]:
    return get_in(['pathParameters', 'organizationId'], event)


def _default_id_fn_v2(event: APIGatewayProxyEvent) -> Optional[str]:
    return get_in(['requestContext', 'authorizer', 'cz_organization_id'], event)


def _default_id_fn(event, _) -> Optional[str]:
    return _default_id_fn_v1(event) or _default_id_fn_v2(event)


SAFE_EVENT = Schema({
    'body': lambda x: list(json.loads(x).keys()) if isinstance(x, str) else [],
    'path': str,
    'pathParameters': Any(dict, None),
    'requestContext': {
        'authorizer': {
            'role': str,
            'user_id': str,
        }
    },
    'resource': str,
}, extra=REMOVE_EXTRA)


def _default_event_filter_fn(event: Mapping) -> Mapping:
    return SAFE_EVENT(event)


def _default_response_filter_fn(event: Mapping) -> Mapping:
    return {}


def publish_event_response(
        detail_type: str, event_name: str,
        id_fn: Callable[[Mapping, Mapping], Optional[str]] = _default_id_fn,
        event_filter_fn: Callable[[Mapping], Mapping] = _default_event_filter_fn,
        response_filter_fn: Callable[[Mapping], Mapping] = _default_response_filter_fn):

    def publish(event, response):
        event_data = event.raw_event if isinstance(event, APIGatewayProxyEvent) else event
        detail = {
            'cz_organization_id': id_fn(event_data, response),
            'data': {
                'event': event_filter_fn(event_data),
                'response': response_filter_fn(response),
            },
            'event_name': event_name,
        }
        return _publish_event(detail_type, detail)

    def event_producer_handler(handler):
        def handler_wrapper(event, context, *args, **kwargs):
            response = handler(event, context, *args, **kwargs)
            publish(event, response)
            return response
        return handler_wrapper
    return event_producer_handler
