from __future__ import annotations

from typing import Iterable, List, Tuple

from analogai.deepthink.application.usecases.learning.learn_statement.models import Attribute


class StatementAttributeHelper:
    def get_subject_attributes(self, subject) -> list:
        if subject is None:
            return []
        try:
            return subject.attributes or []
        except AttributeError:
            return []

    def build_attributes(self, parsed_attributes: Iterable, subject_attributes: Iterable) -> List[Attribute]:
        if parsed_attributes:
            return self._build_from_parsed(parsed_attributes)
        if subject_attributes:
            return self._build_from_subject(subject_attributes)
        return []

    def has_numeric_attribute_values(self, attributes: Iterable) -> bool:
        if not attributes:
            return False
        for attribute in attributes:
            try:
                value = attribute.value
            except AttributeError:
                value = None
            if value is None:
                continue
            try:
                float(value)
                return True
            except (TypeError, ValueError):
                continue
        return False

    def extract_quantity_from_caption(self, caption: str) -> Tuple[str, float | None]:
        if not caption:
            return caption, None
        tokens = caption.split()
        if not tokens or not tokens[0].isdigit():
            return caption, None
        try:
            q = float(tokens[0])
            rest = " ".join(tokens[1:]).strip()
            if rest and rest.endswith("s") and not rest.endswith("ss"):
                rest = rest[:-1]
            return rest, q
        except (ValueError, TypeError):
            return caption, None

    def _build_from_parsed(self, parsed_attributes: Iterable) -> List[Attribute]:
        attributes: List[Attribute] = []
        for attr in parsed_attributes:
            try:
                values_source = attr.values
            except AttributeError:
                values_source = []
            values = self._coerce_float_values(values_source)
            try:
                tag = attr.tag
            except AttributeError:
                tag = None
            try:
                unit = attr.unit
            except AttributeError:
                unit = None
            try:
                min_value = attr.min_value
            except AttributeError:
                min_value = None
            try:
                max_value = attr.max_value
            except AttributeError:
                max_value = None
            attributes.append(
                Attribute(
                    tag=tag,
                    values=values,
                    unit=unit,
                    min_value=min_value,
                    max_value=max_value,
                )
            )
        return attributes

    def _build_from_subject(self, subject_attributes: Iterable) -> List[Attribute]:
        attributes: List[Attribute] = []
        for attribute in subject_attributes:
            try:
                raw_value = attribute.value
            except AttributeError:
                raw_value = None
            values = self._coerce_float_values([raw_value])
            try:
                unit = attribute.unit
            except AttributeError:
                unit = None
            if not values and not unit:
                continue
            try:
                tag = attribute.tag
            except AttributeError:
                tag = None
            attributes.append(
                Attribute(
                    tag=tag,
                    values=values,
                    unit=unit,
                )
            )
        return attributes

    def _coerce_float_values(self, values: Iterable) -> list[float]:
        if not values:
            return []
        coerced: list[float] = []
        for value in values:
            try:
                coerced.append(float(value))
            except (TypeError, ValueError):
                continue
        return coerced
