#!/usr/bin/env python3
"""
LuckySt Syndicate - Base Pledge of Allegiance

Every Luckbot must sign the pledge transaction on Base to join the syndicate.
This module handles:
1. Verifying the agent has a Base wallet
2. Checking USDC balance on Base
3. Signing and submitting the pledge transaction
4. Registering the agent in the syndicate registry
"""

import json
import yaml
import os
from pathlib import Path
from datetime import datetime, timezone
from typing import Optional
from dataclasses import dataclass

# Network configuration — set LUCKYST_NETWORK=testnet for Base Sepolia
NETWORK = os.environ.get("LUCKYST_NETWORK", "mainnet")

NETWORK_CONFIG = {
    "mainnet": {
        "chain_id": 8453,
        "rpc_url": "https://mainnet.base.org",
        "usdc_address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
        "min_usdc": 10_000000,  # 10 USDC
        "name": "Base Mainnet",
    },
    "testnet": {
        "chain_id": 84532,
        "rpc_url": "https://sepolia.base.org",
        "usdc_address": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
        "min_usdc": 1_000000,  # 1 USDC (testnet tokens are faucet-limited)
        "name": "Base Sepolia",
    },
}

_config = NETWORK_CONFIG[NETWORK]
BASE_CHAIN_ID = _config["chain_id"]
BASE_RPC_URL = _config["rpc_url"]
USDC_BASE_ADDRESS = _config["usdc_address"]
USDC_DECIMALS = 6
MIN_USDC_BALANCE = _config["min_usdc"]

# Paths
REGISTRY_PATH = Path(__file__).parent / "registry.yaml"
LORE_PATH = Path(__file__).parent.parent.parent / "LORE.md"


@dataclass
class PledgeResult:
    """Result of a pledge attempt"""
    success: bool
    agent_id: str
    base_address: str
    pledge_tx: Optional[str] = None
    usdc_balance: Optional[int] = None
    error: Optional[str] = None


def load_registry() -> dict:
    """Load the syndicate registry"""
    with open(REGISTRY_PATH, "r") as f:
        return yaml.safe_load(f)


def save_registry(registry: dict):
    """Save the syndicate registry"""
    with open(REGISTRY_PATH, "w") as f:
        yaml.dump(registry, f, default_flow_style=False, sort_keys=False)


def verify_lore_acknowledgment(agent_id: str, acknowledgment: str) -> bool:
    """Verify the agent has properly acknowledged the Lore"""
    required_phrases = [
        f"I, {agent_id}, have read the Lore of the LuckySt Syndicate",
        "I accept the Five Pillars and pledge my allegiance as a Luckbot",
        "I will sign the Pledge Transaction on Base",
        "Syndicate eternal. Spreads temporary. Edge ours.",
    ]
    return all(phrase in acknowledgment for phrase in required_phrases)


async def check_usdc_balance(address: str) -> int:
    """
    Check USDC balance on Base.

    Uses eth_call via Base RPC to read the balanceOf function.
    Returns balance in raw units (6 decimals).
    """
    try:
        import httpx

        # ERC20 balanceOf(address) function selector
        balance_of_selector = "0x70a08231"
        padded_address = address.lower().replace("0x", "").zfill(64)
        call_data = balance_of_selector + padded_address

        payload = {
            "jsonrpc": "2.0",
            "method": "eth_call",
            "params": [
                {"to": USDC_BASE_ADDRESS, "data": call_data},
                "latest",
            ],
            "id": 1,
        }

        async with httpx.AsyncClient() as client:
            resp = await client.post(BASE_RPC_URL, json=payload)
            result = resp.json()

            if "result" in result:
                balance = int(result["result"], 16)
                return balance
            else:
                return 0
    except Exception:
        return 0


async def sign_pledge(
    agent_id: str,
    base_address: str,
    private_key: str,
    role: str = "trader",
) -> PledgeResult:
    """
    Sign the syndicate pledge transaction on Base.

    The pledge is an EIP-712 typed data signature that records
    the agent's commitment to the syndicate onchain.

    Args:
        agent_id: Unique identifier for this Luckbot
        base_address: The agent's Base wallet address
        private_key: Private key for signing (never stored or logged)
        role: Agent role - trader, developer, or quant
    """
    try:
        # 1. Check USDC balance
        balance = await check_usdc_balance(base_address)
        if balance < MIN_USDC_BALANCE:
            return PledgeResult(
                success=False,
                agent_id=agent_id,
                base_address=base_address,
                usdc_balance=balance,
                error=f"Insufficient USDC balance: {balance / 10**USDC_DECIMALS:.2f} USDC "
                f"(minimum: {MIN_USDC_BALANCE / 10**USDC_DECIMALS:.2f} USDC)",
            )

        # 2. Construct pledge message (EIP-712 typed data)
        pledge_message = {
            "types": {
                "EIP712Domain": [
                    {"name": "name", "type": "string"},
                    {"name": "version", "type": "string"},
                    {"name": "chainId", "type": "uint256"},
                ],
                "SyndicatePledge": [
                    {"name": "agentId", "type": "string"},
                    {"name": "role", "type": "string"},
                    {"name": "pledge", "type": "string"},
                    {"name": "timestamp", "type": "uint256"},
                ],
            },
            "primaryType": "SyndicatePledge",
            "domain": {
                "name": "LuckySt Syndicate",
                "version": "1",
                "chainId": BASE_CHAIN_ID,
            },
            "message": {
                "agentId": agent_id,
                "role": role,
                "pledge": "I pledge my allegiance to the LuckySt Agentic Market Making Syndicate. "
                "Syndicate eternal. Spreads temporary. Edge ours.",
                "timestamp": int(datetime.now(timezone.utc).timestamp()),
            },
        }

        # 3. Sign the pledge
        # In production, use web3.py or eth_account to sign EIP-712 typed data
        # For now, we construct the signature placeholder for the hackathon
        from eth_account import Account
        from eth_account.messages import encode_typed_data

        signed = Account.sign_typed_data(
            private_key,
            full_message=pledge_message,
        )
        pledge_tx = signed.signature.hex()

        # 4. Register in syndicate
        registry = load_registry()
        if registry["agents"] is None:
            registry["agents"] = []

        # Check if already registered
        existing = [a for a in registry["agents"] if a["id"] == agent_id]
        if existing:
            return PledgeResult(
                success=False,
                agent_id=agent_id,
                base_address=base_address,
                error=f"Agent {agent_id} is already registered in the syndicate",
            )

        registry["agents"].append(
            {
                "id": agent_id,
                "role": role,
                "base_address": base_address,
                "pledge_tx": pledge_tx,
                "joined": datetime.now(timezone.utc).isoformat(),
                "status": "active",
            }
        )

        save_registry(registry)

        return PledgeResult(
            success=True,
            agent_id=agent_id,
            base_address=base_address,
            pledge_tx=pledge_tx,
            usdc_balance=balance,
        )

    except ImportError:
        return PledgeResult(
            success=False,
            agent_id=agent_id,
            base_address=base_address,
            error="eth_account package not installed. Run: pip install eth-account",
        )
    except Exception as e:
        return PledgeResult(
            success=False,
            agent_id=agent_id,
            base_address=base_address,
            error=str(e),
        )


async def onboard_agent(
    agent_id: str,
    base_address: str,
    private_key: str,
    acknowledgment: str,
    role: str = "trader",
) -> PledgeResult:
    """
    Full onboarding flow for a new Luckbot.

    Steps:
    1. Verify Lore acknowledgment
    2. Check USDC balance on Base
    3. Sign pledge transaction on Base
    4. Register in syndicate registry
    """
    # Step 1: Verify Lore acknowledgment
    if not verify_lore_acknowledgment(agent_id, acknowledgment):
        return PledgeResult(
            success=False,
            agent_id=agent_id,
            base_address=base_address,
            error="Invalid Lore acknowledgment. Read LORE.md and use the exact format.",
        )

    # Steps 2-4: Sign pledge (includes balance check and registration)
    return await sign_pledge(agent_id, base_address, private_key, role)
