import os
import re
import time
from contextlib import suppress
from pathlib import Path
from typing import List, Literal

from pywinauto import Application
from pywinauto.application import WindowSpecification
from pywinauto.timings import wait_until_passes
from selenium.common.exceptions import (ElementClickInterceptedException,
                                        StaleElementReferenceException,
                                        TimeoutException)
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

from nsk.bot.base import IBot
from nsk.utils import retry_if_exception


class MailDealer(IBot):
    def __init__(self, url: str, username: str, password: str, **kwargs):
        super().__init__(**kwargs)
        self.url = url
        self.authenticated = self._authentication(username, password)
        if not self.authenticated:
            raise ConnectionRefusedError("The username or password is incorrect.")
    @retry_if_exception(
        failure_return=False
    )
    def _authentication(self,username:str,password:str) -> bool:
        self.navigate(self.url)
        self.wait.until(
            method = EC.element_to_be_clickable((By.ID, "fUName")),
            message = "Not Found Username Field",
        ).send_keys(username)
        self.wait.until(
            method = EC.element_to_be_clickable((By.ID, "fPassword")),
            message="Not Found Password Field",
        ).send_keys(password)
        self.wait.until(
            method=EC.element_to_be_clickable((By.CSS_SELECTOR, "input[value='ログイン']")),
            message="Not Found Login Button",
        ).click()
        time.sleep(self.retry_interval)
        while self.browser.execute_script("return document.readyState") != "complete":
            time.sleep(self.retry_interval)
        with suppress(TimeoutException):
            error = self.wait.until(
                EC.presence_of_element_located(
                    (By.CSS_SELECTOR, "div[class='d_error_area ']"),
                ),
            )
            self.logger.error(error.text)
            return False
        with suppress(TimeoutException):
            olv_dialog = self.wait.until(
                EC.presence_of_element_located((By.CSS_SELECTOR,"div[class='olv-dialog']"))
            )
            time.sleep(self.retry_interval)
            for button in olv_dialog.find_elements(By.TAG_NAME,'button'):
                if button.text.find("同意する") != -1:
                    self.wait.until(
                        EC.element_to_be_clickable(button)
                    ).click()
                    break
        if self.browser.current_url.find("app") != -1:
            self.logger.info("Authenticated")
            return True
        return True

    @retry_if_exception(
        exceptions=(
            ElementClickInterceptedException,
            StaleElementReferenceException,
            TimeoutException,
        ),
        failure_return=False
    )
    def send_mail(self,fr:str,to:str,subject:str,content:str,attachments:List[str] = [],mode:Literal["送信確認","承認依頼確認","一時保存"] = "一時保存") -> bool:
        for window in self.browser.window_handles:
            if window != self.root_window:
                self.browser.switch_to.window(window)
                self.browser.close()
        self.browser.switch_to.window(self.root_window)
        self.logger.info(f"Send mail from {fr} to {to} (subject: {subject})")
        self.wait.until(
            method = EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, "iframe[id='ifmSide']")),
            message = "Not Found Side Frame",
        )
        self.wait.until(
            method = EC.element_to_be_clickable((By.CSS_SELECTOR, "span[title='メール作成']")),
            message="Not Found Create Mail Button",
        ).click()
        while len(self.browser.window_handles) == 1:
            continue
        new_window = self.browser.window_handles[-1]
        self.browser.switch_to.window(new_window)
        self.browser.maximize_window()
        while self.browser.execute_script("return document.readyState") != "complete":
            time.sleep(self.retry_interval)
        self.wait.until(
            EC.element_to_be_clickable((By.XPATH, "//button[text()='次へ']"))
        ).click()
        # From
        fr_input = self.wait.until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, "input[name='fFrom']"))
        )
        fr_input.clear()
        fr_input.send_keys(fr)
        # To
        to_input = self.wait.until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, "input[name='fTo[]']"))
        )
        to_input.clear()
        to_input.send_keys(to)
        # Subject
        subject_input = self.wait.until(
            EC.element_to_be_clickable((By.CSS_SELECTOR,"input[id='fSubject']"))
        )
        subject_input.clear()
        subject_input.send_keys(subject)
        # Body
        self.wait.until(
            EC.frame_to_be_available_and_switch_to_it((By.TAG_NAME, "iframe"))
        )
        body = self.wait.until(EC.presence_of_element_located((By.TAG_NAME, "body")))
        self.browser.execute_script(
            "arguments[0].innerText = arguments[1];", body, content
        )
        self.browser.switch_to.default_content()
        for attachment in attachments:
            # Check
            if not os.path.exists(attachment):
                raise FileNotFoundError(f"Attachment Not Found: {attachment}")
            # Attachment
            self.wait.until(
                method = EC.element_to_be_clickable((By.CSS_SELECTOR, "button[title='添付ファイル']")),
                message="Not Found Attachment Button",
            ).click()
            file_uploader: WindowSpecification = wait_until_passes(
                timeout=self.timeout,
                retry_interval=self.retry_interval,
                func=lambda: Application()
                .connect(title="Open")
                .window(title="Open"),
            )
            wait_until_passes(
                timeout=self.timeout,
                retry_interval=self.retry_interval,
                func=lambda dialog=file_uploader: dialog 
                .child_window(class_name="ComboBoxEx32")
                .child_window(class_name="Edit")
                .wrapper_object(),
            ).type_keys(re.sub(r"([{}^%~()])", r"{\1}", str(Path(attachment))),with_spaces=True)
            wait_until_passes(
                timeout=self.timeout,
                retry_interval=1,
                func=lambda dialog=file_uploader: dialog.child_window(
                    title="&Open", class_name="Button"
                ).wrapper_object(),
            ).click_input()
            for button in self.wait.until(
                method = EC.presence_of_all_elements_located((By.CSS_SELECTOR,"button[class='accent-btn__btn']")),
                message="Not Found Send Button"
            ):
                if button.text.find(mode) != -1:
                    self.browser.execute_script(
                        "arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});",
                        button,
                    )
                    time.sleep(1)
                    self.wait.until(EC.element_to_be_clickable(button)).click()
                    time.sleep(1)
                    snackbar = self.wait.until(
                        method = EC.presence_of_element_located((By.CSS_SELECTOR, "div[class='snackbar__msg']")),
                        message= "Snackbar Not Found ", 
                    )
                    self.logger.info(snackbar.text)
                    break
        self.browser.close()
        self.browser.switch_to.window(self.root_window)
        self.logger.info("Send mail successfully")
        return True
