import os
import yaml
import json
import random
from datetime import datetime
from time import time
import click

from .timeslot import timeslot
from .report import JaktReport
from .exceptions import *


class jakt:
    def __init__(self):
        # TODO: Set up config
        # TODO: Read from config path and set variables

        self.dataPath = os.path.join(os.path.expanduser("~"), ".jakt")
        self.pathConfig = os.path.join(self.dataPath, "config.yml")
        self.pathCategories = os.path.join(self.dataPath, "categories.yml")
        self.pathProjects = os.path.join(self.dataPath, "projects.json")
        self.pathTimeslots = os.path.join(self.dataPath, "timeslots.json")
        self.pathCurrent = os.path.join(self.dataPath, "current.json")

        # Standard setup for first time use.
        if not os.path.exists(self.dataPath):
            os.mkdir(self.dataPath)

            # Create standard files
            paths = [
                self.pathConfig,
                self.pathCategories,
                self.pathProjects,
                self.pathTimeslots,
            ]

            for path in paths:
                # Create standard config
                f = open(path, "x")
                f.close()

            standardConfig = {"Remote": False}
            with open(self.pathConfig, "a") as f:
                yaml.dump(standardConfig, f, default_flow_style=False)
        else:
            # TODO: Set up config-loading
            pass

    ## Main working functions
    def start(self, project: str, tags: list[str]) -> dict:
        """
        Adds inputed data into the current file in jakt directory.
        """

        if os.path.exists(self.pathCurrent):
            raise JaktActiveError

        if tags == ():
            tags = ["<no tags>"]

        ts = timeslot(
            ID=self.generateUniqueID(),
            start=round(time()),
            end=None,
            project=project,
            tags=tags
        )

        with open(self.pathCurrent, "w") as f:
            f.write(str(ts.toDictString()))
            f.close()

        return ts

    def stop(self) -> timeslot:
        if not os.path.exists(self.pathCurrent):
            raise JaktNotActiveError

        # Update status from file
        self.status()

        #  Create object to add
        ts = timeslot(
            ID=self.generateUniqueID(),
            start=self.activeTimeslot["start"],
            end=round(time()),
            project=self.activeTimeslot["project"],
            tags=self.activeTimeslot["tags"],
        )

        # Add object to timeslots
        ts_added = self.add(ts)

        # Removes timeslot data in current timeslot
        os.remove(self.pathCurrent)

        return ts_added

    def status(self) -> dict:
        if not os.path.exists(self.pathCurrent):
            raise JaktNotActiveError

        with open(self.pathCurrent, "r") as f:
            status = json.load(f)
            f.close()

        elapsedTime = datetime.fromtimestamp(round(time())) - datetime.fromtimestamp(
            status["start"]
        )
        status["elapsed"] = elapsedTime.seconds

        hours, remainder = divmod(elapsedTime.seconds, 3600)
        minutes, seconds = divmod(remainder, 60)

        status["elapsedHour"] = hours
        if seconds > 30:
            status["elapsedMin"] = minutes + 1
        else:
            status["elapsedMin"] = minutes

        self.activeTimeslot = status

        return status

    def add(self, ts: timeslot) -> timeslot:
        """
        Adds new timeslot from.
        TODO: Implement add for known data.
        """

        # Find all logged timeslots
        timeslots = self.getTimeslots()

        # Append new timeslot to list
        timeslots.append(ts)

        # Write all timeslots, including newly added to file
        self.putTimeslots(timeslots)

        return ts

    def report(self) -> JaktReport:
        """
        Returns a JaktReport object
        """

        return JaktReport(self)

    def resume(self) -> timeslot:
    	"""
    	Starts new timeslot with same options as previously logged timeslot
    	"""

    	# Get last logged timeslot
    	timeslots = self.getTimeslots()
    	timeslots.reverse()
    	last_ts = timeslots[0]

    	response = self.start(project = last_ts.project, tags = last_ts.tags)

    	return response


    ## Get and put data
    def getCategories(self) -> list[str]:
        """
        Returns a list of all defined categories
        """
        try:
            with open(self.pathCategories, "r") as f:
                categories = yaml.safe_load(f)
                f.close()

            return categories
        except OSError:
            raise JaktPathError(self.pathCategories)

    def getProjects(self) -> list[str]:
        """
        Returns list of all projects
        """
        timeslots = self.getTimeslots()

        projects = []
        for i in range(len(timeslots)):
            if timeslots[i].project not in projects:
                projects.append(timeslots[i].project)

        return projects

    def getTags(self, project: str = None) -> list[str]:
        """
        Returns a list of all used tags.

        If project is given only tags for the matching project are given.
        """
        timeslots = self.getTimeslots()

        tags = []
        for i in range(len(timeslots)):
            if project and not (project == timeslots[i].project):
                continue

            currentTags = timeslots[i].tags

            for j in range(len(currentTags)):
                if currentTags[j] not in tags:
                    tags.append(currentTags[j])

        return tags

    def getTimeslots(
        self, from_=False, to=False, project=False, tag=False
    ) -> list[timeslot]:
        """
        Returns a list of logged timeslots
        """
        try:
            with open(self.pathTimeslots, "r") as f:
                timeslots = json.load(f)
                f.close()

            # Create timeslot instances
            for i in range(len(timeslots)):
                timeslots[i] = timeslot.from_json(timeslots[i])

            # TODO: Implement filtering with to and from_
            if to and from_:
                # Remove timeslots that do not match
                pass

            # Filters by project if project is given
            if project:
                project_filter = []
                for ts in timeslots:
                    if ts.project == project:
                        project_filter.append(ts)

                timeslots = project_filter

            # Filters by tags if tage are given
            if tag:
                tag_filter = []
                for ts in timeslots:
                    if tag in ts.tags:
                        tag_filter.append(ts)

                timeslots = tag_filter

            return timeslots
        except OSError:
            raise JaktPathError(self.pathTimeslots)

    def getTimeslot(self, queryId: str) -> timeslot:
        timeslots = self.getTimeslots()

        for ts in timeslots:
            if ts.id == queryId:
                return ts

        return False

    def putTimeslots(self, timeslots: list[timeslot]):
        obj_list = []

        for ts in timeslots:
            obj_list.append(ts.toDict())

        try:
            with open(self.pathTimeslots, "w") as f:
                json.dump(obj_list, f)
                f.close()
        except OSError:
            raise JaktPathError(self.pathTimeslots)

    ## Helper functions
    def generateUniqueID(self):
        timeslots = self.getTimeslots()

        usedIDs = []
        for ts in timeslots:
            usedIDs.append(ts.id)

        ID = "%08x" % random.randrange(16**8)
        if ID not in usedIDs:
            return ID
        else:
            return self.generateUniqueID()

    ## Remote syncronization
    def fetch(self):
        pass

    def pull(self):
        pass

    def push(self):
        pass
