/**
 * External Wallet Signer
 *
 * Wraps an EIP-1193 provider (from EIP-6963 discovery) for transaction
 * operations. The browser wallet (MetaMask, Rainbow, etc.) handles all
 * signing — we just construct and forward requests.
 */

import type { EIP1193Provider } from './eip6963';

// ============================================================================
// TYPES
// ============================================================================

export interface TransactionRequest {
  from: string;
  to: string;
  value?: string;
  data?: string;
  gas?: string;
  gasPrice?: string;
  maxFeePerGas?: string;
  maxPriorityFeePerGas?: string;
  nonce?: string;
  chainId?: string;
}

export interface TypedDataDomain {
  name?: string;
  version?: string;
  chainId?: number;
  verifyingContract?: string;
}

// ============================================================================
// EXTERNAL SIGNER
// ============================================================================

export class ExternalSigner {
  private provider: EIP1193Provider;

  constructor(provider: EIP1193Provider) {
    this.provider = provider;
  }

  async getAccounts(): Promise<string[]> {
    return this.provider.request({ method: 'eth_requestAccounts' });
  }

  async getChainId(): Promise<string> {
    return this.provider.request({ method: 'eth_chainId' });
  }

  async switchChain(chainId: number): Promise<void> {
    const hexChainId = '0x' + chainId.toString(16);
    try {
      await this.provider.request({
        method: 'wallet_switchEthereumChain',
        params: [{ chainId: hexChainId }],
      });
    } catch (err: any) {
      // 4902 = chain not added
      if (err?.code === 4902) {
        throw new Error(`Chain ${chainId} not configured in wallet. Please add it manually.`);
      }
      throw err;
    }
  }

  async sendTransaction(tx: TransactionRequest): Promise<string> {
    // Ensure correct chain
    if (tx.chainId) {
      const currentChain = await this.getChainId();
      const targetHex = '0x' + parseInt(tx.chainId).toString(16);
      if (currentChain.toLowerCase() !== targetHex.toLowerCase()) {
        await this.switchChain(parseInt(tx.chainId));
      }
    }

    return this.provider.request({
      method: 'eth_sendTransaction',
      params: [tx],
    });
  }

  async personalSign(message: string, account: string): Promise<string> {
    return this.provider.request({
      method: 'personal_sign',
      params: [message, account],
    });
  }

  async signTypedData(
    account: string,
    domain: TypedDataDomain,
    types: Record<string, Array<{ name: string; type: string }>>,
    value: Record<string, any>,
  ): Promise<string> {
    // Build EIP712Domain type array dynamically from domain fields
    const domainType: Array<{ name: string; type: string }> = [];
    if (domain.name !== undefined) domainType.push({ name: 'name', type: 'string' });
    if (domain.version !== undefined) domainType.push({ name: 'version', type: 'string' });
    if (domain.chainId !== undefined) domainType.push({ name: 'chainId', type: 'uint256' });
    if (domain.verifyingContract !== undefined) domainType.push({ name: 'verifyingContract', type: 'address' });
    const data = {
      types: { EIP712Domain: domainType, ...types },
      domain,
      primaryType: Object.keys(types).find(k => k !== 'EIP712Domain') || '',
      message: value,
    };

    return this.provider.request({
      method: 'eth_signTypedData_v4',
      params: [account, JSON.stringify(data)],
    });
  }
}
