# pylint: disable=too-few-public-methods
"""Utils methods for pytest-dash such wait_for wrappers."""

import time
import logging
from selenium.common.exceptions import WebDriverException
from selenium.webdriver.common.by import By
from dash.testing.errors import TestingTimeoutError


logger = logging.getLogger(__name__)


def until(
    wait_cond, timeout, poll=0.1, msg="expected condition not met within timeout"
):  # noqa: C0330
    res = wait_cond()
    logger.debug(
        "start wait.until with method, timeout, poll => %s %s %s",
        wait_cond,
        timeout,
        poll,
    )
    end_time = time.time() + timeout
    while not res:
        if time.time() > end_time:
            raise TestingTimeoutError(msg)
        time.sleep(poll)
        res = wait_cond()
        logger.debug("poll => %s", time.time())

    return res


def until_not(
    wait_cond, timeout, poll=0.1, msg="expected condition met within timeout"
):  # noqa: C0330
    res = wait_cond()
    logger.debug(
        "start wait.until_not method, timeout, poll => %s %s %s",
        wait_cond,
        timeout,
        poll,
    )
    end_time = time.time() + timeout
    while res:
        if time.time() > end_time:
            raise TestingTimeoutError(msg)
        time.sleep(poll)
        res = wait_cond()
        logger.debug("poll => %s", time.time())

    return res


class contains_text:
    def __init__(self, selector, text, timeout):
        self.selector = selector
        self.text = text
        self.timeout = timeout

    def __call__(self, driver):
        try:
            elem = driver.find_element(By.CSS_SELECTOR, self.selector)
            logger.debug("contains text {%s} => expected %s", elem.text, self.text)
            value = elem.get_attribute("value")
            return self.text in str(elem.text) or (
                value is not None and self.text in str(value)
            )
        except WebDriverException:
            return False

    def message(self, driver):
        try:
            element = self._get_element(driver)
            text = "found: " + str(element.text) or str(element.get_attribute("value"))
        except WebDriverException:
            text = f"{self.selector} not found"
        return f"text -> {self.text} not found inside element within {self.timeout}s, {text}"

    def _get_element(self, driver):
        return driver.find_element(By.CSS_SELECTOR, self.selector)


class contains_class:
    def __init__(self, selector, classname):
        self.selector = selector
        self.classname = classname

    def __call__(self, driver):
        try:
            elem = driver.find_element(By.CSS_SELECTOR, self.selector)
            classname = elem.get_attribute("class")
            logger.debug(
                "contains class {%s} => expected %s", classname, self.classname
            )
            return self.classname in str(classname).split(" ")
        except WebDriverException:
            return False


class text_to_equal:
    def __init__(self, selector, text, timeout):
        self.selector = selector
        self.text = text
        self.timeout = timeout

    def __call__(self, driver):
        try:
            elem = self._get_element(driver)
            logger.debug("text to equal {%s} => expected %s", elem.text, self.text)
            value = elem.get_attribute("value")
            return str(elem.text) == self.text or (
                value is not None and str(value) == self.text
            )
        except WebDriverException:
            return False

    def message(self, driver):
        try:
            element = self._get_element(driver)
            text = "found: " + str(element.text) or str(element.get_attribute("value"))
        except WebDriverException:
            text = f"{self.selector} not found"
        return f"text -> {self.text} not found within {self.timeout}s, {text}"

    def _get_element(self, driver):
        return driver.find_element(By.CSS_SELECTOR, self.selector)


class style_to_equal:
    def __init__(self, selector, style, val):
        self.selector = selector
        self.style = style
        self.val = val

    def __call__(self, driver):
        try:
            elem = driver.find_element(By.CSS_SELECTOR, self.selector)
            val = elem.value_of_css_property(self.style)
            logger.debug("style to equal {%s} => expected %s", val, self.val)
            return val == self.val
        except WebDriverException:
            return False


class class_to_equal:
    def __init__(self, selector, classname):
        self.selector = selector
        self.classname = classname

    def __call__(self, driver):
        try:
            elem = driver.find_element(By.CSS_SELECTOR, self.selector)
            classname = elem.get_attribute("class")
            logger.debug(
                "class to equal {%s} => expected %s", classname, self.classname
            )
            return str(classname) == self.classname
        except WebDriverException:
            return False
