#global imports
from db.entities.layer_adapter import LayerAdapter
from services.layer_adapter_service import LayerAdapterService

import json
from flask import Response, request


def use_cases():
    return [adapter.to_serializable_dict() for adapter in LayerAdapterService.all_use_cases()]

def delete_uses_cases():
    LayerAdapterService.delete_all_use_cases()
    return Response(status=200)

def all():
    return LayerAdapterService.all()

def get_all_for_use_case(use_case: str):
    return LayerAdapterService.all_for_use_case(use_case)

def add(name: str, use_case: str):
    '''
    add an empty layer to the DB

    @params:
        name        - Required : unique identifier for the layer
        use_case    - Required : String-identifier for the use-case
    '''
    if LayerAdapterService.one(name, use_case) != None:
        return Response(status=400, response=f"Layer with name '{name}' already exists!")

    LayerAdapterService.add(name, use_case)
    return Response(status=200)

def add_complete():
    '''
    add a layer already containing attribute mappings to the BD.
    '''
    data = request.json

    if "name" not in data or "properties" not in data or "cluster_properties" not in data or "use_case" not in data:
        return Response(status=400, response=f"Field missing! Fields required: (name, properties, cluster_properties)")

    layer = LayerAdapterService.one(data["name"], data["use_case"])

    if layer != None:
        return Response(status=400, response=f'Layer with name "{data["name"]}" already exists!')

    try:
        layer_new = LayerAdapter.from_serializable_dict(data)
        LayerAdapterService.add_complete(layer_new)
        return Response(status=200)
    except BaseException as e:
        print(f"Exception: {e}")
        return Response(status=400)


def one(name: str, use_case: str):
    '''
    fetch a single layer from the DB

    @params:
        name        - Required : unique identifier for the layer
        use_case    - Required : String-identifier for the use-case
    '''
    layer = LayerAdapterService.one(name, use_case)

    if layer == None:
        return Response(status=404, response=f"Layer with name '{name}' does not exist!")

    return Response(status=200, response=json.dumps(layer.to_serializable_dict()))

def add_mapping_for_all(use_case: str):
    '''
    add a new mapping to each layer of the use-case

    @params:
        use_case    - Required : String-identifier for the use-case
    '''
    data = request.json

    if "internal" not in data or "external" not in data:
        return Response(status=400, response=f"Field missing! Fields required: (internal, external)")
    
    layers = LayerAdapterService.all_for_use_case(use_case)
    layers = [LayerAdapter.from_serializable_dict(d) for d in layers]

    for layer in layers:
        layer.add_mapping(data["internal"], data["external"])
        LayerAdapterService.update(layer)

    return Response(status=200)

def add_mapping(name: str, use_case: str):
    '''
    add a new mapping to the layer identified by name

    @params:
        name        - Required : unique identifier for the layer
        use_case    - Required : String-identifier for the use-case
    '''
    layer = LayerAdapterService.one(name, use_case)

    if layer == None:
        return Response(status=404, response=f"Layer with name '{name}' does not exist!")

    data = request.json

    if "internal" not in data or "external" not in data:
        return Response(status=400, response=f"Field missing! Fields required: (internal, external)")

    layer.add_mapping(data["internal"], data["external"])
    LayerAdapterService.update(layer)

    return Response(status=200)

def update_use_case_for_all(use_case: str):
    '''
    update the use-case for each layer in the use-case
    this changes the use-case of all layers, meaning the use-case used
    for this request might not be usable after the request

    @params:
        use_case    - Required : String-identifier for the use-case
    '''
    data = request.json

    if "use_case" not in data:
        return Response(status=400, response=f"Field missing! Fields required: (use_case)")
    
    layers = LayerAdapterService.all_for_use_case(use_case)
    layers = [LayerAdapter.from_serializable_dict(d) for d in layers]

    for layer in layers:
        layer.use_case = data["use_case"]
        LayerAdapterService.update_use_case(layer, use_case)

    return Response(status=200)

def delete_mapping_for_all(use_case: str):
    '''
    delete a mapping from each layer in the use-case

    @params:
        use_case    - Required : String-identifier for the use-case
    '''
    data = request.json

    if "internal" not in data:
        return Response(status=400, response=f"Field missing! Fields required: (internal)")
    
    layers = LayerAdapterService.all_for_use_case(use_case)
    layers = [LayerAdapter.from_serializable_dict(d) for d in layers]

    for layer in layers:
        try:
            layer.delete_mapping(data["internal"])
        except ValueError:
            continue
        LayerAdapterService.update(layer)

    return Response(status=200)

def delete_mapping(name: str, use_case: str):
    '''
    delete a mapping from the layer identified by name

    @params:
        name        - Required : unique identifier for the layer
        use_case    - Required : String-identifier for the use-case
    '''
    layer = LayerAdapterService.one(name, use_case)

    if layer == None:
        return Response(status=404, response=f"Layer with name '{name}' does not exist!")

    data = request.json

    if "internal" not in data:
        return Response(status=400, response=f"Field missing! Fields required: (internal)")

    try:
        layer.delete_mapping(data["internal"])
        LayerAdapterService.update(layer)
    except ValueError:
        return Response(status=400, response=f'{data["internal"]} is not a property of the layer!')

    return Response(status=200)


def add_cluster_mapping(name: str, use_case: str):
    '''
    add a mapped property to the list of properties to cluster with

    @params:
        name        - Required : unique identifier for the layer
        use_case    - Required : String-identifier for the use-case
    '''
    layer = LayerAdapterService.one(name, use_case)

    if layer == None:
        return Response(status=404, response=f"Layer with name '{name}' does not exist!")

    data = request.json

    if "attribute" not in data:
        return Response(status=400, response=f"Field missing! Fields required: (attribute)")

    try:
        layer.add_cluster_mapping(data["attribute"])
        LayerAdapterService.update(layer)
        return Response(status=200)
    except:
        return Response(status=400, response=f'{data["attribute"]} is no attribute in the layer!')


def delete_cluster_mapping(name: str, use_case: str):
    '''
    remove a mapped property from the list of properties to cluster with

    @params:
        name        - Required : unique identifier for the layer
        use_case    - Required : String-identifier for the use-case
    '''
    layer = LayerAdapterService.one(name, use_case)

    if layer == None:
        return Response(status=404, response=f"Layer with name '{name}' does not exist!")

    data = request.json

    if "attribute" not in data:
        return Response(status=400, response=f"Field missing! Fields required: (attribute)")

    try:
        layer.delete_cluster_mapping(data["attribute"])
        LayerAdapterService.update(layer)
        return Response(status=200)
    except ValueError as e:
        print(e)
        return Response(status=400, response=f'{data["attribute"]} is no attribute in the layer!')


def delete_one(name: str, use_case: str):
    '''
    delete a layer and all its mappings from the Db

    @params:
        name        - Required : unique identifier for the layer
        use_case    - Required : String-identifier for the use-case
    '''
    layer = LayerAdapterService.one(name, use_case)

    if layer == None:
        return Response(status=404, response=f"Layer with name '{name}' does not exist!")

    LayerAdapterService.delete(layer)
    return Response(status=200)
