from __future__ import annotations

from typing import Optional, Any, Dict

from analogai.foundation.common.enums.criterion import Criterion
from analogai.foundation.common.enums.deontic_modality import DeonticModality
from analogai.foundation.common.utils.modifier_utils import ModifierBuilder
from analogai.foundation.core.value_objects.attribute import Attribute
from analogai.foundation.core.value_objects.modifier import Modifier
from analogai.foundation.core.value_objects.time import Time


def is_plural_subject(caption: str) -> bool:
    if caption is None:
        return False
    normalized = str(caption).strip().lower()
    if not normalized:
        return False
    if " and " in normalized or " & " in normalized or "," in normalized:
        return True
    tokens = normalized.split()
    if not tokens:
        return False
    if tokens[0].isdigit():
        try:
            return float(tokens[0]) != 1
        except ValueError:
            pass
    plural_pronouns = {"we", "they", "you", "these", "those", "people", "children", "men", "women", "data"}
    if tokens[0] in plural_pronouns:
        return True
    if tokens[0] in {"a", "an", "the"} and len(tokens) > 1:
        token = tokens[1]
    else:
        token = tokens[-1]
    if token in {"news", "series", "species", "socrates"}:
        return False
    if token.endswith("s") and not token.endswith(("ss", "us", "is")):
        return True
    return False


def normalize_timezone(timezone: Optional[str]) -> Optional[str]:
    if timezone is None:
        return None
    timezone = timezone.strip()
    return timezone if timezone else None


def normalize_time(value: object) -> Optional[Time]:
    if value is None:
        return None
    if isinstance(value, Time):
        return value
    if isinstance(value, dict):
        return Time(
            year=value.get("year"),
            month=value.get("month"),
            day=value.get("day"),
            hour=value.get("hour"),
            minute=value.get("minute"),
        )
    return None


def normalize_deontic(value: object) -> Optional[DeonticModality]:
    if value is None:
        return None
    if isinstance(value, DeonticModality):
        return value
    try:
        return DeonticModality(value)
    except (ValueError, TypeError):
        return None


def normalize_attributes(value: object) -> Optional[list[Attribute]]:
    if value is None:
        return None
    if isinstance(value, list):
        measures: list[Attribute] = []
        for item in value:
            if isinstance(item, Attribute):
                measures.append(item)
                continue
            if isinstance(item, dict):
                tag = item.get("tag") or item.get("kind")
                if tag is None:
                    continue
                val = item.get("value")
                if val is None:
                    val = item.get("min_value")
                if val is None:
                    val = item.get("max_value")
                measures.append(Attribute(tag=str(tag), value=val, unit=item.get("unit")))
        return measures
    return None


def modifier_from_criteria(criteria: Dict[Criterion, Any]) -> Optional[Modifier]:
    if not criteria:
        return None
    builder = ModifierBuilder()
    location = criteria.get(Criterion.Location)
    if location is not None:
        builder.with_location(location)
    deontic = normalize_deontic(criteria.get(Criterion.DeonticModality))
    if deontic is not None:
        builder.with_deontic_modality(deontic)
    from_time = normalize_time(criteria.get(Criterion.FromTime))
    if from_time is not None:
        builder.with_from_time(
            year=from_time.year,
            month=from_time.month,
            day=from_time.day,
            hour=from_time.hour,
            minute=from_time.minute,
        )
    to_time = normalize_time(criteria.get(Criterion.ToTime))
    if to_time is not None:
        builder.with_to_time(
            year=to_time.year,
            month=to_time.month,
            day=to_time.day,
            hour=to_time.hour,
            minute=to_time.minute,
        )
    attributes = normalize_attributes(criteria.get(Criterion.AttributeValue))
    if attributes is not None:
        builder.with_attributes(attributes)
    quantity = criteria.get(Criterion.Quantity)
    if quantity is not None:
        builder.with_quantity(quantity)
    modifier = builder.build()
    if (
        modifier.location is None
        and modifier.deontic_modality is None
        and modifier.from_time is None
        and modifier.to_time is None
        and not modifier.attributes
        and modifier.quantity is None
    ):
        return None
    return modifier
