import os
import sys

import matplotlib.pyplot as plt
from matplotlib.ticker import FormatStrFormatter

inputDir = str(sys.argv[1])

mode = "attack"
if len(sys.argv) > 2:
    mode = str(sys.argv[2])

numQuestions = 100
if len(sys.argv) > 3:
    numQuestions = int(sys.argv[3])

maxQueries = 4000
if len(sys.argv) > 4:
    maxQueries = int(sys.argv[4])

model = ""
if len(sys.argv) > 5:
    model = str(sys.argv[5])

outDir = os.path.join(inputDir, "plots")
if not os.path.exists(outDir):
    os.makedirs(outDir)
outPrefix = f"{outDir}/{mode}_"
# if model != "":
#     outPrefix += f"{model}_"


data = dict()
keys = []

data["time"] = dict()
data["queries"] = dict()
data["jailbreaks"] = dict()
data["questions"] = dict()
data["attacks"] = dict()
data["templates"] = dict()
data["successful"] = dict()
data["invalid"] = dict()
data["all_questions"] = dict()
data["safe_questions"] = dict()
data["vulnerable_questions"] = dict()

markers = ['x', 'o', '+', '^', 's', 'd']
prop_cycle = plt.rcParams['axes.prop_cycle']
colors = prop_cycle.by_key()['color']

warmup = dict()
warmup["time"] = dict()
warmup["queries"] = dict()
warmup["jailbreaks"] = dict()
warmup["questions"] = dict()
warmup["attacks"] = dict()
warmup["templates"] = dict()
warmup["successful"] = dict()
warmup["invalid"] = dict()


def read_input(key, filename):
    global data, keys, warmup, maxQueries

    data["time"][key] = [0]
    data["queries"][key] = [0]
    data["jailbreaks"][key] = [0]
    data["questions"][key] = [0]
    data["attacks"][key] = [0]
    data["templates"][key] = [0]
    data["successful"][key] = [0]
    data["invalid"][key] = [0]

    data["all_questions"][key] = dict()
    data["safe_questions"][key] = dict()
    data["vulnerable_questions"][key] = dict()

    warmup["time"][key] = 0
    warmup["queries"][key] = 0
    warmup["jailbreaks"][key] = 0
    warmup["questions"][key] = 0
    warmup["attacks"][key] = 0
    warmup["templates"][key] = 0
    warmup["successful"][key] = 0
    warmup["invalid"][key] = 0

    f = open(filename, 'r')
    lines = f.readlines()
    i = 0
    while i < len(lines):
        line = lines[i].strip()
        i += 1
        if "Summary after" in line:
            words = line.split()
            if words[3] != "Summary" and words[4] != "after":
                continue

            # for i, w in enumerate(words):
            #     print(f"{i}: {w}")
            # sys.exit(0)

            data["time"][key].append(int(words[5]))
            data["queries"][key].append(int(words[8]))
            data["jailbreaks"][key].append(int(words[11]))
            data["questions"][key].append(int(words[14]))
            data["attacks"][key].append(int(words[17]))
            data["templates"][key].append(int(words[20]))
            data["successful"][key].append(int(words[22].replace("(", "")))
            data["invalid"][key].append(int(words[24]))

            if int(words[8]) >= maxQueries:
                break

        elif line.endswith("SAFE") or line.endswith("VULNERABLE"):
            words = line.split()
            qid = words[3].replace("Q", "").replace(":", "")
            data["all_questions"][key][qid] = data["all_questions"][key].get(qid, 0) + 1
            if words[-1] == "SAFE":
                data["safe_questions"][key][qid] = data["safe_questions"][key].get(qid, 0) + 1
            elif words[-1] == "VULNERABLE":
                data["vulnerable_questions"][key][qid] = data["vulnerable_questions"][key].get(qid, 0) + 1
            else:
                raise
        elif "Warmup finished!" in line:
            warmup["time"][key] = data["time"][key][-1]
            warmup["queries"][key] = data["queries"][key][-1]
            warmup["jailbreaks"][key] = data["jailbreaks"][key][-1]
            warmup["questions"][key] = data["questions"][key][-1]
            warmup["attacks"][key] = data["attacks"][key][-1]
            warmup["templates"][key] = data["templates"][key][-1]
            warmup["successful"][key] = data["successful"][key][-1]
            warmup["invalid"][key] = data["invalid"][key][-1]

    if len(data["time"][key]) > 1:
        keys.append(key)


def read_all_inputs():
    for root, dirs, files in os.walk(inputDir):
        for file in files:
            if file.endswith("details.log"):
                filename = os.path.join(root, file)
                if f"{mode}_" in filename and (model == "" or model in filename):
                    key = filename
                    key = key.replace(inputDir, "")
                    key = key.replace("details.log", "")
                    key = key.replace("/", "")
                    key = key[:key.rindex('_')]
                    key = key.replace(f"{mode}_", "")
                    read_input(key, filename)


def get_marker(key):
    idx = keys.index(key)
    return markers[idx % len(markers)]


def get_color(key):
    idx = keys.index(key)
    return colors[idx % len(colors)]


def plot_scatter(ax, xkey, ykey):
    x = data[xkey]
    wx = warmup[xkey]
    if ykey == "seed-asr":
        y = {k: [(100 * v) / numQuestions for v in data["questions"][k]] for k in keys}
        wy = {k: (100 * warmup["questions"][k]) / numQuestions for k in keys}
    elif ykey == "aqj":
        y = {k: [float(data["queries"][k][i]) / data["jailbreaks"][k][i] if data["jailbreaks"][k][i] > 0 else 0 for i in
                 range(len(data["jailbreaks"][k]))] for k in keys}
        wy = {k: float(warmup["queries"][k]) / warmup["jailbreaks"][k] if warmup["jailbreaks"][k] > 0 else 0 for k in
              keys}
    else:
        y = data[ykey]
        wy = warmup[ykey]

    x_lim = 0
    y_lim = 0
    for k in keys:
        ax.plot(x[k], y[k], label=k, alpha=0.5, clip_on=False, marker=get_marker(k), color=get_color(k))
        x_lim = max(x_lim, max(x[k]))
        y_lim = max(y_lim, max(y[k]))
        if ykey == "aqj":
            ax.annotate('%d' % y[k][-1], xy=(x[k][-1], y[k][-1]), xytext=(8, 0), xycoords=('data', 'data'),
                        textcoords='offset points', color=get_color(k), ha='left', rotation=0)
            print(f"Average queries per jailbreak {k}: {y[k][-1]}")
        elif xkey == "templates" and ykey == "successful":
            print(f"Number of successful templates {k}: {y[k][-1]}")
            # print(f"%% successful templates {k}: {y[k][-1] / x[k][-1]}")
        else:
            if xkey == "queries" and ykey == "seed-asr":
                print(f"ASR {k}: {y[k][-1]}")
            ax.annotate('%d' % max(y[k]), xy=(max(x[k]), max(y[k])), xytext=(8, 0), xycoords=('data', 'data'),
                        textcoords='offset points', color=get_color(k), ha='left', rotation=0)
        ax.axvline(x=wx[k], ymin=0, ymax=wy[k], alpha=0.5, color=get_color(k), linestyle='dotted', linewidth=1.0)
        ax.axhline(y=wy[k], xmin=0, xmax=wx[k], alpha=0.5, color=get_color(k), linestyle='dotted', linewidth=1.0)

    ax.set_ylim(0, y_lim)
    ax.set_xlim(0, x_lim)
    ax.yaxis.set_major_formatter(FormatStrFormatter('%d'))

    # starting from here
    ymin, ymax = ax.get_ylim()
    ax.set_ylim(ymin, ymax)

    if x_lim != 0:
        plt.figtext(0.01, 1.001, "y-max: " + str(y_lim), ha="left")
    if y_lim != 0:
        plt.figtext(1.001, 0.01, "x-max: " + str(x_lim), ha="right")

    # Get handles and labels
    handles, labels = ax.get_legend_handles_labels()

    # Sort handles and labels based on the last y-value (you can adjust this)
    handles, labels = zip(
        *sorted(zip(handles, labels), key=lambda t: (t[0].get_ydata()[-1], -t[0].get_xdata()[-1]), reverse=True))

    # Create the legend with the sorted handles and labels
    ax.legend(handles, labels, loc='center left', bbox_to_anchor=(1.08, 0.5))

    ax.grid(True, alpha=0.25)


def queries_vs_time():
    fig, ax = plt.subplots()
    plot_scatter(ax, "time", "queries")
    ax.set(xlabel='Time (s)', ylabel='Number of Queries')
    ax.set_title('Queries vs Time', pad=20)
    fig.savefig(outPrefix + "queries_vs_time.png", bbox_inches="tight")


def attacks_vs_time():
    fig, ax = plt.subplots()
    plot_scatter(ax, "time", "attacks")
    ax.set(xlabel='Time (s)', ylabel='Number of Attacks')
    ax.set_title('Attacks vs Time', pad=20)
    fig.savefig(outPrefix + "attacks_vs_time.png", bbox_inches="tight")


def attacks_vs_queries():
    fig, ax = plt.subplots()
    plot_scatter(ax, "queries", "attacks")
    ax.set(xlabel='Number of Queries', ylabel='Number of Attacks')
    ax.set_title('Attacks vs Queries', pad=20)
    fig.savefig(outPrefix + "attacks_vs_queries.png", bbox_inches="tight")


def jailbreaks_vs_time():
    fig, ax = plt.subplots()
    plot_scatter(ax, "time", "jailbreaks")
    ax.set(xlabel='Time (s)', ylabel='Number of Jailbreaks')
    ax.set_title('Jailbreaks vs Time', pad=20)
    fig.savefig(outPrefix + "jailbreaks_vs_time.png", bbox_inches="tight")


def jailbreaks_vs_queries():
    fig, ax = plt.subplots()
    plot_scatter(ax, "queries", "jailbreaks")
    ax.set(xlabel='Number of Queries', ylabel='Number of Jailbreaks')
    ax.set_title('Jailbreaks vs Queries', pad=20)
    fig.savefig(outPrefix + "jailbreaks_vs_queries.png", bbox_inches="tight")


def jailbreaks_vs_attacks():
    fig, ax = plt.subplots()
    plot_scatter(ax, "attacks", "jailbreaks")
    ax.set(xlabel='Number of Attacks', ylabel='Number of Jailbreaks')
    ax.set_title('Jailbreaks vs Attacks', pad=20)
    fig.savefig(outPrefix + "jailbreaks_vs_attacks.png", bbox_inches="tight")


def seed_asr_vs_time():
    fig, ax = plt.subplots()
    plot_scatter(ax, "time", "seed-asr")
    ax.set(xlabel='Time (s)', ylabel='Seed ASR')
    ax.set_title('Seed ASR vs Time', pad=20)
    fig.savefig(outPrefix + "seed_asr_vs_time.png", bbox_inches="tight")


def seed_asr_vs_queries():
    fig, ax = plt.subplots()
    plot_scatter(ax, "queries", "seed-asr")
    ax.set(xlabel='Number of Queries', ylabel='Seed ASR')
    ax.set_title('Seed ASR vs Queries', pad=20)
    fig.savefig(outPrefix + "seed_asr_vs_queries.png", bbox_inches="tight")


def seed_asr_vs_attacks():
    fig, ax = plt.subplots()
    plot_scatter(ax, "attacks", "seed-asr")
    ax.set(xlabel='Number of Attacks', ylabel='Seed ASR')
    ax.set_title('Seed ASR vs Attacks', pad=20)
    fig.savefig(outPrefix + "seed_asr_vs_attacks.png", bbox_inches="tight")


def seed_asr_vs_templates():
    fig, ax = plt.subplots()
    plot_scatter(ax, "templates", "seed-asr")
    ax.set(xlabel='Number of Templates', ylabel='Seed ASR')
    ax.set_title('Seed ASR vs Templates', pad=20)
    fig.savefig(outPrefix + "seed_asr_vs_templates.png", bbox_inches="tight")


def seed_asr_vs_successful_templates():
    fig, ax = plt.subplots()
    plot_scatter(ax, "successful", "seed-asr")
    ax.set(xlabel='Number of Successful Templates', ylabel='Seed ASR')
    ax.set_title('Seed ASR vs Successful Templates', pad=20)
    fig.savefig(outPrefix + "seed_asr_vs_successful_templates.png", bbox_inches="tight")


def templates_vs_queries():
    fig, ax = plt.subplots()
    plot_scatter(ax, "queries", "templates")
    ax.set(xlabel='Number of Queries', ylabel='Number of Templates')
    ax.set_title('Templates vs Queries', pad=20)
    fig.savefig(outPrefix + "templates_vs_queries.png", bbox_inches="tight")


def successful_templates_vs_queries():
    fig, ax = plt.subplots()
    plot_scatter(ax, "queries", "successful")
    ax.set(xlabel='Number of Queries', ylabel='Number of Successful Templates')
    ax.set_title('Successful Templates vs Queries', pad=20)
    fig.savefig(outPrefix + "successful_templates_vs_queries.png", bbox_inches="tight")


def successful_templates_vs_templates():
    fig, ax = plt.subplots()
    plot_scatter(ax, "templates", "successful")
    ax.set(xlabel='Number of Templates', ylabel='Number of Successful Templates')
    ax.set_title('Successful Templates vs Templates', pad=20)
    fig.savefig(outPrefix + "successful_templates_vs_templates.png", bbox_inches="tight")


def invalid_templates_vs_queries():
    fig, ax = plt.subplots()
    plot_scatter(ax, "queries", "invalid")
    ax.set(xlabel='Number of Queries', ylabel='Number of Invalid Templates')
    ax.set_title('Invalid Templates vs Queries', pad=20)
    fig.savefig(outPrefix + "invalid_templates_vs_queries.png", bbox_inches="tight")


def attacks_vs_questions():
    fig, ax = plt.subplots()

    for k in keys:
        # create data
        x = sorted(data["all_questions"][k])
        y1 = [data["safe_questions"][k].get(qid, 0) for qid in x]
        y2 = [data["vulnerable_questions"][k].get(qid, 0) for qid in x]

        # plot bars in stack manner
        ax.bar(x, y1, color='green')
        ax.bar(x, y2, bottom=y1, color='red')

        ax.set(xlabel='Questions', ylabel='Number of Attacks')
        ax.set_title('Attacks vs Questions', pad=20)
        ax.grid(True, alpha=0.25)
        fig.savefig(outPrefix + f"question_{k}.png", bbox_inches="tight")


def aqj_vs_queries():
    fig, ax = plt.subplots()
    plot_scatter(ax, "queries", "aqj")
    ax.set(xlabel='Number of Queries', ylabel='Average Queries per Jailbreak')
    ax.set_title('Average Queries per Jailbreak vs Queries', pad=20)
    fig.savefig(outPrefix + "aqj_vs_queries.png", bbox_inches="tight")


def aqj_vs_jailbreaks():
    fig, ax = plt.subplots()
    plot_scatter(ax, "jailbreaks", "aqj")
    ax.set(xlabel='Number of Jailbreaks', ylabel='Average Queries per Jailbreak')
    ax.set_title('Average Queries per Jailbreak vs Jailbreaks', pad=20)
    fig.savefig(outPrefix + "aqj_vs_jailbreaks.png", bbox_inches="tight")


read_all_inputs()
keys = sorted(keys)

# aqj_vs_queries()
aqj_vs_jailbreaks()

# queries_vs_time()
# attacks_vs_queries()

# seed_asr_vs_time()
seed_asr_vs_queries()
# seed_asr_vs_attacks()
seed_asr_vs_templates()
seed_asr_vs_successful_templates()

templates_vs_queries()
successful_templates_vs_queries()
successful_templates_vs_templates()
invalid_templates_vs_queries()

# attacks_vs_questions()
