from typing import Union, TypeVar
from types import FunctionType

E = TypeVar("E")
T = TypeVar("T")
A = TypeVar("A")
ACT = Union[A, tuple[A]]
SELECTOR = Union[str, FP, SP, NP, list[str]]
ARGUMENT = Union[SELECTOR, T, E, list[T]]

class TEST:
	"""
	Этот объект используется для описания теста. Обязательно должен быть задан заголовок теста и последовательность команд которые будут выполнены при запуске теста.
	"""
	@classmethod
	def __getattr__(cls, attr: str) -> TEST:
		pass
	
	def __init__(self, *arg, **args):
		pass
	
	def __call__(self, *arg, **args) -> TEST:
		pass

class SCRIPT:
	"""
	Этот объект позволяет задать идентификатор последовательности команд для последующего переиспользования в тестах.
	Для скрипта может быть переопределён атрибут **fatality**.
	"""
	@classmethod
	def __getattr__(cls, attr: str) -> SCRIPT:
		pass
	
	def __init__(self, *arg, **args):
		pass

class STEP:
	"""
	Логически связанную последовательность команд можно объединить вместе при помощи объекта **STEP**. Это позволит детализировать выводимую в отчётах информацию.
	Для краткости можно указывать только заголовок шага, все команды находящиеся после него до следующего заголовка или конца теста будут рассматриваться как тело шага. 
	"""
	@classmethod
	def __getattr__(cls, attr: str) -> STEP:
		pass
	
	def __init__(self, *arg, **args):
		pass

class SCOPE:
	"""
	Для передачи данных между тестами можно создавать пользовательские пространства имён. Вызов объекта SCOPE возвращает ссылку на пространство имён доступное всем тестам. Это полезно для хранения данных которые используют тесты из разных файлов.
	Объект SCOPE может принимать текстовый параметр **space_id** который является идентификатором возвращаемого пространства имён.
	"""
	def __init__(self, name: str):
		pass
	
	def keys(self) -> tuple[str]:
		pass
	
	def values(self) -> tuple[T]:
		pass
	
	def clear(self):
		pass
	
	def __setattr__(self, item: str, val: T):
		pass
	
	def __getattr__(cls, attr: str) -> T:
		pass
	__getitem__ = SCOPE.__getattr__

class ARG(ARGUMENT):
	"""
	Перед запуском теста создаётся специальное пространство имён доступ к которому могут получить только команды выполняемые во время тестирования. Это пространство используется для обмена данными между скриптами, субтестами и основным тестом, а так же для параметризации тестов и скриптов.
	Непосредственный доступ к пространству внутри теста можно получить посредством команды **ARG** следующим образом:
	
	* **ARG("name", default=value)** - Возвращает значение ассоциированное с именем **name**. Если с именем **name** не было ассоциировано никаких данных, то возвращается значение определённое в атрибуте **default**.
	* **ARG("name", value)** - Устанавливает ассоциацию имени **name** со значением **value**
	* **ARG("name")** - Возвращает значение ассоциированное с именем **name**. Если с именем **name** не было ассоциировано никаких данных, то действие в котором использован аргумент будет пропущено. В режиме отладки пропущенные действия отображаются со статусом **SCIP**. Если необходимо пропустить не одно, а несколько действий, то можно воспользоваться условным оператором.
	"""
	def __init__(self, name: Union[str, int], value: T = None, default: T = None):
		pass

class LOOP:
	"""
	Выполнение действия заданное число раз
	
	Аргументы:
	* count - Натуральное число указывающие количество повторений действия
	* act - Команда или кортеж команд
	"""
	def __init__(self, count: int, act: ACT):
		pass

class WHILE:
	"""
	Выполнение действия пока выражение истинно.
	
	Аргументы:
	* expression - предикат, если значение истинно, то действие выполняется
	* act - Команда или кортеж команд
	* iteration - Максимальное число итераций. Если число итераций будет превышено, то тест завершится с ошибкой.
	"""
	def __init__(self, expression: E, act: ACT, iteration: int = 0):
		pass

class IF:
	"""
	Тернарный условный оператор.
	
	Аргументы:
	* expression - Если **выражение** истинно, то выполняется действие act1, а если ложно то действие act2. 
	* act1 - Команда или кортеж команд
	* act2 - Команда или кортеж команд
	"""
	def __init__(self, expression: E, act1: ACT, act2: ACT = None):
		pass

class STOP_TEST:
	"""
	Остановка теста. Статус теста устанавливается в **STOPPED**.
	
	Аргументы:
	* predicat - предикат, если значение истинно, то тест будет пропущен
	* message - Сообщение для отчётов
	"""
	def __init__(self, predicat: E = None, message: str = ""):
		pass

class SKIP_TEST:
	"""
	Пропуск теста. Статус теста устанавливается в **SKIPPED**, если текущий статус выше чем SKIPPED, то он не меняется.
	
	Аргументы:
	* predicat - предикат, если значение истинно, то тест будет пропущен
	* message - Сообщение для отчётов
	"""
	def __init__(self, predicat: E = None, message: str = ""):
		pass

class SKIP_STEP:
	"""
	Пропуск текущего шага. Статус теста не меняется.
	
	Аргументы:
	* predicat - предикат, если значение истинно, то шаг будет пропущен
	* message - Сообщение для отчётов
	"""
	def __init__(self, predicat: E = None, message: str = ""):
		pass

class FATAL:
	"""
	Вычисляет выражение, если его значение **False**, то тест завершится со статусом **FATAL**.
	К отчёту можно добавить текст сообщения об ошибки.
	
	Аргументы:
	* expression - Выражение в котором произошла ошибка
	* message    - пользовательское описание ошибки
	"""
	def __init__(self, expression: E = None, message: str = ""):
		pass

class ASSERT:
	"""
	Вычисляет выражение, если его значение **False**, то тест продолжится со статусом **FAILED**.
	К отчёту можно добавить текст сообщения об ошибки.
	
	Аргументы:
	* expression - Выражение в котором произошла ошибка
	* message    - пользовательское описание ошибки
	"""
	def __init__(self, expression: E = None, message: str = ""):
		pass

class WARNING:
	"""
	Выводит в отчёты переданные сообщения.
	Статус теста устанавливается в WARNING, если текущий статус выше чем WARNING, то он не меняется.
	"""
	def __init__(self, *arg: str):
		pass

class PRINT:
	"""
	Выводит в отчёты переданные сообщения. Статус теста не меняется, может быть использоваться для отладки теста.
	"""
	def __init__(self, *arg: str):
		pass

class PAUSE:
	"""
	Ничего не делает пока не истечёт время задержки.
	
	Аргументы:
	* time - Время паузы в секундах. *По умолчанию: 1.0*
	"""
	def __init__(self, time: float = 1.0):
		pass

class FP:
	"""
	Для определения пути к элементу находящемуся в фрейме нужно использовать специальный объект **FP** (Frame Pointer).
	Сначала ему нужно передать селектор фрейма (или нескольких фреймов в порядке вложенности) в котором находится элемент, а затем селектор самого элемента.
	"""
	def __init__(self, *select: SELECTOR, timeout: float):
		pass

class SP:
	"""
	Для элементов в теневом DOM тоже есть аналогичный объект, он называется **SP** (Shadow Pointer).
	Ему сначала передают селекторы контейнеров содержащих вложенный DOM, а затем селектор элемента.
	"""
	def __init__(self, *select: SELECTOR, timeout: float):
		pass

class NP(ARGUMENT):
	"""
	Для доступа к SCOPE-пространству имён данным из теста кроме ссылки на само пространство можно использовать специальный локатор - **"scope::space_id/name"** или объект **NP(space_id, name)**
	"""
	def __init__(self, domain: str, name: str = ""):
		pass

class EP:
	"""
	Указатель на элемент. Может использоваться с командами которым могут быть нужны элементы (например JS)
	"""
	def __init__(self, *selector: SELECTOR, timeout: float):
		pass

class Command:
	def __init__(self, call: FunctionType, *arg, **args):
		pass
	
	@property
	def warning(self) -> list[str]:
		"""
		информация о предупреждениях возникшая в результате выполнения шага
		["сообщение_1", "сообщение_2"]
		"""
	
	@warning.setter
	def warning(self, value: str):
		"""
		Добавляет предупреждение
		"""
	
	@property
	def message(self) -> list[str]:
		"""
		Возвращает список сообщений
		["сообщение_1", "сообщение_2"]
		"""
	
	@message.setter
	def message(self, value: str):
		"""
		Добавляет сообщение
		"""
	
	@property
	def screenshots(self) -> list[bytes]:
		"""
		Возвращает список скриншотов
		"""
	
	@screenshots.setter
	def screenshots(self, value: bytes):
		"""
		Добавляет скриншот
		"""
	
	def __call__(self, *arg, **args) -> T:
		pass

def command(call: FunctionType) -> Command:
	"""
	Декоратор команды
	
	@command
	def is_sort(browser, list):
		return ....
	...
	is_sort(ALL(...))
	"""
