from pprint import pprint

import inflection
from troposphere import apigateway, cloudformation
import troposphere as tp

from deploy.utils import camelize_path

_ROOT_ENDPOINT_NAME = "$root"


def generate_root_endpoints_template(conio_env_param: tp.Parameter, first_level_endpoints, vpc_endpoint, private=False):
    root_endpoint_template = tp.Template()
    root_endpoint_template.add_parameter(conio_env_param)

    kwargs = {
        "Name": tp.Sub('conio-sdk-${ConioEnv}'),
        "MinimumCompressionSize": 0
    }

    if private:
        kwargs.update(
            {
                "EndpointConfiguration": apigateway.EndpointConfiguration(
                    Types=["PRIVATE"]
                ),
                "Policy": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Deny",
                            "Principal": "*",
                            "Action": [
                                "execute-api:Invoke"
                                ],
                            "Resource": tp.Sub("execute-api:/${ConioEnv}/*/*"),
                            "Condition": {
                                "StringNotEquals": {
                                    "aws:sourceVpce": vpc_endpoint
                                }
                            }
                        },
                        {
                            "Effect": "Allow",
                            "Principal": "*",
                            "Action": [
                                "execute-api:*"
                            ],
                            "Resource": tp.Sub("execute-api:/${ConioEnv}/*/*")
                        }
                    ],
                },
            }
        )
    else:
        kwargs.update(
            {
                "EndpointConfiguration": apigateway.EndpointConfiguration(
                    Types=["REGIONAL"]
                )
            }
        )

    root_endpoint = apigateway.RestApi(
        'ConioSDKAPI',
        **kwargs
    )
    root_endpoint_template.add_resource(root_endpoint)
    root_endpoint_ref_output = tp.Output(
        "RootEndpointId",
        Value=tp.Ref(root_endpoint)
    )
    root_endpoint_template.add_output(root_endpoint_ref_output)

    root_endpoint_output = tp.Output(
        "RootEndpointResourceId",
        Value=tp.GetAtt(root_endpoint, 'RootResourceId')
    )
    root_endpoint_template.add_output(root_endpoint_output)

    generated_paths = {}

    for name, data in first_level_endpoints.items():
        _rec_generate_endpoints(
            root_endpoint_template,
            data['path'],
            root_endpoint,
            generated_paths
        )

    return root_endpoint_template.to_yaml()

def _rec_generate_endpoints(root_template, path: str, root_endpoint, generated_paths) -> str:
    if path == '/':
        return tp.GetAtt(root_endpoint, 'RootResourceId')
    if path in generated_paths.keys():
        return generated_paths[path]
    if '/' in path:
        split_path = path.split('/')[:-1]
        parent_path = '/' + '/'.join(split_path) if len(split_path) == 1 else '/'.join(split_path)
        parent = _rec_generate_endpoints(root_template, parent_path, root_endpoint, generated_paths)
        name = camelize_path(path) + 'Resource'
        tmp = apigateway.Resource(
            name,
            RestApiId=tp.Ref(root_endpoint),
            ParentId=parent,
            PathPart=path.split('/')[-1]
        )
        root_template.add_resource(tmp)
        root_template.add_output(
            tp.Output(
                name + "Ref",
                Value=tp.Ref(tmp)
            )
        )
        self_id = tp.Ref(tmp)
        generated_paths[path] = self_id
        return self_id


def _generate_endpoints_list(functions):
    endpoints = {
        _ROOT_ENDPOINT_NAME: {
            'name': 'RootResourceId'
        }
    }
    for fun in functions:
        path = fun['path']
        path_items = path.strip('/').split('/')
        parent_path_name = ''
        path_name = ''
        for p in path_items:
            path_name = path_name + inflection.camelize(p.replace('{', '').replace('}', ''), uppercase_first_letter=True)
            if path_name not in endpoints:
                endpoints[path_name] = {
                    'path_part': p,
                    'parent': parent_path_name + 'Resource' if parent_path_name else '',
                    'name': path_name + 'Resource'
                }
            parent_path_name = path_name
        fun['path_name'] = path_name + 'Resource'
    # check for consistency
    names = [e['name'] for _, e in endpoints.items()]
    assert(len(names) == len(set(names)))
    return endpoints


def generate_endpoint_template(endpoints, conio_env_param, first_level_endpoint_resources):
    endpoint_template = tp.Template()
    endpoint_template.add_parameter(conio_env_param)

    root_endpoint_id = tp.Parameter(
        "RootEndpointId",
        Description="Root Endpoint Id",
        Type="String"
    )
    endpoint_template.add_parameter(root_endpoint_id)

    root_endpoint_resource_id = tp.Parameter(
        "RootEndpointResourceId",
        Description="Root Endpoint Id",
        Type="String"
    )
    endpoint_template.add_parameter(root_endpoint_resource_id)

    endpoint_refs = {}
    for k, v in first_level_endpoint_resources.items():
        tmp = tp.Parameter(
            k+"Ref",
            Description=k+" Endpoint Id",
            Type="String"
        )
        endpoint_template.add_parameter(tmp)
        endpoint_refs[k+"Ref"] = tmp


    # pprint(endpoints)
    for e in _generate_endpoints(endpoints, root_endpoint_id, root_endpoint_resource_id, endpoint_refs, first_level_endpoint_resources):
        endpoint_template.add_resource(e)
        endpoint_template.add_output(
            tp.Output(
                e.title,
                Value=tp.Ref(e)
            )
        )

    return endpoint_template.to_yaml()


def _generate_endpoints(endpoints, root_endpoint_id, root_endpoint_resource_id, endpoint_refs, first_level_endpoint_resources):
    prev_endpoint = None
    for k, v in endpoints.items():
        if k == _ROOT_ENDPOINT_NAME or v['name'] in first_level_endpoint_resources.keys():
            continue
        parent_ref = None
        if v['parent']:
            if v['parent'] in first_level_endpoint_resources.keys():
                parent_ref = tp.Ref(endpoint_refs[v['parent']+"Ref"])
            else:
                parent_ref = tp.Ref(v['parent'])
        else:
            parent_ref = tp.Ref(root_endpoint_resource_id)
        kwargs = {
            'RestApiId': tp.Ref(root_endpoint_id),
            'ParentId': parent_ref,
            'PathPart': v['path_part']
        }
        if prev_endpoint:
            kwargs['DependsOn'] = prev_endpoint
        endpoint = apigateway.Resource(
            v['name'],
            **kwargs
        )
        prev_endpoint = endpoint
        yield endpoint