from typing import Dict, List, Optional
from datetime import datetime
from analogai.foundation.modules.belief.belief import Belief
from analogai.foundation.modules.shared.statement_base import StatementBase
from analogai.foundation.modules.notion.notion import Notion
from analogai.foundation.core.value_objects.modifier import Modifier
from analogai.foundation.core.value_objects.relation import Relation
from analogai.foundation.common.utils.modifier_utils import ModifierBuilder
from analogai.foundation.common.enums.deontic_modality import DeonticModality
from .notion_storage_mapper import NotionStorageMapper
from .time_storage_mapper import TimeStorageMapper


def _relation_to_storage(relation: Relation) -> str:
    return relation.value


def _relation_from_storage(value) -> Relation:
    if value is None or value == "":
        return Relation.from_string("")
    return Relation.from_string(str(value))


class BeliefStorageMapper:
    @staticmethod
    def to_dict(belief: Belief) -> Dict:
        result = {
            "id": belief.id,
            "agent_id": belief.agent_id,
            "subject_notion": NotionStorageMapper.to_dict(belief.subject_notion),
            "complement_notion": NotionStorageMapper.to_dict(belief.complement_notion),
            "relation": _relation_to_storage(belief.relation),
            "statement_ids": [s.id for s in belief.unfiltered_statements],
            "created_at": belief.created_at.isoformat(),
            "updated_at": belief.updated_at.isoformat()
        }
        if belief.modifier is not None and belief.modifier.quantity is not None:
            result["quantity"] = belief.modifier.quantity
        if belief.location is not None:
            result["location"] = belief.location
        if belief.from_time is not None:
            result.update(TimeStorageMapper.flatten_to_dict(belief.from_time, "from_time"))
        if belief.to_time is not None:
            result.update(TimeStorageMapper.flatten_to_dict(belief.to_time, "to_time"))
        if belief.deontic_modality is not None:
            result["deontic_modality"] = belief.deontic_modality.value
        if belief.modifier and belief.modifier.attributes is not None:
            result["attributes"] = [
                {"tag": attribute.tag, "value": attribute.value, "unit": attribute.unit}
                for attribute in belief.modifier.attributes
            ]
        return result

    @staticmethod
    def from_dict(data: Dict, statements: Optional[List[StatementBase]] = None) -> Belief:
        created_at = datetime.fromisoformat(data["created_at"]) if data.get("created_at") else datetime.now()
        updated_at = datetime.fromisoformat(data["updated_at"]) if data.get("updated_at") else datetime.now()

        belief = Belief(
            id=data.get("id"),
            agent_id=data.get("agent_id", ""),
            created_at=created_at,
            updated_at=updated_at,
        )
        stored_updated_at = updated_at
        statements_list = statements or []
        if not statements_list:
            statement_ids = data.get("statement_ids", [])
            if statement_ids:
                raise ValueError(f"Belief {data.get('id')} has statement_ids {statement_ids} but no statements were provided")
        
        modifier = None
        from_time = TimeStorageMapper.unflatten_from_dict(data, "from_time")
        if not from_time and data.get("from_time"):
            from_time = TimeStorageMapper.from_dict(data.get("from_time"))
        to_time = TimeStorageMapper.unflatten_from_dict(data, "to_time")
        if not to_time and data.get("to_time"):
            to_time = TimeStorageMapper.from_dict(data.get("to_time"))
        location = data.get("location")
        deontic_modality = data.get("deontic_modality")
        
        attributes_payload = data.get("attributes")
        if attributes_payload is None:
            attributes_payload = data.get("measures")
        if any([location, from_time, to_time, deontic_modality]) or attributes_payload is not None or data.get("quantity") is not None:
            builder = ModifierBuilder()
            if location:
                builder.with_location(location)
            if from_time:
                builder.with_from_time(
                    year=from_time.year,
                    month=from_time.month,
                    day=from_time.day,
                    hour=from_time.hour,
                    minute=from_time.minute
                )
            if to_time:
                builder.with_to_time(
                    year=to_time.year,
                    month=to_time.month,
                    day=to_time.day,
                    hour=to_time.hour,
                    minute=to_time.minute
                )
            if deontic_modality:
                builder.with_deontic_modality(DeonticModality(deontic_modality))
            modifier = builder.build()
            if attributes_payload is not None:
                from analogai.foundation.core.value_objects.attribute import Attribute
                modifier.attributes = [
                    Attribute(tag=m.get("tag") or m.get("kind"), value=m.get("value"), unit=m.get("unit"))
                    for m in attributes_payload or []
                ]
            if data.get("quantity") is not None:
                modifier.quantity = data.get("quantity")
            belief._modifier = modifier
        
        # Add statements - validation will happen in add_statement if parameters don't match
        for statement in statements_list:
            belief.add_statement(statement)
        if not belief.statements:
            raise ValueError(f"Belief {belief.id} must have at least one statement")
        belief.updated_at = stored_updated_at
        return belief

