import webbrowser
import typing
import os
import sys
from abc import ABC, abstractmethod

import speech_recognition as sr

class Action(ABC):

    action_key: str

    @abstractmethod
    def action_callback(self, action_text) -> typing.Any:
        pass

    def register_va(self, virtual_assisant):
        self._va = virtual_assisant

    def sans_action_key(self, action_text):
        return action_text[len(self.action_key)+1:]

class BrowserAction(Action):

    action_key = "open browser"

    def action_callback(self, action_text):
        domain = self.sans_action_key(action_text)
        url = f"https://{domain}"
        print(f"Opening: {url}")
        self._va.("Opening {domain}")
        webbrowser.open(url)

class EchoAction(Action):

    action_key = "echo"

    def action_callback(self, action_text) -> typing.Any:
        print(action_text)
        self._va.text_to_speech(self.sans_action_key(action_text))

class StopAction(Action):
    
    action_key = "stop listening"

    def action_callback(self, action_text) -> typing.Any:
        print("Stopping Virtual Assistant...")
        sys.exit(0)


class VirtualAssistant:

    def __init__(self, timeout: int = 3, actions: typing.List[Action]=[]):
        self.listener = sr.Recognizer()
        self.timeout = timeout
        self.action_register = []
        for action in actions:
            self.register_action(action)

    def register_action(self, action: Action):
        """Adds new Action instances to the Virtual Assistant's action register."""
        self.action_register.append(action)
        action.register_va(self)

    def parse_action(self, text: str):
        """Checks the VA command register and executes callbacks when found."""
        for action in self.action_register:
            if text.lower().startswith(action.action_key):
                action.action_callback(text)
                return
        print(f"No actions taken on: {text}")

    def speech_to_text(self) -> str:
        """"""
        try:
            with sr.Microphone() as mic:
                print("Arni is listening...")
                self.listener.adjust_for_ambient_noise(mic)
                utterance = self.listener.listen(mic, timeout=self.timeout)
                content = self.listener.recognize_google(utterance)
                return content
        except (sr.UnknownValueError, sr.RequestError, sr.WaitTimeoutError):
            return ""

    def text_to_speech(self, text: str):
        print(f"Saying: {text}")
        cmd = f'gtts-cli --nocheck "{text}" | mpg123 -q -'
        print(cmd)
        os.system(cmd)



def main():
    print("Creating virtual assistant...")
    va = VirtualAssistant(actions=[BrowserAction(),EchoAction(), StopAction()])
    # breakpoint()
    while True:
        content = va.speech_to_text()
        va.parse_action(content)

if __name__ == "__main__":
    main()