import asyncio
#crawlee imports
from crawlee.crawlers import PlaywrightCrawler, PlaywrightCrawlingContext, PlaywrightPreNavCrawlingContext
from crawlee.sessions import SessionPool, SessionCookies
from crawlee.browsers import BrowserPool
from crawlee.browsers._playwright_browser_plugin import PlaywrightBrowserPlugin
from crawlee.sessions import CookieParam
from crawlee.storages import RequestQueue
from playwright.async_api import expect, Locator
#kivy imports
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.popup import Popup
from kivy.uix.spinner import Spinner
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import ScreenManager, Screen
import threading
#modules for the window popping up
import tkinter
from tkinter.filedialog import askopenfilename
from enum import Enum, auto
from functools import partial

class OrganizedListOfExternalVariables():
    """
    class for handling global properties that need to be accessed in a whole bunch of places
    """

    def __init__(self, spaceConst, titleText = "") -> None:
        #using the underscore prefix to abide by the naming convention for priv variables
        self.spaceConst = spaceConst
        self.titleText = titleText
        self.websiteProcessingNum = 1

    def __getattr__(self, name: str):
        #https://realpython.com/python-descriptors/#how-attributes-are-accessed-with-the-lookup-chain for info about why __dict__ is used
        #tldr in this implimentation, using __dict__ lets you look up the instantiated value of the variable with ref to the class that used the descriptor
        #so the loop is like Class -> Descriptor -> Variable name via dict lookup(and consequently value)
        return self.__dict__[name]
    def __setattr__(self, name: str, value) -> None:
        #setter function, if you need any specific validation you can specify stuff with the name 
        if name in self.__dict__:
            self.__dict__[name] = value
        else:
            self.__dict__.update({name: value})
    

# enumerator sets up an easy way to add more screens while keeping them named in a semi-sane fashion
class ScreenList(Enum):
    MAIN = "main"
    VALIDATOR = "validator"
    END = "end"


externalVariables = OrganizedListOfExternalVariables(spaceConst=100)


# Handles screen navigation
class ScreenController:
    def __init__(self, manager):
        #manager var is the instance of ScreenManagerClass that we have below
        self.manager = manager
    #switches screens based on the enum value
    def switch_to(self, screen_type: ScreenList):
        #enum's "auto" function sets strings to be lowercase if the main input is uppercase, so this works out quite nicely
        self.manager.current = screen_type.name.lower()

class ScreenClass(Screen):
    def __init__(self, screenList: ScreenList, appInstance: App, **kwargs):
        super(ScreenClass, self).__init__(**kwargs)
        #add the grid layout under this
        self.name = screenList.name.lower()
        self.screenList = screenList
        self.buildInstructions()
        self.appInstance = appInstance

    def buildInstructions(self):
        baseLayout = GridLayout(cols=2, spacing=[externalVariables.spaceConst,externalVariables.spaceConst])
        print("Reached main!!!!")

        #button for transitioning to the next screen
        transitionLabel = Label(text="Press this button once you're ready")
        transitionButton = self.genericButtonFunction(text='Click me!', event=self.screenTransitioner)
        baseLayout.add_widget(transitionLabel)
        baseLayout.add_widget(transitionButton)
        print("Reached end of main function!!")

        self.add_widget(baseLayout)

    def genericButtonFunction(self, text, event):
        button = Button(text=text)
        button.bind(on_press=event) #type: ignore
        return button
    def screenTransitioner(self, instance):
        print("reached the transition!")
        App.stop(self.appInstance)


class ValidatorScreen(Screen):
    def __init__(self, screenList: ScreenList, appInstance: App, **kwargs):
        super(Screen, self).__init__(**kwargs)
        self.screenList = screenList
        self.appInstance = appInstance
        # thread1 = threading.Thread(self.validatorFunction())
        # thread2 = threading.Thread(self.mainRunner()) 
        # thread1.start()
        # thread2.start()

        # thread1.join()
        # thread2.join()

        #self.validatorFunction()
    
    def screenTransitioner(self, instance):
        #self.screenManager.add_widget(ScreenClass(ScreenList.END, self.controller, self.screenManager))
        App.stop(self.appInstance)
        pass

    def validatorFunction(self):
        baseLayout = GridLayout(cols=2, spacing=[externalVariables.spaceConst,externalVariables.spaceConst])
        #what i could do here is move everything from the screen class to screen manager so that screen manager holds all of the code for what each screen does, and then this has all the functionality of those screens?
        transitionLabel = Label(text="Press this button once you're ready")
        transitionButton = self.genericButtonFunction(text='Click me!', event=self.screenTransitioner)
        baseLayout.add_widget(transitionLabel)
        baseLayout.add_widget(transitionButton)
        

    
    def genericButtonFunction(self, text, event):
        button = Button(text=text)
        button.bind(on_press=event) #type: ignore
        return button
    
# class ScreenManagerClass(ScreenManager):
#     def __init__(self,**kwargs):
#         super(ScreenManagerClass, self).__init__(**kwargs)
#         self.controller = ScreenController(self)
#         self.transitionBool = False
#         self.screenLoader()


#     def screenLoader(self):
#         mainScreen = ScreenClass(ScreenList.MAIN, self.controller, self)
#         self.add_widget(mainScreen)




class MainScreenApp(App):

    def build(self):
        return ScreenClass(ScreenList.MAIN, self)
    

class ValidatorScreenApp(App):

    def build(self):
        # t1 = threading.Thread(self.mainRunner())
        # t1.start()
        # t1.join()
        return ValidatorScreen(ScreenList.VALIDATOR, self)
    
class MainProgram():

    async def mainFunction(self, validatorBool):
        headLessBool = True
        browserPoolInit = BrowserPool()
        browserPoolVar = browserPoolInit.with_default_plugin(headless=headLessBool, browser_type='firefox', browser_new_context_options={"is_mobile": False})
        crawler = PlaywrightCrawler(
                                use_session_pool=True, 
                                max_session_rotations=0,
                                browser_pool=browserPoolVar,
                                session_pool=SessionPool(max_pool_size=1)
                                )

        @crawler.router.default_handler
        async def request_handler(context: PlaywrightCrawlingContext) -> None:
            print("test4")
            #set up variables
            url = context.request.url
            print(url)
            context.log.info(f'Processing {url}...')
            currentPage = context.page
            websiteProcessingNum = externalVariables.websiteProcessingNum

            #specific protocol for the amazon page since everything else is a diff page
            match websiteProcessingNum:
                case 1:
                    titleLocator = currentPage.locator('//*[@id="productTitle"]')
                    externalVariables.titleText = await titleLocator.inner_text()
                case _:
                    print("AAAAAAA bad stuff happened!!!")    
            print("test5")
        
        await crawler.run(['https://www.amazon.ca/dp/B0BBBTVFRD'])
        externalVariables.websiteProcessingNum = 2
        print("test 3")

        return
    
    
def mainRunner():
    mainClass = MainProgram()
    print("mainClass check?")
    asyncio.run(mainClass.mainFunction(validatorBool=True))



if __name__ == '__main__':
    mainScreen = MainScreenApp()
    valScreen = ValidatorScreenApp()
    t1 = threading.Thread(mainScreen.run())
    t1.start()
    t1.join()

    t3 = threading.Thread(mainRunner())
    t3.start()
    t3.join()

    t2 = threading.Thread(valScreen.run())
    t2.start()
    t2.join()

    # MainScreenApp().run()
    # ValidatorScreenApp().run()


