from . import enums, exception
from .utils import Utils
import json, os, logging
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException

logger = logging.getLogger(__name__)
 
class YoutubeUpload:
    URL = "https://youtube.com"
    action_step = 0
    MAX_ACTION_POINT = 100
    TOTAL_NUMBER_OF_ACTION = 4
    
    def __init__(self, cookie_path, upload_info: dict, headless=False):
        self.cookie_path = cookie_path    
        self.headless = headless
        self.upload_info = upload_info
        
        self.__validate()
        self.__set_up()
        self.__preload_site()
        self.__load_site_with_cookie()
        Utils.big_wait()
    
    def __validate(self):
        self.__validate_upload_info()
        self.__validate_with_enums()
        self.__validate_file_path()
        
    def __validate_with_enums(self):
        fields = (("category", enums.Category), ("privacy", enums.Privacy))
        for field in fields:
            if self.upload_info.get(field[0]) not in field[1].values():
                raise ValueError("Invalid %s field: %s" %(field[0], field[1].values()))
        
    def __validate_upload_info(self):
        required_fields = ("title", "video", "privacy")
        for field in required_fields:
            if field not in self.upload_info:
                raise ValueError("%s not present in upload_info" % field )
            
    def __validate_file_path(self):
        files = [{"name": "video", "file": self.upload_info.get("video"), "required": True}, {"name": "thumbnail", "file": self.upload_info.get("thumbnail"), "required": False}]
        for file_path in files:
            if not os.path.exists(file_path.get("file")):
                if file_path.get("required"):
                    raise ValueError("%s path '%s' does not exist" % (file_path.get("name"), file_path.get("file")))
                
        
    def __set_up(self):
        chrome_path = ChromeDriverManager().install()
        chrome_service = Service(chrome_path)

        chrome_options = webdriver.ChromeOptions()
        
        if self.headless:
            chrome_options.add_argument('--headless')  # Run chrome in headless mode
            chrome_options.add_argument('--no-sandbox')
            chrome_options.add_argument('--disable-dev-shm-usage')

        # Set the path to chromedriver
        self.driver = webdriver.Chrome(options=chrome_options, service=chrome_service)
        self.wait = WebDriverWait(self.driver, 10)
        self.ignored_wait = WebDriverWait(self.driver, 10, ignored_exceptions=[TimeoutException])
        
    def __preload_site(self):
        # Define the URL you want to visit
        self.driver.get(self.URL)
        # delete the current cookies
        self.driver.delete_all_cookies()
                
    def __load_site_with_cookie(self):
        # Add cookies to the browser instance
        with open("cookies.json", "r") as file: 
            cookies = json.load(file)
            
        
        for cookie in cookies:
            if 'sameSite' in cookie:
                if cookie['sameSite'] != 'Strict' or cookie['sameSite'] == 'None':
                    cookie['sameSite'] = 'Strict'
                    
            if cookie.get('expirationDate'): 
                cookie['expirationDate'] = 3333333333
            
            self.driver.add_cookie(cookie)

        # Open the URL with the added cookies
        self.driver.get(self.URL)
    
    def __click_and_wait(self, element ,small_wait=True):
        element.click()
        if small_wait:
            Utils.small_wait()
            return
        Utils.big_wait()
        Utils.console_loader(self.__action_point)
    
    def __send_keys_and_wait(self, element, text, small_wait=True, clear=False):
        if clear:
            element.clear()
        element.send_keys(text)
        if small_wait:
            Utils.small_wait()
            return
        Utils.big_wait()
        Utils.console_loader(self.__action_point)
    
    @property   
    def __action_point(self):
        self.action_step += 1
        return self.action_step * (self.MAX_ACTION_POINT / self.TOTAL_NUMBER_OF_ACTION)
        
    def _go_to_studio(self):
        # Click Avatar
        self.__click_and_wait(self.wait.until(EC.presence_of_element_located((By.ID, "img"))))
        try:
            # Click Studio Button
            self.__click_and_wait(self.wait.until(EC.presence_of_element_located((By.XPATH, "//*[text()='YouTube Studio']"))))
        except Exception as e:
            error = "cookie expired; grab another cookie"
            logging.error(error)
            raise exception.CookieTimeOutError(error)
        
    def _create(self):
        # Click Create Button
        self.__click_and_wait(self.wait.until(EC.presence_of_element_located((By.XPATH, "//div[text()='Create']"))))
        # Click Upload video button
        self.__click_and_wait(self.wait.until(EC.presence_of_element_located((By.XPATH, "//*[text()='Upload videos']"))))
        # Upload the file        
        self.__send_keys_and_wait(self.wait.until(EC.presence_of_element_located((By.XPATH, "//input[@type='file']"))), self.upload_info.get("video"))

    def _next(self):
        self.__click_and_wait(self.wait.until(EC.presence_of_element_located((By.XPATH, "//div[text()='Next']"))))
    
    def _fill_in_info(self):
        # title
        self.__send_keys_and_wait(self.wait.until(EC.presence_of_element_located((By.ID, "textbox"))), self.upload_info.get("title"), clear=True)
        try:
            # description
            if self.upload_info.get("description"):
                self.__send_keys_and_wait(self.driver.execute_script(f"return document.querySelectorAll('{enums.ElementsPath.DESCRIPTION_QUERY_SELECTOR.value}')[{enums.ElementsPath.DESCRIPTION_INDEX.value}]"), self.upload_info.get("description"))
        except Exception as e:
            logging.error("inserting description error %s" %e)
        
        # Thumbnail
        if self.upload_info.get("thumbnail"):
            self.__send_keys_and_wait(self.wait.until(EC.presence_of_element_located((By.XPATH, "//input[@type='file']"))), self.upload_info["thumbnail"])
        
        # Show more
        self.__click_and_wait(self.wait.until(EC.presence_of_element_located((By.XPATH, "//div[text()='Show more']"))), small_wait=False)
        
    def _publish(self):
        # Select privacy
        privacy = self.upload_info.get("privacy")
        self.__click_and_wait(self.wait.until(EC.presence_of_element_located((By.XPATH, f"//div[text()='{privacy}']"))))
        # if public instant premier
        if privacy == enums.Privacy.PUBLIC.value:
            self.__click_and_wait(self.wait.until(EC.presence_of_element_located((By.XPATH, "//div[text()='Set as instant Premiere']"))))
        
        # Click public
        self.__click_and_wait(self.wait.until(EC.presence_of_element_located((By.XPATH, "//div[text()='Publish']"))))
        
    def _fill_in_other_info(self):
        if self.upload_info.get("tags"):
            tags = self.upload_info["tags"]
            if isinstance(tags, list):
                tags = ", ".join(tags) 
            
            try:    
                self.__send_keys_and_wait(self.ignored_wait.until(EC.presence_of_element_located((By.ID, "text-input"))), tags)
            except Exception as e:
                logging.error("Error Uploading tags error %s" %e)
                
        # if self.upload_info.get("category"):
        #     try:    
        #         self.__click_and_wait(self.driver.execute_script("""
        #                     let category = document.getElementsByClassName('left-container style-scope ytcp-dropdown-trigger')[6]
        #                     category.scrollIntoView({ behavior: "smooth", block: "center", inline: "center" });
        #                     return category
        #             """))
        #         print(self.upload_info['category'])
        #         self.__click_and_wait(self.ignored_wait.until(EC.presence_of_element_located((By.XPATH, f"//*[text()='{self.upload_info['category']}']"))))
        #     except Exception as e:
        #         logging.critical("inserting category error %s" %e)
        
        # Select Kid's privacy
        kids = self.upload_info.get("kids", False)
        if kids:
            self.__click_and_wait(self.wait.until(EC.presence_of_element_located((By.ID, "radioLabel"))))
        else:
            self.__click_and_wait(self.driver.execute_script(f"return document.getElementsByClassName('{enums.ElementsPath.YES_KIDS_CLASS.value}')[{enums.ElementsPath.YES_KIDS_INDEX.value}]"))
    
    def upload(self):
        self._go_to_studio()
        self._create()
        self._fill_in_info()
        self._fill_in_other_info()
        
        for _ in range(3):
            self._next()
            
        self._publish()
        
        
        