# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import grpc
from flask import current_app, redirect, render_template, Blueprint, flash, session, url_for
from common.proto.context_pb2 import (
    IsolationLevelEnum, Service, ServiceId, ServiceTypeEnum, ServiceStatusEnum, Connection)
from common.tools.context_queries.Context import get_context
from common.tools.context_queries.EndPoint import get_endpoint_names
from common.tools.context_queries.Service import get_service
from context.client.ContextClient import ContextClient
from service.client.ServiceClient import ServiceClient

service = Blueprint('service', __name__, url_prefix='/service')

context_client = ContextClient()
service_client = ServiceClient()

@service.get('/')
def home():
    if 'context_uuid' not in session or 'topology_uuid' not in session:
        flash("Please select a context!", "warning")
        return redirect(url_for("main.home"))
    context_uuid = session['context_uuid']

    context_client.connect()

    context_obj = get_context(context_client, context_uuid, rw_copy=False)
    if context_obj is None:
        flash('Context({:s}) not found'.format(str(context_uuid)), 'danger')
        services, device_names, endpoints_data = list(), list(), list()
    else:
        try:
            services = context_client.ListServices(context_obj.context_id)
            services = services.services
        except grpc.RpcError as e:
            if e.code() != grpc.StatusCode.NOT_FOUND: raise
            if e.details() != 'Context({:s}) not found'.format(context_uuid): raise
            services, device_names, endpoints_data = list(), dict(), dict()
        else:
            endpoint_ids = list()
            for service_ in services:
                endpoint_ids.extend(service_.service_endpoint_ids)
            device_names, endpoints_data = get_endpoint_names(context_client, endpoint_ids)

    context_client.close()
    return render_template(
        'service/home.html', services=services, device_names=device_names, endpoints_data=endpoints_data,
        ste=ServiceTypeEnum, sse=ServiceStatusEnum)


@service.route('add', methods=['GET', 'POST'])
def add():
    flash('Add service route called', 'danger')
    raise NotImplementedError()
    #return render_template('service/home.html')


@service.get('<path:service_uuid>/detail')
def detail(service_uuid: str):
    if 'context_uuid' not in session or 'topology_uuid' not in session:
        flash("Please select a context!", "warning")
        return redirect(url_for("main.home"))
    context_uuid = session['context_uuid']

    try:
        context_client.connect()

        endpoint_ids = list()
        service_obj = get_service(context_client, service_uuid, rw_copy=False)
        if service_obj is None:
            flash('Context({:s})/Service({:s}) not found'.format(str(context_uuid), str(service_uuid)), 'danger')
            service_obj = Service()
        else:
            endpoint_ids.extend(service_obj.service_endpoint_ids)
            connections: Connection = context_client.ListConnections(service_obj.service_id)
            connections = connections.connections
            for connection in connections: endpoint_ids.extend(connection.path_hops_endpoint_ids)

        if len(endpoint_ids) > 0:
            device_names, endpoints_data = get_endpoint_names(context_client, endpoint_ids)
        else:
            device_names, endpoints_data = dict(), dict()

        context_client.close()

        return render_template(
            'service/detail.html', service=service_obj, connections=connections, device_names=device_names,
            endpoints_data=endpoints_data, ste=ServiceTypeEnum, sse=ServiceStatusEnum, ile=IsolationLevelEnum)
    except Exception as e:
        flash('The system encountered an error and cannot show the details of this service.', 'warning')
        current_app.logger.exception(e)
        return redirect(url_for('service.home'))


@service.get('<path:service_uuid>/delete')
def delete(service_uuid: str):
    if 'context_uuid' not in session or 'topology_uuid' not in session:
        flash("Please select a context!", "warning")
        return redirect(url_for("main.home"))
    context_uuid = session['context_uuid']

    try:
        request = ServiceId()
        request.service_uuid.uuid = service_uuid
        request.context_id.context_uuid.uuid = context_uuid
        service_client.connect()
        service_client.DeleteService(request)
        service_client.close()

        flash('Service "{:s}" deleted successfully!'.format(service_uuid), 'success')
    except Exception as e:
        flash('Problem deleting service "{:s}": {:s}'.format(service_uuid, str(e.details())), 'danger')
        current_app.logger.exception(e)
    return redirect(url_for('service.home'))
