from datetime import datetime, timedelta
from time import time

from libqtile.command.base import expose_command
from libqtile.utils import send_notification
from libqtile.widget import base


class Pomodoro(base.InLoopPollText):
    """Pomodoro technique widget"""

    defaults = [
        ("num_pomodori", 4, "Number of pomodori to do in a cycle"),
        ("length_pomodori", 25, "Length of one pomodori in minutes"),
        ("length_short_break", 5, "Length of a short break in minutes"),
        ("length_long_break", 15, "Length of a long break in minutes"),
        ("color_inactive", "ff0000", "Colour then pomodoro is inactive"),
        ("color_active", "00ff00", "Colour then pomodoro is running"),
        ("color_break", "ffff00", "Colour then it is break time"),
        ("notification_on", True, "Turn notifications on"),
        ("prefix_inactive", "POMODORO", "Prefix when app is inactive"),
        ("prefix_active", "", "Prefix then app is active"),
        ("prefix_break", "B ", "Prefix during short break"),
        ("prefix_long_break", "LB ", "Prefix during long break"),
        ("prefix_paused", "PAUSE", "Prefix during pause"),
        (
            "update_interval",
            1,
            "Update interval in seconds, if none, the "
            "widget updates whenever the event loop is idle.",
        ),
    ]

    STATUS_START = "start"
    STATUS_INACTIVE = "inactive"
    STATUS_ACTIVE = "active"
    STATUS_BREAK = "break"
    STATUS_LONG_BREAK = "long_break"
    STATUS_PAUSED = "paused"

    status = "inactive"
    paused_status = None
    end_time = datetime.now()
    time_left = None
    pomodoros = 1

    def __init__(self, **config):
        base.InLoopPollText.__init__(self, "", **config)
        self.add_defaults(Pomodoro.defaults)
        self.prefix = {
            "inactive": self.prefix_inactive,
            "active": self.prefix_active,
            "break": self.prefix_break,
            "long_break": self.prefix_long_break,
            "paused": self.prefix_paused,
        }

        self.add_callbacks(
            {
                "Button1": self.toggle_break,
                "Button3": self.toggle_active,
            }
        )

    def tick(self):
        self.update(self.poll())
        return self.update_interval - time() % self.update_interval

    def _update(self):
        if self.status in [self.STATUS_INACTIVE, self.STATUS_PAUSED]:
            return

        if self.end_time > datetime.now() and self.status != self.STATUS_START:
            return

        if self.status == self.STATUS_ACTIVE and self.pomodoros == self.num_pomodori:
            self.status = self.STATUS_LONG_BREAK
            self.end_time = datetime.now() + timedelta(minutes=self.length_long_break)
            self.pomodoros = 1
            if self.notification_on:
                self._send_notification(
                    "normal",
                    "Please take a long break! End Time: " + self.end_time.strftime("%H:%M"),
                )
            return

        if self.status == self.STATUS_ACTIVE:
            self.status = self.STATUS_BREAK
            self.end_time = datetime.now() + timedelta(minutes=self.length_short_break)
            self.pomodoros += 1
            if self.notification_on:
                self._send_notification(
                    "normal",
                    "Please take a short break! End Time: " + self.end_time.strftime("%H:%M"),
                )
            return

        self.status = self.STATUS_ACTIVE
        self.end_time = datetime.now() + timedelta(minutes=self.length_pomodori)
        if self.notification_on:
            self._send_notification(
                "critical",
                "Please start with the next Pomodori! End Time: "
                + self.end_time.strftime("%H:%M"),
            )

        return

    def _get_text(self):
        self._update()

        if self.status in [self.STATUS_INACTIVE, self.STATUS_PAUSED]:
            self.layout.colour = self.color_inactive
            return self.prefix[self.status]

        time_left = self.end_time - datetime.now()

        if self.status == self.STATUS_ACTIVE:
            self.layout.colour = self.color_active
        else:
            self.layout.colour = self.color_break

        time_string = f"{time_left.seconds // 3600:d}:{time_left.seconds % 3600 // 60:02d}:{time_left.seconds % 60:02d}"
        return self.prefix[self.status] + time_string

    @expose_command()
    def toggle_break(self):
        if self.status == self.STATUS_INACTIVE:
            self.status = self.STATUS_START
            return

        if self.paused_status is None:
            self.paused_status = self.status
            self.time_left = self.end_time - datetime.now()
            self.status = self.STATUS_PAUSED
            if self.notification_on:
                self._send_notification("low", "Pomodoro has been paused")
        else:
            self.status = self.paused_status
            self.paused_status = None
            self.end_time = self.time_left + datetime.now()
            if self.notification_on:
                if self.status == self.STATUS_ACTIVE:
                    status = "Pomodoro"
                else:
                    status = "break"

                self._send_notification(
                    "normal",
                    f"Please continue on {status}! End Time: " + self.end_time.strftime("%H:%M"),
                )

    @expose_command()
    def toggle_active(self):
        if self.status != self.STATUS_INACTIVE:
            self.status = self.STATUS_INACTIVE
            if self.notification_on:
                self._send_notification("critical", "Pomodoro has been suspended")
        else:
            self.status = self.STATUS_START

    def _send_notification(self, urgent, message):
        send_notification("Pomodoro", message, urgent=urgent)

    def poll(self):
        return self._get_text()
