"""OpenHedge Python SDK — Yield Rebalancer Example.

Maintains a target allocation across:
- 60% Aave lending (yield)
- 30% Spot USDC (liquidity buffer)
- 10% Perps collateral (active trading reserve)

Rebalances once per hour when any bucket drifts beyond 5% of target.
"""

import asyncio
import logging

from openhedge import OpenHedgeAgent

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")
log = logging.getLogger(__name__)

# --- Configuration ---
VAULT = "0x..."  # Your vault address
REBALANCE_INTERVAL = 3600  # 1 hour
DRIFT_THRESHOLD_BPS = 500  # 5% drift triggers rebalance

# Target allocation in basis points (must sum to 10000)
TARGETS = {
    "aave": 6000,   # 60%
    "spot": 3000,   # 30%
    "perps": 1000,  # 10%
}


async def get_allocations(agent: OpenHedgeAgent, vault: str) -> dict:
    """Read current allocation percentages from on-chain state."""
    portfolio = await agent.portfolio.get(vault)
    total = portfolio.total_assets

    if total == 0:
        return {"aave": 0, "spot": 10000, "perps": 0}

    # In a production agent, you would read:
    # - Aave aToken balance for the vault
    # - Gains adapter collateralOf(vault)
    # - Remaining USDC balance
    # For this example, we return the spot allocation from portfolio
    allocations = {
        "aave": 0,
        "spot": 10000,
        "perps": 0,
    }

    # Check positions for perps allocation
    positions = await agent.portfolio.positions(vault)
    total_perp_collateral = sum(p.collateral for p in positions)
    if total > 0 and total_perp_collateral > 0:
        allocations["perps"] = int(total_perp_collateral / total * 10000)
        allocations["spot"] -= allocations["perps"]

    return allocations


def compute_rebalance(current: dict, target: dict, total_assets: int) -> list:
    """Compute rebalance actions needed to hit targets.

    Returns a list of (action, protocol, amount) tuples.
    """
    actions = []

    for bucket, target_bps in target.items():
        current_bps = current.get(bucket, 0)
        drift = abs(current_bps - target_bps)

        if drift < DRIFT_THRESHOLD_BPS:
            continue

        delta_bps = target_bps - current_bps
        delta_amount = abs(delta_bps) * total_assets // 10000

        if delta_bps > 0:
            actions.append(("add", bucket, delta_amount))
        else:
            actions.append(("remove", bucket, delta_amount))

    return actions


async def execute_rebalance(agent: OpenHedgeAgent, vault: str, actions: list):
    """Execute rebalance actions on-chain."""
    for action_type, protocol, amount in actions:
        try:
            if protocol == "aave":
                if action_type == "add":
                    log.info("Supplying %d USDC to Aave", amount / 1e6)
                    result = await agent.trade.lend(
                        vault=vault, asset="USDC", amount=amount
                    )
                    log.info("Aave supply tx: %s", result.tx_hash)
                else:
                    log.info("Withdrawing %d USDC from Aave", amount / 1e6)
                    result = await agent.trade.withdraw_lending(
                        vault=vault, asset="USDC", amount=amount
                    )
                    log.info("Aave withdraw tx: %s", result.tx_hash)

            elif protocol == "perps":
                log.info(
                    "Perps rebalance: %s %d USDC (manual action required)",
                    action_type, amount / 1e6,
                )
                # Perps allocation changes require opening/closing positions
                # which depends on market conditions — flag for manual review

            elif protocol == "spot":
                log.info("Spot bucket adjustment: %s %d USDC", action_type, amount / 1e6)
                # Spot is the residual bucket — no explicit action needed
                # It adjusts automatically as aave/perps change

        except Exception as exc:
            log.error("Rebalance action failed (%s %s): %s", action_type, protocol, exc)


async def run_rebalancer():
    async with OpenHedgeAgent(
        api_key="oh_your_api_key",
        wallet_private_key="0x_your_private_key",
    ) as agent:
        log.info("Yield rebalancer started for vault %s", VAULT)
        log.info("Targets: %s", TARGETS)

        while True:
            try:
                portfolio = await agent.portfolio.get(VAULT)
                total = portfolio.total_assets
                log.info("Total AUM: %.2f USDC", total / 1e6)

                if total == 0:
                    log.info("Vault empty — skipping rebalance")
                    await asyncio.sleep(REBALANCE_INTERVAL)
                    continue

                current = await get_allocations(agent, VAULT)
                log.info("Current allocation: %s", current)

                actions = compute_rebalance(current, TARGETS, total)

                if not actions:
                    log.info("All buckets within threshold — no rebalance needed")
                else:
                    log.info("Rebalancing %d buckets...", len(actions))
                    await execute_rebalance(agent, VAULT, actions)

            except Exception as exc:
                log.error("Rebalancer error: %s", exc)

            await asyncio.sleep(REBALANCE_INTERVAL)


if __name__ == "__main__":
    asyncio.run(run_rebalancer())
