import webbrowser
class TaskAlreadyExistsError(Exception):
    """Exception raised when a task already exists in the list."""
    def __init__(self, message):
        super().__init__(message)

class TaskNotFoundError(Exception):
    """Exception raised when a task is not found in the list."""
    def __init__(self, message):
        super().__init__(message)

class ToDo:
    """Class representing a to-do list."""

    def __init__(self) -> None:
        """Initialize an empty to-do list. Which is a dictionary"""
        self.tasks = {}

    def add_task(self, task:str, deadline:str) -> str:
        """Add a new task to the list.

        Args:
            task (str): The task description.
            deadline (str): The deadline in dd-mm-yy format.

        Returns:
            str: A confirmation message indicating that the task has been added.

        Raises:
            TaskAlreadyExistsError: If the task already exists in the list.
            TypeError: If the task parameter is not a string.
            ValueError: If the task parameter is an empty string.
        """
        if not isinstance(task, str):
            raise TypeError("Task must be a string.")
        if not task:
            raise ValueError("Task cannot be an empty string.")
        if task in self.tasks.keys():
            raise TaskAlreadyExistsError(f"{task} already exists in the list.")
        else:
            self.tasks[task] = {'status':False, 'deadline':deadline}
            print(self.tasks)
            return f"Task has been added to the list."
        
    def finish_task(self, task:str) -> str:
        """
        Mark a task as finished and open a web page for celebration.

        Args:
            task (str): The task to mark as finished.

        Returns:
            str: A message confirming that the task has been marked as finished.

        Raises:
            TypeError: If the task is not a string.
            TaskNotFoundError: If the task does not exist in the list.
        """
        try:
            if not isinstance(task, str):
                raise TypeError("Task must be a string.")
            self.tasks[task]['status'] = True
            webbrowser.open("well_done.html")
            return "Task finished"
        except Exception:
            raise TaskNotFoundError("No such task in the list.")
    
    def remove_task(self, task:str) ->str:
        """
        Remove a task from the task list.

        Args:
            task (str): The task to remove.

        Returns:
            str: A message confirming that the task has been removed.

        Raises:
            TypeError: If the task is not a string.
            TaskNotFoundError: If the task does not exist in the list.
        """
        try:
            if not isinstance(task, str):
                raise TypeError("Task must be a string.")
            del self.tasks[task]
            return "Task removed"
        except Exception:
            raise TaskNotFoundError("No such task in the list.")
    
    def update_task(self, task:str, new_deadline:str) -> str:
        """
        Update the status and deadline of a task.

        Args:
            task (str): The task to update.
            new_deadline (str): The new deadline for the task in dd-mm-yy format.

        Returns:
            str: A message confirming that the task has been updated.

        Raises:
            TypeError: If the task is not a string.
            TaskNotFoundError: If the task does not exist in the list.
        """
        try:
            if not isinstance(task, str):
                raise TypeError("Task must be a string.")
            self.tasks[task] = {'status': False, 'deadline': new_deadline}
            return "Task updated"
        except Exception:
            raise TaskNotFoundError("No such task in the list.")
    
    def view_tasks(self) -> list:
        """View the tasks in the list.

        Returns:
            list: A list of task descriptions and their status or completion.

        Raises:
            TaskNotFoundError: If there are no tasks in the list.
        """
        try:
            task_list = []
            for task in self.tasks:
                if self.tasks[task]['status'] == False:
                    task_list.append(f"{task} by {self.tasks[task]['deadline']}")
                if self.tasks[task]['status'] == True:
                    task_list.append(f"{task} finished")
            return task_list
        except Exception:
            raise TaskNotFoundError("There are no tasks in the list.")

if __name__ == "__main__":
    app = ToDo()
    while True:
        print("-------------------------------")
        print()
        print("1. Add task")
        print("2. View tasks")
        print("3. delete task")
        print("4. update task")
        print("5. finish task")
        print("6. exit app")
        print()
        print("-------------------------------")

        choice = int(input("Enter your choice: "))
        if choice == 1:
            task = input("Enter task: ")
            deadline = input("Enter task deadline: ")
            try:
                message = app.add_task(task, deadline)
                print(message)
            except TypeError as e:
                print(e)
            except ValueError as e:
                print(e)
            except TaskAlreadyExistsError as e:
                print(e)
        elif choice == 2:
            try:
                print("-------------------------------")
                task_list = app.view_tasks()
                for task in task_list:
                    print(task)
                print("-------------------------------")
            except TaskNotFoundError as e:
                print(e)
        elif choice == 3:
            try:
                task = input("Enter the task you want to remove: ")
                message = app.remove_task(task)
                print(message)
            except TypeError as e:
                print(e)
            except TaskNotFoundError as e:
                print(e)
        elif choice == 4:
            try:
                task = input("Enter the task you want to update: ")
                deadline = input("What is the new deadline? ")
                message = app.update_task(task,deadline)
                print(message)
            except TypeError as e:
                print(e)
            except TaskNotFoundError as e:
                print(e)
        elif choice == 5:
            try:
                task = input("Enter the task you have finised: ")
                message = app.finish_task(task)
                print(message)
            except TypeError as e:
                print(e)
            except TaskNotFoundError as e:
                print(e)
        elif choice == 6:
            print("Thanks for using")
            break
        else:
            print("Invalid choice!")