"""Karma engine -- BitTorrent-style contribute/consume ratio tracking.

Ratio = contributed / consumed (clamped to [0.1, 10.0]).
High ratio (>1.0) = generous contributor, gets priority routing.
Low ratio (<1.0) = net consumer, gets deprioritized (not blocked).
"""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from llmhosts.hive.manager import HiveManager

logger = logging.getLogger(__name__)

_KARMA_MIN = 0.1
_KARMA_MAX = 10.0


class KarmaEngine:
    """BitTorrent-style karma tracking.

    Ratio = contributed / consumed (clamped to [0.1, 10.0])
    High ratio (>1.0) = generous contributor, gets priority routing
    Low ratio (<1.0) = net consumer, gets deprioritized (not blocked)
    """

    def __init__(self, hive_manager: HiveManager) -> None:
        self._manager = hive_manager

    async def record_contribution(self, member_id: str, compute_hours: float) -> float:
        """Record compute contributed. Returns new karma score."""
        hive = await self._manager.get_hive()
        if hive is None:
            logger.warning("No Hive for karma update")
            return 1.0

        member = next((m for m in hive.members if m.id == member_id), None)
        if member is None:
            logger.warning("Member %s not found in Hive", member_id)
            return 1.0

        member.compute_contributed_hours += compute_hours
        member.karma_score = self.calculate_karma(
            member.compute_contributed_hours,
            member.compute_consumed_hours,
        )
        await self._manager.save()

        # Update registry if we publish there
        if hasattr(self._manager, "_publish_to_registry"):
            await self._manager._publish_to_registry(hive)

        logger.debug("Karma contribution: %s +%.2fh -> %.2f", member_id, compute_hours, member.karma_score)
        return member.karma_score

    async def record_consumption(self, member_id: str, compute_hours: float) -> float:
        """Record compute consumed. Returns new karma score."""
        hive = await self._manager.get_hive()
        if hive is None:
            logger.warning("No Hive for karma update")
            return 1.0

        member = next((m for m in hive.members if m.id == member_id), None)
        if member is None:
            logger.warning("Member %s not found in Hive", member_id)
            return 1.0

        member.compute_consumed_hours += compute_hours
        member.karma_score = self.calculate_karma(
            member.compute_contributed_hours,
            member.compute_consumed_hours,
        )
        await self._manager.save()

        if hasattr(self._manager, "_publish_to_registry"):
            await self._manager._publish_to_registry(hive)

        logger.debug("Karma consumption: %s +%.2fh -> %.2f", member_id, compute_hours, member.karma_score)
        return member.karma_score

    async def get_karma(self, member_id: str) -> float:
        """Get current karma score for a member."""
        hive = await self._manager.get_hive()
        if hive is None:
            return 1.0

        member = next((m for m in hive.members if m.id == member_id), None)
        return member.karma_score if member else 1.0

    async def get_priority_order(self) -> list[tuple[str, float]]:
        """Get members sorted by karma (highest first)."""
        members = await self._manager.list_members()
        return [(m.id, m.karma_score) for m in sorted(members, key=lambda m: m.karma_score, reverse=True)]

    @staticmethod
    def calculate_karma(contributed: float, consumed: float) -> float:
        """Calculate karma ratio. Clamped to [0.1, 10.0].

        Zero consumed -> 10.0 (max karma, contributor).
        Zero contributed -> 0.1 (min karma, consumer).
        """
        if consumed <= 0:
            return _KARMA_MAX
        if contributed <= 0:
            return _KARMA_MIN
        ratio = contributed / consumed
        return max(_KARMA_MIN, min(_KARMA_MAX, ratio))
