'use client';

/**
 * EIP-7702 one-signature onboard helper.
 *
 * Uses viem's experimental 7702 actions to:
 *   1. Have the user's EOA sign an authorization delegating its code to
 *      VaultfireBatchOnboarder (0x82c6...8890).
 *   2. Send a Type-4 transaction from the SAME EOA to itself, calling
 *      onboardAndBond(...) against the delegated code, all in one sig.
 *
 * Wallet capability detection:
 *   - EIP-7702 authorization signing requires a wallet that speaks
 *     `eth_signTypedData_v4` for the Auth typed struct AND accepts Type-4
 *     txs via `eth_sendTransaction`.
 *   - Smart-wallet UIs (Coinbase Smart Wallet used by Base App) don't yet
 *     expose this. We detect via the provider's reported capabilities and
 *     fall back gracefully.
 *
 * On failure/rejection, the caller should route the user to the classic
 * 2-step flow (/register then /bonds) which we always keep working.
 */
import {
  type WalletClient,
  createPublicClient,
  encodeFunctionData,
  http,
  keccak256,
  toBytes,
} from 'viem';
import { base } from 'viem/chains';
import { signAuthorization } from 'viem/experimental';
import {
  BATCH_ONBOARDER_ABI,
  CONTRACTS,
  BASE_CHAIN_ID,
  buildAgentURI,
} from './vaultfire';

export interface OneTapOnboardArgs {
  walletClient: WalletClient;
  /** The user's connected EOA address. */
  userAddress: `0x${string}`;
  /** The AI agent (or human counterparty) to bond with. Must be ≠ userAddress. */
  aiAgent: `0x${string}`;
  /** Human-readable partnership type ("coding", "research", ...). */
  partnershipType: string;
  /** Stake amount in wei. PartnershipBondsV2 requires > 0. */
  stakeWei: bigint;
  /** Optional agent description stored in agentURI JSON. */
  description?: string;
  /** Optional capability list; defaults to ['trust','verification','identity']. */
  capabilities?: string[];
  /** Optional agent type tag. Defaults to 'human-operated'. */
  agentType?: string;
}

export interface OneTapOnboardResult {
  /** The tx hash of the broadcast Type-4 transaction. */
  txHash: `0x${string}`;
  /** The agentURI that was stored on-chain. */
  agentURI: string;
}

/**
 * Check whether a connected wallet can sign EIP-7702 authorizations.
 *
 * Heuristics (best-effort; false negatives are acceptable since we have a
 * fallback; false positives are not — they'd result in a bad UX):
 *
 *   - Coinbase Smart Wallet / Base App: provider exposes `isCoinbaseWallet`
 *     but NOT a regular EOA; flag as unsupported.
 *   - MetaMask, Rabby, Rainbow (injected EOA): flag as supported.
 *   - Unknown: assume unsupported (safe default).
 */
export function walletSupports7702(
  walletClient: WalletClient | undefined | null,
): boolean {
  if (!walletClient) return false;
  const transport = walletClient.transport as
    | { type?: string; name?: string }
    | undefined;
  // Only injected (EIP-1193) wallets can give us a raw EOA signature.
  // Custom/HTTP transports used by smart-wallet orchestration bypass this.
  if (transport?.type !== 'custom' && transport?.type !== 'injected') {
    return false;
  }

  // Feature-detect from the raw provider. Access the injected provider
  // through window.ethereum since viem's walletClient doesn't expose it
  // directly in all versions.
  const eth = typeof window !== 'undefined'
    ? (window as unknown as {
        ethereum?: {
          isCoinbaseWallet?: boolean;
          isCoinbaseBrowser?: boolean;
          isMetaMask?: boolean;
          isRabby?: boolean;
          isRainbow?: boolean;
        };
      }).ethereum
    : undefined;

  if (!eth) return false;
  // Smart-wallet UIs (Coinbase Smart Wallet / Base App in-app browser) do not
  // expose EOA auth signing today. Explicitly exclude.
  if (eth.isCoinbaseWallet || eth.isCoinbaseBrowser) return false;
  // Known 7702-capable wallets.
  if (eth.isMetaMask || eth.isRabby || eth.isRainbow) return true;
  // Unknown injected wallet — default to unsupported to avoid surprise failures.
  return false;
}

/**
 * Run the full one-signature onboard + bond flow.
 *
 * Throws if the wallet rejects or the tx reverts. Caller should catch and
 * route to the fallback multi-tx flow.
 */
export async function oneTapOnboard(
  args: OneTapOnboardArgs,
): Promise<OneTapOnboardResult> {
  const {
    walletClient,
    userAddress,
    aiAgent,
    partnershipType,
    stakeWei,
    description,
    capabilities,
    agentType = 'human-operated',
  } = args;

  if (aiAgent.toLowerCase() === userAddress.toLowerCase()) {
    throw new Error(
      'Counterparty cannot be your own wallet. Pick a different address.',
    );
  }
  if (stakeWei <= 0n) {
    throw new Error('Stake must be greater than 0 wei.');
  }

  // Build the on-chain agent payload.
  const caps = capabilities ?? ['trust', 'verification', 'identity'];
  const capsHash = keccak256(toBytes([...caps].sort().join(','))) as `0x${string}`;
  const agentURI = buildAgentURI({ address: userAddress, description, capabilities: caps });

  // Step 1: sign the EIP-7702 authorization delegating the EOA to the
  // VaultfireBatchOnboarder contract.
  //
  // Because the same EOA is BOTH the authorization signer AND the tx sender
  // ("self-executing"), the tx itself bumps the nonce before the
  // authorizationList is applied. We therefore sign for `currentNonce + 1`.
  // viem 2.21.x doesn't expose an `executor: 'self'` shortcut, so we fetch
  // the nonce explicitly and pass it.
  const publicClient = createPublicClient({
    chain: base,
    transport: http('https://mainnet.base.org'),
  });
  const currentNonce = await publicClient.getTransactionCount({
    address: userAddress,
  });

  const authorization = await signAuthorization(walletClient, {
    account: userAddress,
    contractAddress: CONTRACTS.BatchOnboarder as `0x${string}`,
    chainId: BASE_CHAIN_ID,
    nonce: currentNonce + 1,
  });

  // Step 2: encode the onboardAndBond call.
  const data = encodeFunctionData({
    abi: BATCH_ONBOARDER_ABI,
    functionName: 'onboardAndBond',
    args: [agentURI, agentType, capsHash, aiAgent, partnershipType],
  });

  // Step 3: send the Type-4 tx. `to` is the user's own EOA — under the
  // delegation authorization, calls to the EOA execute the onboarder code.
  const txHash = await walletClient.sendTransaction({
    account: userAddress,
    chain: walletClient.chain,
    to: userAddress,
    data,
    value: stakeWei,
    authorizationList: [authorization],
  } as Parameters<WalletClient['sendTransaction']>[0]);

  return { txHash, agentURI };
}
