import holoviews as hv
import pandas as pd

from django.utils import timezone

from math import pi

from bokeh.models import HoverTool
from bokeh.io import output_file, show
from bokeh.plotting import figure
from bokeh.transform import cumsum
from bokeh.layouts import row, column
from holoviews.operation import timeseries
from dateutil.relativedelta import relativedelta

from .models import Status, Mood, StatusActivity


def moodstats(user):
    hv.extension("bokeh")

    tooltips = [("Date", "@date{%F %H:%M}"), ("Mood", "@name (@value)")]

    formatters = {"@date": "datetime"}

    hover = HoverTool(tooltips=tooltips, formatters=formatters)

    pointdict = {"date": [], "value": [], "color": [], "name": []}

    for status in Status.objects.filter(user=user):
        if status.mood:
            pointdict["date"].append(status.timestamp)
            pointdict["value"].append(status.mood.value)
            pointdict["color"].append(status.mood.color)
            pointdict["name"].append(status.mood.name)

    pointframe = pd.DataFrame.from_dict(pointdict)

    points = hv.Points(pointframe)

    points.opts(
        tools=[hover],
        color="color",
        cmap="Category20",
        line_color="black",
        size=25,
        width=600,
        height=400,
        show_grid=True,
    )

    pointtuples = [
        (pointdict["date"][i], pointdict["value"][i])
        for i in range(len(pointdict["date"]))
    ]

    line = hv.Curve(pointtuples)

    maxval = Mood.objects.filter(user=user).latest("value").value
    maxy = maxval + max(maxval * 0.1, 1)

    maxx = timezone.now().timestamp() * 1000
    minx = maxx - (60 * 60 * 24 * 7) * 1000

    output = points * line * timeseries.rolling(line, rolling_window=7)
    output.opts(ylim=(0, maxy), xlim=(minx, maxx))

    return output


def activitystats(user):
    output = {}

    for status in Status.objects.filter(user=user):
        for activity in status.activity_set:
            if not activity in output.keys():
                output[activity] = {
                    "alltime": 0,
                    "yearly": 0,
                    "monthly": 0,
                    "weekly": 0,
                }

            output[activity]["alltime"] += 1

            if status.timestamp > timezone.now() - relativedelta(years=1):
                output[activity]["yearly"] += 1

            if status.timestamp > timezone.now() - relativedelta(months=1):
                output[activity]["monthly"] += 1

            if status.timestamp > timezone.now() - relativedelta(weeks=1):
                output[activity]["weekly"] += 1

    return output


def moodpies(user):
    hv.extension("bokeh")

    maxdate = timezone.now()

    weekly_moods = Status.objects.filter(
        user=user,
        timestamp__lte=maxdate,
        timestamp__gte=maxdate - relativedelta(weeks=1),
    )
    monthly_moods = Status.objects.filter(
        user=user,
        timestamp__lte=maxdate,
        timestamp__gte=maxdate - relativedelta(months=1),
    )
    yearly_moods = Status.objects.filter(
        user=user,
        timestamp__lte=maxdate,
        timestamp__gte=maxdate - relativedelta(years=1),
    )

    weekly = dict()
    colors = []

    for mood in Mood.objects.filter(user=user):
        weekly[mood.name] = 0
        colors.append(mood.color)

    monthly, yearly = weekly.copy(), weekly.copy()

    for status in weekly_moods:
        if status.mood:
            weekly[status.mood.name] += 1

    for status in monthly_moods:
        if status.mood:
            monthly[status.mood.name] += 1

    for status in yearly_moods:
        if status.mood:
            yearly[status.mood.name] += 1

    weekly_data = (
        pd.Series(weekly).reset_index(name="value").rename(columns={"index": "mood"})
    )
    weekly_data["angle"] = weekly_data["value"] / weekly_data["value"].sum() * 2 * pi
    weekly_data["color"] = colors

    weekly_chart = figure(
        height=350,
        title="Weekly",
        toolbar_location=None,
        tools="hover",
        tooltips="@mood: @value",
    )
    weekly_chart.axis.visible = False

    weekly_chart.wedge(
        x=0,
        y=1,
        radius=0.4,
        start_angle=cumsum("angle", include_zero=True),
        end_angle=cumsum("angle"),
        line_color="white",
        fill_color="color",
        legend_label="mood",
        source=weekly_data,
    )

    monthly_data = (
        pd.Series(monthly).reset_index(name="value").rename(columns={"index": "mood"})
    )
    monthly_data["angle"] = monthly_data["value"] / monthly_data["value"].sum() * 2 * pi
    monthly_data["color"] = colors

    monthly_chart = figure(
        height=350,
        title="Monthly",
        toolbar_location=None,
        tools="hover",
        tooltips="@mood: @value",
    )
    monthly_chart.axis.visible = False

    monthly_chart.wedge(
        x=0,
        y=1,
        radius=0.4,
        start_angle=cumsum("angle", include_zero=True),
        end_angle=cumsum("angle"),
        line_color="white",
        fill_color="color",
        legend_label="mood",
        source=monthly_data,
    )

    yearly_data = (
        pd.Series(yearly).reset_index(name="value").rename(columns={"index": "mood"})
    )
    yearly_data["angle"] = yearly_data["value"] / yearly_data["value"].sum() * 2 * pi
    yearly_data["color"] = colors

    yearly_chart = figure(
        height=350,
        title="Yearly",
        toolbar_location=None,
        tools="hover",
        tooltips="@mood: @value",
    )
    yearly_chart.axis.visible = False

    yearly_chart.wedge(
        x=0,
        y=1,
        radius=0.4,
        start_angle=cumsum("angle", include_zero=True),
        end_angle=cumsum("angle"),
        line_color="white",
        fill_color="color",
        legend_label="mood",
        source=yearly_data,
    )

    return column(weekly_chart, monthly_chart, yearly_chart)


def activitymood(activity):
    hv.extension("bokeh")

    tooltips = [("Date", "@date{%F %H:%M}"), ("Mood", "@name (@value)")]

    formatters = {"@date": "datetime"}

    hover = HoverTool(tooltips=tooltips, formatters=formatters)

    pointdict = {"date": [], "value": [], "color": [], "name": []}

    for statusactivity in StatusActivity.objects.filter(activity=activity):
        if statusactivity.status.mood:
            pointdict["date"].append(statusactivity.status.timestamp)
            pointdict["value"].append(statusactivity.status.mood.value)
            pointdict["color"].append(statusactivity.status.mood.color)
            pointdict["name"].append(statusactivity.status.mood.name)

    pointframe = pd.DataFrame.from_dict(pointdict)

    points = hv.Points(pointframe)

    points.opts(
        tools=[hover],
        color="color",
        cmap="Category20",
        line_color="black",
        size=25,
        width=600,
        height=400,
        show_grid=True,
    )

    pointtuples = [
        (pointdict["date"][i], pointdict["value"][i])
        for i in range(len(pointdict["date"]))
    ]

    line = hv.Curve(pointtuples)

    maxval = Mood.objects.filter(user=activity.user).latest("value").value
    maxy = maxval + max(maxval * 0.1, 1)

    maxx = timezone.now().timestamp() * 1000
    minx = maxx - (60 * 60 * 24 * 7) * 1000

    output = points * line * timeseries.rolling(line, rolling_window=7)
    output.opts(ylim=(0, maxy), xlim=(minx, maxx))

    return output


def activitypies(activity):
    hv.extension("bokeh")

    maxdate = timezone.now()

    sa = StatusActivity.objects.filter(activity=activity)

    weekly = dict()
    colors = []

    for mood in Mood.objects.filter(user=activity.user):
        weekly[mood.name] = 0
        colors.append(mood.color)

    monthly, yearly = weekly.copy(), weekly.copy()

    for single in sa:
        if single.status.mood:
            if single.status.timestamp > timezone.now() - relativedelta(weeks=1):
                weekly[single.status.mood.name] += 1
            if single.status.timestamp > timezone.now() - relativedelta(months=1):
                monthly[single.status.mood.name] += 1
            if single.status.timestamp > timezone.now() - relativedelta(years=1):
                yearly[single.status.mood.name] += 1

    weekly_data = (
        pd.Series(weekly).reset_index(name="value").rename(columns={"index": "mood"})
    )
    weekly_data["angle"] = weekly_data["value"] / weekly_data["value"].sum() * 2 * pi
    weekly_data["color"] = colors

    weekly_chart = figure(
        height=350,
        title="Weekly",
        toolbar_location=None,
        tools="hover",
        tooltips="@mood: @value",
    )
    weekly_chart.axis.visible = False

    weekly_chart.wedge(
        x=0,
        y=1,
        radius=0.4,
        start_angle=cumsum("angle", include_zero=True),
        end_angle=cumsum("angle"),
        line_color="white",
        fill_color="color",
        legend_label="mood",
        source=weekly_data,
    )

    monthly_data = (
        pd.Series(monthly).reset_index(name="value").rename(columns={"index": "mood"})
    )
    monthly_data["angle"] = monthly_data["value"] / monthly_data["value"].sum() * 2 * pi
    monthly_data["color"] = colors

    monthly_chart = figure(
        height=350,
        title="Monthly",
        toolbar_location=None,
        tools="hover",
        tooltips="@mood: @value",
    )
    monthly_chart.axis.visible = False

    monthly_chart.wedge(
        x=0,
        y=1,
        radius=0.4,
        start_angle=cumsum("angle", include_zero=True),
        end_angle=cumsum("angle"),
        line_color="white",
        fill_color="color",
        legend_label="mood",
        source=monthly_data,
    )

    yearly_data = (
        pd.Series(yearly).reset_index(name="value").rename(columns={"index": "mood"})
    )
    yearly_data["angle"] = yearly_data["value"] / yearly_data["value"].sum() * 2 * pi
    yearly_data["color"] = colors

    yearly_chart = figure(
        height=350,
        title="Yearly",
        toolbar_location=None,
        tools="hover",
        tooltips="@mood: @value",
    )
    yearly_chart.axis.visible = False

    yearly_chart.wedge(
        x=0,
        y=1,
        radius=0.4,
        start_angle=cumsum("angle", include_zero=True),
        end_angle=cumsum("angle"),
        line_color="white",
        fill_color="color",
        legend_label="mood",
        source=yearly_data,
    )

    return column(weekly_chart, monthly_chart, yearly_chart)
