import boto3
import jwt
import json
import os
import logging
import requests
import random
# from shared.helper_functions import get_tenant_context, get_boto3_client
from flask import Flask, request
from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.core import patch_all
from aws_xray_sdk.ext.flask.middleware import XRayMiddleware
patch_all()
app = Flask(__name__)
app.logger.setLevel(logging.DEBUG)
table_name = os.environ["TABLE_NAME"]
fulfillment_endpoint = os.environ["FULFILLMENT_ENDPOINT"]
xray_service_name = os.environ["AWS_XRAY_SERVICE_NAME"] + \
    "-" + os.environ["POD_NAMESPACE"]
xray_recorder.configure(service=xray_service_name)
XRayMiddleware(app, xray_recorder)


# LAB 3: REMOVE START (cleanup)
class TenantContext:
    tenant_id: str = None
    tenant_tier: str = None

    def __init__(self, jwt):
        self.tenant_id = jwt.get('custom:tenant_id', None)
        self.tenant_tier = jwt.get('custom:tenant_tier', None)


def get_tenant_context(authorization):
    token = authorization.replace("Bearer ", "")
    decoded_token = jwt.decode(token, options={"verify_signature": False})
    return TenantContext(decoded_token)


def get_boto3_client(service, authorization):
    token_vendor_endpoint = "127.0.0.1"
    token_vendor_endpoint_port = os.environ["TOKEN_VENDOR_ENDPOINT_PORT"]
    url = "http://" + token_vendor_endpoint + ":" + token_vendor_endpoint_port
    print("Token Vendor URL: " + url)
    response = requests.get(
        url,
        headers={
            "Authorization": authorization
        }
    ).json()

    access_key = response["Credentials"]["AccessKeyId"]
    secret_key = response["Credentials"]["SecretAccessKey"]
    session_token = response["Credentials"]["SessionToken"]

    return boto3.client(
        service,
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key,
        aws_session_token=session_token
    )
# LAB 3: REMOVE END (cleanup)


class Order():
    order_id: str
    name: str
    tenant_id: str
    description: str = None
    products: list

    def __init__(self, order_json):
        self.order_id = "ord-" + str(random.randint(10000, 99999))
        self.name = order_json['name']
        self.description = order_json.get('description', '')
        self.products = order_json['products']


@app.route("/orders/health")
def health():
    return {"message": "Status is Ok!"}


@app.route("/orders")
def getAllOrder():
    authorization = request.headers.get("Authorization", None)
    tenantContext = get_tenant_context(authorization)
    if tenantContext.tenant_id is None:
        return {"msg": "Unable to read 'tenantId' claim from JWT."}, 400

    try:
        dynamodb_client = get_boto3_client("dynamodb", authorization)
        resp = dynamodb_client.query(
            TableName=table_name,
            KeyConditionExpression='tenantId = :t_id',
            ExpressionAttributeValues={
                ':t_id': {'S': tenantContext.tenant_id}
            }
        )

        items = resp['Items']
        list = []

        for item in items:
            list.append({
                'order_id': item['orderId']['S'],
                'name': item['name']['S'],
                'description': item['description']['S'],
                'products': item['products']['SS']
            })

        return {"msg": "GET successful! ", "orders": list}, 200

    except Exception as e:
        app.logger.error("Exception raised! " + str(e))
        return {"msg": "Unable to get all orders!"}, 500


@app.route("/orders/<order_id>")
def getOrder(order_id):
    try:
        authorization = request.headers.get("Authorization", None)
        tenantContext = get_tenant_context(authorization)
        if tenantContext.tenant_id is None:
            return {"msg": "Unable to read 'tenantId' claim from JWT."}, 400

        dynamodb_client = get_boto3_client("dynamodb", authorization)
        resp = dynamodb_client.query(
            TableName=table_name,
            KeyConditionExpression='tenantId=:t_id AND order_id=:o_id',
            ExpressionAttributeValues={
                ':t_id': {'S': tenantContext.tenant_id},
                ':o_id': {'S': order_id}
            }
        )

        if len(resp['Items']) < 1:
            return {"msg": "Order not found!", "order_id": order_id}, 404

        order_dict = {
            'order_id': order_id,
            'name': resp['Items'][0]['name']['S'],
            'description': resp['Items'][0]['description']['S'],
            'products': resp['Items'][0]['products']['SS']
        }

        return {"msg": "GET successful! ", "order_id": order_dict}, 200

    except Exception as e:
        app.logger.error("Exception raised! " + str(e))
        return {"msg": "Unable to get order!", "order_id": order_id}, 500


@app.route("/orders", methods=['POST'])
def postOrder():
    try:
        authorization = request.headers.get("Authorization", None)
        tenantContext = get_tenant_context(authorization)
        if tenantContext.tenant_id is None:
            return {"message": "Unable to read 'tenant_id' claim from JWT."}, 400

        order = Order(request.get_json())
        dynamodb_client = get_boto3_client("dynamodb", authorization)
        dynamodb_client.put_item(
            Item={
                'tenantId': {
                    'S': tenantContext.tenant_id,
                },
                'orderId': {
                    'S': order.order_id,
                },
                'name': {
                    'S': order.name,
                },
                'description': {
                    'S': order.description,
                },
                'products': {
                    'SS': order.products,
                },
            },
            TableName=table_name,
        )
        submitFulfillment(order, authorization,
                          tenantContext, fulfillment_endpoint)

        # REPLACE BELOW: LAB5 (log)
        app.logger.debug("Order created: " + str(order.order_id) +
                         ", tenant:" + str(tenantContext.tenant_id))

        return {"msg": "Order created", "order": order.__dict__}, 200
    except Exception as e:
        app.logger.error("Exception raised! " + str(e))
        return {"msg": "Unable to save order!", "order": order.__dict__}, 500


def submitFulfillment(order, authorization, tenantContext, fulfillment_endpoint):
    try:
        url = "http://" + fulfillment_endpoint + "/fulfillments/" + order.order_id
        app.logger.debug("Fulfillment request: " + url)
        response = requests.post(
            url=url,
            json=order.__dict__,
            headers={
                "Authorization": authorization,
                # PASTE LINES BELOW: LAB4 (routing)
            },
        )
        response.raise_for_status()
        return None
    except Exception as e:
        app.logger.error("Exception raised! " + str(e))
        return None