from typing import Optional, Tuple, cast

import axipy
from PySide2.QtGui import QDoubleValidator, QValidator
from PySide2.QtWidgets import QLineEdit, QWidget


class _CustomDoubleValidator(QDoubleValidator):

    def __init__(self, line_edit: QLineEdit) -> None:
        super().__init__()
        self.__line_edit = line_edit
        self.__last_acceptable_text = "0"

        self.setNotation(QDoubleValidator.ScientificNotation)

    def __is_valid_float_coord(self, value: str) -> bool:
        try:
            axipy.FloatCoord(value)
        except ValueError:
            return False
        return True

    def validate(self, text: str, position: int) -> QValidator.State:
        # TODO: Fix PySide2 stubs
        result = super().validate(text, position)
        if cast(Tuple[QValidator.State, str, int], result)[0] == QValidator.State.Acceptable:
            if not self.__is_valid_float_coord(text):
                return QValidator.Invalid
            self.__last_acceptable_text = text

        return result

    def fixup(self, _text: str) -> None:
        self.__line_edit.setText(self.__last_acceptable_text)


class _NumberLineEdit(QLineEdit):
    """
    QLineEdit, принимающий только корректные числа (включая научную нотацию).
    При завершении редактирования (Enter / потеря фокуса) проверяет ввод:
      - Если ввод валиден → запоминает его как последнее валидное значение.
      - Если невалиден → восстанавливает последнее валидное значение.
    """

    def __init__(
        self,
        parent: Optional[QWidget] = None,
    ):
        super().__init__(parent)
        self.setValidator(_CustomDoubleValidator(self))

    def validator(self) -> _CustomDoubleValidator:
        return cast(_CustomDoubleValidator, super().validator())
