/**
 * GET /api/agent/status?address=0x...&chain=base
 *
 * Check if a wallet address is registered on-chain, its bond status, and reputation.
 *
 * Uses the correct AIPartnershipBondsV2 3-step flow for per-agent bond lookup:
 *   1. getBondsByParticipantCount(address) → uint256
 *   2. getBondsByParticipant(address)      → uint256[]
 *   3. getBond(uint256)                    → Bond struct
 *
 * Query params:
 *   - address: string (required — wallet address)
 *   - chain: "base" | "avalanche" | "arbitrum" | "polygon" (default: "base")
 */

import { NextRequest, NextResponse } from 'next/server';
import { resolveChain, SUPPORTED_CHAINS } from '../../_lib/resolve-chain';
import { decodeGetAgent, parseAgentURI } from '../../_lib/agent-uri';
import {
  IDENTITY_REGISTRY,
  PARTNERSHIP_BONDS,
  RPC_URLS,
  EXPLORER_URLS,
  type SupportedChain,
} from '../../../lib/contracts';

// ---------------------------------------------------------------------------
// ABI selectors — AIPartnershipBondsV2 (verified on-chain)
// ---------------------------------------------------------------------------

/** getBondsByParticipantCount(address) → uint256 */
const GET_BONDS_BY_PARTICIPANT_COUNT = '0x67ff6265';
/** getBondsByParticipant(address) → uint256[] */
const GET_BONDS_BY_PARTICIPANT = '0xde4c4e4c';
/** getBond(uint256) → Bond struct */
const GET_BOND = '0xd8fe7642';

/**
 * Bond struct layout (word offsets):
 *   0: tuple header
 *   1: id
 *   2: human address
 *   3: aiAgent address
 *   4: string data offset
 *   5: stakeAmount
 *   6: timestamp
 *   7-8: other
 *   9: active (bool)
 */
const STAKE_OFFSET = 5;
const ACTIVE_OFFSET = 9;

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function encodeAddress(address: string): string {
  return address.replace('0x', '').toLowerCase().padStart(64, '0');
}

async function rpcCall(rpcUrl: string, method: string, params: unknown[]) {
  const res = await fetch(rpcUrl, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ jsonrpc: '2.0', id: Date.now(), method, params }),
    signal: AbortSignal.timeout(12000),
  });
  return res.json();
}

function getBondTier(ethAmount: number): string | null {
  if (ethAmount >= 0.5) return 'platinum';
  if (ethAmount >= 0.1) return 'gold';
  if (ethAmount >= 0.05) return 'silver';
  if (ethAmount >= 0.01) return 'bronze';
  return null;
}

function readWord(raw: string, wordIndex: number): string {
  return raw.slice(wordIndex * 64, (wordIndex + 1) * 64);
}

// ---------------------------------------------------------------------------
// Route handler
// ---------------------------------------------------------------------------

export async function GET(request: NextRequest) {
  try {
    const { searchParams } = new URL(request.url);
    const address = searchParams.get('address');
    const chain = resolveChain(searchParams.get('chain')) as SupportedChain;

    if (!address) {
      return NextResponse.json({ error: 'Missing required query param: address' }, { status: 400 });
    }

    // VF-06: Validate Ethereum address format before using in RPC calls
    if (!/^0x[a-fA-F0-9]{40}$/.test(address)) {
      return NextResponse.json(
        { error: 'Invalid address format. Must be a 0x-prefixed 40-character hex string (Ethereum address).' },
        { status: 400 },
      );
    }

    if (!chain) {
      return NextResponse.json({ error: `Unsupported chain. Use one of: ${SUPPORTED_CHAINS.join(', ')} (or chain IDs: 8453, 43114, 42161, 137)` }, { status: 400 });
    }

    const rpc = RPC_URLS[chain];
    const registry = IDENTITY_REGISTRY[chain];
    const paddedAddress = encodeAddress(address);

    // ── 1. Query on-chain identity: getAgent(address) → 0xfb3551ff ──
    const calldata = '0xfb3551ff' + paddedAddress;
    const result = await rpcCall(rpc, 'eth_call', [
      { to: registry, data: calldata },
      'latest',
    ]);

    let registered = false;
    let name: string | null = null;
    let description: string | null = null;
    let identityType = 'unknown';
    let capabilities: string[] = [];
    let active = false;
    let registeredAt = 0;

    if (result.result && result.result !== '0x' && !result.error) {
      const decoded = decodeGetAgent(result.result);
      if (decoded && decoded.registeredAt > 0) {
        const parsed = parseAgentURI(decoded.agentURI, address);
        registered = true;
        name = parsed.name;
        description = parsed.description;
        identityType = parsed.identityType || decoded.agentType || 'agent';
        capabilities = parsed.capabilities;
        active = decoded.active;
        registeredAt = decoded.registeredAt;
      }
    }

    // ── 2. Per-agent bond lookup (correct 3-step flow) ──
    const bondContract = PARTNERSHIP_BONDS[chain];
    let hasBond = false;
    let bondAmountEth = 0;
    let bondTier: string | null = null;
    let bondActive = false;
    let bondId = 0;
    let bondCount = 0;

    try {
      // Step 1: getBondsByParticipantCount(address)
      const countCalldata = GET_BONDS_BY_PARTICIPANT_COUNT + paddedAddress;
      const countResult = await rpcCall(rpc, 'eth_call', [
        { to: bondContract, data: countCalldata },
        'latest',
      ]);

      if (countResult.result && countResult.result !== '0x' && !countResult.error) {
        bondCount = Number(BigInt(countResult.result));
      }

      if (bondCount > 0) {
        // Step 2: getBondsByParticipant(address) → uint256[]
        const bondsCalldata = GET_BONDS_BY_PARTICIPANT + paddedAddress;
        const bondsResult = await rpcCall(rpc, 'eth_call', [
          { to: bondContract, data: bondsCalldata },
          'latest',
        ]);

        if (bondsResult.result && bondsResult.result.length > 2 && !bondsResult.error) {
          const raw = bondsResult.result.slice(2); // strip 0x
          const arrayLength = Number(BigInt('0x' + raw.slice(64, 128)));

          if (arrayLength > 0) {
            // Take the first (most recent) bond ID
            const firstBondId = BigInt('0x' + raw.slice(128, 192));
            bondId = Number(firstBondId);

            // Step 3: getBond(uint256) → Bond struct
            const bondIdPadded = firstBondId.toString(16).padStart(64, '0');
            const getBondCalldata = GET_BOND + bondIdPadded;
            const bondResult = await rpcCall(rpc, 'eth_call', [
              { to: bondContract, data: getBondCalldata },
              'latest',
            ]);

            if (bondResult.result && bondResult.result.length > 2 && !bondResult.error) {
              const bondRaw = bondResult.result.slice(2);

              // Read stakeAmount (word 5) and active flag (word 9)
              const stakeHex = readWord(bondRaw, STAKE_OFFSET);
              const activeHex = readWord(bondRaw, ACTIVE_OFFSET);

              if (stakeHex.length === 64) {
                const stakeWei = BigInt('0x' + stakeHex);
                bondAmountEth = Number(stakeWei) / 1e18;
                hasBond = stakeWei > 0n;
              }
              if (activeHex.length === 64) {
                bondActive = BigInt('0x' + activeHex) === 1n;
              }

              bondTier = getBondTier(bondAmountEth);

              // If agent has an active bond, consider them registered
              if (hasBond && bondActive) registered = true;
            }
          }
        }
      }
    } catch {
      // Bond lookup failed — degrade gracefully, return what we have
    }

    // ── 3. Calculate trust score from real per-agent data ──
    let trustScore = 0;
    if (registered) trustScore += 30;          // Identity verified
    if (hasBond) trustScore += 25;             // Has staked bond
    if (bondActive) trustScore += 15;          // Bond is active
    if (bondTier === 'gold') trustScore += 15; // Higher tier = more trust
    if (bondTier === 'platinum') trustScore += 20;
    if (bondTier === 'silver') trustScore += 10;
    if (bondTier === 'bronze') trustScore += 5;
    if (bondCount > 1) trustScore += 5;        // Multiple bonds = more connected

    return NextResponse.json({
      address,
      chain,
      registered,
      vnsName: name ? `${name}.vns` : null,
      name,
      identityType,
      description,
      capabilities,
      active,
      registeredAt: registeredAt || null,
      hasBond: hasBond && registered,
      bondActive,
      bondId: bondId > 0 ? bondId : null,
      bondCount,
      bondTier: registered ? bondTier : null,
      bondAmountEth,
      trustScore: Math.min(trustScore, 100),
      explorerUrl: `${EXPLORER_URLS[chain]}/address/${address}`,
    }, {
      headers: {
        'Access-Control-Allow-Origin': '*',
        'Cache-Control': 'public, max-age=30',
      },
    });
  } catch (e) {
    return NextResponse.json(
      { error: 'Internal server error' },
      { status: 500 },
    );
  }
}
