#!/usr/bin/env python3.9
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
"""Lex Slots CloudWatch Custom Widgets"""

import json

import pandas as pd

_SLOT_NAME_SUFFIX = ".value.originalValue"


def render_slots_top_n_widget(event, input_df):
    """Render Slots Custom Widget"""
    # pylint: disable=too-many-locals
    slots_to_exclude = event.get("slotsToExclude", [])
    intents_to_exclude = event.get("intentToExclude", [])
    top_n = event.get("topN", 10)

    # deserialize json from message fields
    message_series = input_df["@message"].apply(json.loads)
    # flatten dictionaries up to one level and create a dataframe from it
    # only one level to selectively flatten other fields
    normalized_message_df = pd.DataFrame.from_records(
        pd.json_normalize(message_series, max_level=1)
    )
    if "sessionState.intent" not in normalized_message_df.columns:
        return "<pre>No intent data found</pre>"

    # extract sessionState.intent dictionaries and turn then into a dataframe
    intent_df = pd.DataFrame.from_records(normalized_message_df["sessionState.intent"])

    # extract slots and normalize
    if "slots" not in intent_df.columns:
        return "<pre>No slots data found</pre>"

    slots_df = pd.json_normalize(intent_df["slots"]).dropna(how="all")

    # join slots with intent to get a flattened dataframe with slots and intents
    slots_intent_df = slots_df.join(intent_df)
    if slots_intent_df.empty:
        return "<pre>No slot values found</pre>"

    # get intent names in data without exclusion
    slots_intent_columns = slots_intent_df.columns
    intent_names = [i for i in slots_intent_df["name"].unique() if i not in intents_to_exclude]

    # get slot names in data without exclusion
    slots_to_exclude_column_names = [f"{s}{_SLOT_NAME_SUFFIX}" for s in slots_to_exclude]
    slot_names = [
        c[: -len(_SLOT_NAME_SUFFIX)]
        for c in slots_intent_columns
        if c.endswith(_SLOT_NAME_SUFFIX) and c not in slots_to_exclude_column_names
    ]

    intent_slot_topn_values = []
    for intent_name in intent_names:
        for slot_name in slot_names:
            slot_column_name = f"{slot_name}{_SLOT_NAME_SUFFIX}"

            intent_slot_values_df = slots_intent_df[
                (slots_intent_df["name"] == intent_name)
                & slots_intent_df["state"].isin(["Fulfilled", "ReadyForFulfillment"])
            ][[slot_column_name]].rename({slot_column_name: "value"}, axis="columns")

            intent_slot_topn_values_df = (
                intent_slot_values_df.value_counts()
                .head(top_n)
                .to_frame()
                .rename({0: "count"}, axis="columns")
                .reset_index()
            )

            intent_slot_topn_values.append(
                {
                    "intent_name": intent_name,
                    "slot_name": slot_name,
                    "topn_df": intent_slot_topn_values_df,
                }
            )

    output = f"<h2>Top {top_n} Slot Values in Fulfilled Intents</h2>"
    for entry in intent_slot_topn_values:
        if not entry["topn_df"].empty:
            output = (
                output
                + f"<br><h3>Intent: {entry['intent_name']}"
                + f" Slot: {entry['slot_name']}</h3><br>"
            )
            output = output + entry["topn_df"].to_html(index=False)

    return output