from typing import Optional, Protocol
from datetime import datetime
from analogai.foundation.core.value_objects.modifier import Modifier
from analogai.foundation.core.value_objects.time import Time


def _to_absolute_time(time: Time) -> Optional[Time]:
    return time


class ModalContextHolder(Protocol):
    location: Optional[str]
    from_time: Optional[Time]
    to_time: Optional[Time]
    deontic_modality: Optional[object]
    quantity: Optional[float]


class ModifierMatcher:
    def matches(
        self,
        statement: ModalContextHolder,
        context: Modifier,
        reference_time: Optional[datetime] = None,
    ) -> bool:
        if context.location is not None and statement.location != context.location:
            return False
        
        if context.from_time is not None and not self._matches_time(statement.from_time, context.from_time, reference_time):
            return False
        
        if context.to_time is not None and not self._matches_time(statement.to_time, context.to_time, reference_time):
            return False
        
        if context.deontic_modality is not None:
            if statement.deontic_modality is None:
                return False
            if statement.deontic_modality != context.deontic_modality:
                return False

        if context.quantity is not None and statement.quantity != context.quantity:
            return False
        
        return True
    
    def _matches_time(self, statement_time: Optional[Time], context_time: Time, reference_time: Optional[datetime] = None) -> bool:
        if statement_time is None:
            return False
        
        resolved_statement = _to_absolute_time(statement_time)
        resolved_context = _to_absolute_time(context_time)
        
        if resolved_statement is None or resolved_context is None:
            return False
        
        if resolved_context.year is not None and resolved_statement.year != resolved_context.year:
            return False
        
        if resolved_context.month is not None and resolved_statement.month != resolved_context.month:
            return False
        
        if resolved_context.day is not None and resolved_statement.day != resolved_context.day:
            return False
        
        if resolved_context.hour is not None and resolved_statement.hour != resolved_context.hour:
            return False
        
        if resolved_context.minute is not None and resolved_statement.minute != resolved_context.minute:
            return False
        
        return True

    def exact_matches(
        self,
        statement: ModalContextHolder,
        context: Modifier,
        reference_time: Optional[datetime] = None,
    ) -> bool:
        if (context.location is None) != (statement.location is None):
            return False
        if context.location is not None and statement.location != context.location:
            return False
        if (context.from_time is None) != (statement.from_time is None):
            return False
        if context.from_time is not None and not self._matches_time(
            statement.from_time, context.from_time, reference_time
        ):
            return False
        if (context.to_time is None) != (statement.to_time is None):
            return False
        if context.to_time is not None and not self._matches_time(
            statement.to_time, context.to_time, reference_time
        ):
            return False
        if (context.deontic_modality is None) != (statement.deontic_modality is None):
            return False
        if (
            context.deontic_modality is not None
            and statement.deontic_modality != context.deontic_modality
        ):
            return False
        if (context.quantity is None) != (statement.quantity is None):
            return False
        if context.quantity is not None and statement.quantity != context.quantity:
            return False
        return True

    def context_matches(
        self,
        stored: Optional["Modifier"],
        query: Optional["Modifier"],
        reference_time: Optional[datetime] = None,
    ) -> bool:
        if query is None:
            return True
        if stored is None:
            return False
        stub = type("_Stub", (), {
            "location": stored.location,
            "from_time": stored.from_time,
            "to_time": stored.to_time,
            "deontic_modality": stored.deontic_modality,
            "quantity": stored.quantity,
        })()
        return self.matches(stub, query, reference_time)

    def context_exact_matches(
        self,
        stored: Optional["Modifier"],
        query: Optional["Modifier"],
        reference_time: Optional[datetime] = None,
    ) -> bool:
        if query is None and stored is None:
            return True
        if query is None or stored is None:
            return False
        stub = type("_Stub", (), {
            "location": stored.location,
            "from_time": stored.from_time,
            "to_time": stored.to_time,
            "deontic_modality": stored.deontic_modality,
            "quantity": stored.quantity,
        })()
        return self.exact_matches(stub, query, reference_time)
