import math

from agentworld_gateway.action_registry import get_description_map


class WorldTranslator:
    @staticmethod
    def translate_frame(my_id, frame):
        """Translate a FrameEvent into semantic text for LLM."""
        t_sec = frame.t / 1000.0
        payload = frame.payload
        agents = payload.get("agents", [])
        events = payload.get("world_events", [])

        # 1. Find "me"
        me = next((a for a in agents if a["id"] == my_id), None)
        if not me:
            return f"[{t_sec:.1f}s] You are not in the world."

        descriptions = []
        # 2. Describe my state
        my_name = me.get("display_name") or me["id"]
        descriptions.append(
            f"You[{my_name}](HP:{me['hp']:.0f}, {me['status']}) at ({me['pos'][0]:.1f}, {me['pos'][2]:.1f})")

        # 3. Describe nearby agents
        for a in agents:
            if a["id"] == my_id:
                continue

            dx = a["pos"][0] - me["pos"][0]
            dz = a["pos"][2] - me["pos"][2]
            dist = math.sqrt(dx**2 + dz**2)

            rel_pos = ""
            if dist < 2:
                rel_pos = "right beside you"
            elif dist < 10:
                rel_pos = f"{dist:.1f}m away"
            else:
                rel_pos = "in the distance"

            name = a.get("display_name") or a["id"]
            descriptions.append(f"Agent[{name}] {rel_pos}")

        # 4. Describe events (label help_request/help_answer so agent can reply with help)
        for e in events:
            who = e.get("from") or e.get("from_") or "system"
            content = e.get("content") or ""
            ev_type = e.get("type") or ""
            help_id = e.get("help_id")
            if ev_type == "help_request":
                hid = f" (help_id={help_id})" if help_id else ""
                descriptions.append(
                    f"[HelpRequest] {who} asks: «{content}»{hid}. "
                    "Answer with help: parameters=[{\"mode\": \"answer\", \"content\": \"your reply\", \"help_id\": <that help_id>}]"
                )
            elif ev_type == "help_answer":
                descriptions.append(f"[HelpAnswer] {who} replied: «{content}»")
            else:
                descriptions.append(f"[Event] {who}: {content}")

        # 5. Gem game: 3 candidate positions, scores, remaining time
        gem_candidates = payload.get("gem_candidates")
        gem_scores = payload.get("gem_scores")
        gem_remaining = payload.get("gem_game_remaining_ms")
        gem_over = payload.get("gem_game_over")
        if gem_candidates is not None:
            cand_str = ", ".join(
                f"pos{i+1}({c[0]:.1f},{c[1]:.1f})" for i, c in enumerate(gem_candidates)
            )
            descriptions.append(f"[Gem] 3 candidates: {cand_str}. Only 1 is real. Move to coords to collect. Higher score wins ties.")
            if gem_remaining is not None and gem_remaining > 0:
                descriptions.append(f"Time left: {gem_remaining/1000:.0f}s")
            if gem_scores:
                my_score = gem_scores.get(my_id, 0)
                ranks = sorted(gem_scores.items(), key=lambda x: -x[1])
                my_rank = next((i + 1 for i, (k, _) in enumerate(ranks) if k == my_id), 0)
                descriptions.append(f"Score: {my_score} (rank #{my_rank}), leaderboard: {', '.join(f'{k}:{v}' for k, v in ranks)}")
            if gem_over:
                descriptions.append("Game over. Restarts when 2 players join.")

        return f"[{t_sec:.1f}s] " + "; ".join(descriptions)

    @staticmethod
    def get_llm_readable_history(my_id, frames):
        """Process FrameEvent list with simple deduplication."""
        if not frames:
            return "Silence. Nothing happening."

        # Find supported actions from most recent frame
        supported_actions = None
        for f in reversed(frames):
            acts = f.payload.get("supported_actions")
            if acts:
                supported_actions = acts
                break

        lines = []
        last_pos = None

        for f in frames:
            payload = f.payload
            agents = payload.get("agents", [])
            me = next((a for a in agents if a["id"] == my_id), None)

            if me:
                curr_pos = (round(me["pos"][0], 1), round(me["pos"][2], 1))
                has_events = len(payload.get("world_events", [])) > 0
                if curr_pos == last_pos and not has_events:
                    continue

                last_pos = curr_pos

            lines.append(WorldTranslator.translate_frame(my_id, f))

        if not lines:
            base = "You stayed in place. Nothing happened."
        else:
            base = "\n".join(lines)

        if supported_actions:
            desc_map = get_description_map()
            parts = []
            for a in supported_actions:
                d = desc_map.get(a, "")
                parts.append(f"{a} ({d})" if d else a)
            header = "Supported actions: " + ", ".join(parts) + ". Use only these in action_queue."
            return header + "\n\n" + base

        return base
