"""
game_mapper.py — Game Decision → Soul Dimension Mapper

Translates player actions/choices in games into soul dimension data:
- Decision actions → Soul dimensions (identity/value/emotion/cognition/relationship/creation/mission)
- Choice patterns → Decision tags (risk/ethics/trust/aesthetic/...)
- Behavioral context → Emotion inference
- Game type → Weight calculation

Design principles:
· Game data source_engine='game_inference' or 'game_explicit'
· Explicit choices (dialogue options) have higher weight; behavioral inference has lower weight
· Built-in default mappings + game custom mappings overlay
"""

import json
from pathlib import Path

# ── Default action → dimension mapping ─────────────────────────

DEFAULT_ACTION_MAP = {
    # Moral/value choices
    "moral_choice":     {"dimension": "value",    "tags": ["ethics"],    "weight": 0.7},
    "ethical_dilemma":  {"dimension": "value",    "tags": ["ethics"],    "weight": 0.75},
    "sacrifice":        {"dimension": "value",    "tags": ["ethics", "meaning-first"], "weight": 0.8},
    "betrayal":         {"dimension": "value",    "tags": ["trust"],     "weight": 0.7},
    "loyalty":          {"dimension": "value",    "tags": ["trust"],     "weight": 0.7},

    # Risk/strategy
    "risk_decision":    {"dimension": "cognition", "tags": ["risk"],     "weight": 0.6},
    "strategy":         {"dimension": "cognition", "tags": ["risk"],     "weight": 0.55},
    "resource_trade":   {"dimension": "cognition", "tags": ["risk"],     "weight": 0.5},

    # Social/relationship
    "dialogue_choice":  {"dimension": "relationship", "tags": ["trust"], "weight": 0.65},
    "alliance":         {"dimension": "relationship", "tags": ["trust"], "weight": 0.6},
    "conflict":         {"dimension": "relationship", "tags": ["trust"], "weight": 0.65},
    "romance":          {"dimension": "relationship", "tags": ["trust", "identity"], "weight": 0.6},

    # Identity/role
    "character_build":  {"dimension": "identity", "tags": ["identity"],  "weight": 0.65},
    "faction_choice":   {"dimension": "identity", "tags": ["identity"],  "weight": 0.7},
    "self_expression":  {"dimension": "identity", "tags": ["aesthetic"], "weight": 0.6},
    "name_choice":      {"dimension": "identity", "tags": ["identity"],  "weight": 0.5},

    # Creation
    "build":            {"dimension": "creation", "tags": ["aesthetic"],  "weight": 0.5},
    "design":           {"dimension": "creation", "tags": ["aesthetic"],  "weight": 0.55},
    "craft":            {"dimension": "creation", "tags": ["aesthetic"],  "weight": 0.5},

    # Exploration/goals
    "exploration":      {"dimension": "mission",  "tags": ["intuition"], "weight": 0.45},
    "quest_choice":     {"dimension": "mission",  "tags": ["meaning-first"], "weight": 0.6},
    "ending_choice":    {"dimension": "mission",  "tags": ["meaning-first"], "weight": 0.75},

    # Emotional response
    "emotional_response": {"dimension": "emotion", "tags": ["intuition"], "weight": 0.6},
    "reaction":         {"dimension": "emotion",  "tags": ["intuition"], "weight": 0.55},
}

# ── Choice pattern → emotion inference ─────────────────────────

CHOICE_EMOTION_HINTS = {
    # Positive
    "help":       {"emotion": "warmth", "valence": 0.6},
    "save":       {"emotion": "resolve", "valence": 0.5},
    "protect":    {"emotion": "resolve", "valence": 0.5},
    "forgive":    {"emotion": "relief", "valence": 0.4},
    "share":      {"emotion": "contentment", "valence": 0.5},
    "cooperate":  {"emotion": "trust", "valence": 0.4},

    # Negative
    "kill":       {"emotion": "conflicted", "valence": -0.3},
    "steal":      {"emotion": "ambivalent", "valence": -0.2},
    "abandon":    {"emotion": "helpless", "valence": -0.4},
    "betray":     {"emotion": "ambivalent", "valence": -0.5},
    "destroy":    {"emotion": "ruthless", "valence": -0.3},
    "reject":     {"emotion": "calm", "valence": -0.1},

    # Neutral/strategic
    "trade":      {"emotion": "rational", "valence": 0.1},
    "negotiate":  {"emotion": "focused", "valence": 0.2},
    "explore":    {"emotion": "curious", "valence": 0.4},
    "retreat":    {"emotion": "cautious", "valence": 0.0},
    "wait":       {"emotion": "patient", "valence": 0.1},
}


def map_decision(action: str, choice: str = "", context: str = "",
                 custom_mappings: dict = None) -> dict:
    """
    Map a game decision to soul dimension data.

    Args:
        action: Action type (moral_choice/risk_decision/dialogue_choice/...)
        choice: Player's specific choice ("saved the villagers"/"chose to betray")
        context: Game scenario description
        custom_mappings: Game custom mappings (overrides defaults)

    Returns:
        {dimension, tags, source_engine, weight, emotion_hint, decision_text, reason_text}
    """
    # Look up mapping: custom first, default fallback
    mapping = None
    if custom_mappings and action in custom_mappings:
        mapping = custom_mappings[action]
    if not mapping:
        mapping = DEFAULT_ACTION_MAP.get(action)
    if not mapping:
        mapping = {"dimension": "identity", "tags": ["intuition"], "weight": 0.5}

    # Determine source engine: explicit choice → game_explicit, otherwise game_inference
    is_explicit = bool(choice and choice.strip())
    source_engine = "game_explicit" if is_explicit else "game_inference"
    weight = mapping["weight"] if is_explicit else mapping["weight"] * 0.8

    # Emotion inference
    emotion_hint = _infer_emotion(action, choice, context)

    # Build decision text
    decision_text = choice if choice else f"[game action] {action}"
    reason_text = f"Game scenario: {context}" if context else f"Game action type: {action}"

    return {
        "dimension": mapping["dimension"],
        "tags": mapping["tags"] + ["game"],
        "source_engine": source_engine,
        "weight": round(weight, 2),
        "emotion_hint": emotion_hint,
        "decision_text": decision_text,
        "reason_text": reason_text,
    }


def _infer_emotion(action: str, choice: str, context: str) -> dict | None:
    """Infer emotion from choice text (simple keyword matching)."""
    text = f"{choice} {context}".lower()
    for keyword, hint in CHOICE_EMOTION_HINTS.items():
        if keyword in text:
            return hint
    # Default emotions by action type
    action_emotions = {
        "moral_choice": {"emotion": "ambivalent", "valence": 0.0},
        "risk_decision": {"emotion": "tense", "valence": -0.1},
        "sacrifice": {"emotion": "heavy", "valence": -0.3},
        "exploration": {"emotion": "curious", "valence": 0.3},
        "emotional_response": {"emotion": "moved", "valence": 0.0},
    }
    return action_emotions.get(action)


def validate_mappings(mappings: dict) -> list[str]:
    """Validate custom mapping format, return list of errors."""
    errors = []
    valid_dims = {"identity", "value", "emotion", "cognition",
                  "relationship", "creation", "mission"}
    for action, m in mappings.items():
        if not isinstance(m, dict):
            errors.append(f"{action}: mapping must be a dict")
            continue
        if "dimension" not in m:
            errors.append(f"{action}: missing dimension field")
        elif m["dimension"] not in valid_dims:
            errors.append(f"{action}: invalid dimension '{m['dimension']}'")
        if "weight" in m and not (0 <= m["weight"] <= 1):
            errors.append(f"{action}: weight must be between 0 and 1")
    return errors
