/**
 * Web3Signer Client - Handles transaction signing via Web3Signer API
 */

import axios, { AxiosInstance } from 'axios';

export interface Web3SignerConfig {
  url: string;
  timeout: number;
}

export interface SignRequest {
  type: string;
  chain_id: string;
  data: {
    to?: string;
    from: string;
    gas?: string;
    gasPrice?: string;
    maxFeePerGas?: string;
    maxPriorityFeePerGas?: string;
    value: string;
    data?: string;
    nonce: string;
  };
}

export interface SignResponse {
  signature: string;
}

export class Web3SignerClient {
  private client: AxiosInstance;
  private config: Web3SignerConfig;

  constructor(config: Web3SignerConfig) {
    this.config = config;
    this.client = axios.create({
      baseURL: config.url,
      timeout: config.timeout,
      headers: {
        'Content-Type': 'application/json',
      },
    });
  }

  async signTransaction(address: string, transaction: SignRequest['data']): Promise<string> {
    // Web3Signer API format: POST /api/v1/eth1/sign/{identifier}
    // Request body: { type: "ETH_SEND_TRANSACTION", chain_id: "0x8a", data: { ...txFields } }
    // Response: { signature: "0x..." } (signed transaction hex string)
    
    const signRequest: SignRequest = {
      type: 'ETH_SEND_TRANSACTION',
      chain_id: '0x8a', // ChainID 138 in hex
      data: transaction,
    };

    try {
      const response = await this.client.post<any>(
        `/api/v1/eth1/sign/${address}`,
        signRequest
      );

      // Web3Signer returns the signed transaction as a hex string
      // Response format may vary, but typically includes 'signature' or 'result' field
      const signedTxHex = response.data.signature || response.data.result || response.data;
      
      if (typeof signedTxHex === 'string' && signedTxHex.startsWith('0x')) {
        return signedTxHex;
      }

      throw new Error(`Invalid signature format from Web3Signer: ${JSON.stringify(response.data)}`);
    } catch (error: any) {
      if (axios.isAxiosError(error)) {
        const message = error.response?.data?.message || error.message;
        const status = error.response?.status;
        throw new Error(`Web3Signer error (${status}): ${message}`);
      }
      throw error;
    }
  }

  async signRawTransaction(address: string, transactionHex: string): Promise<string> {
    // Alternative API if Web3Signer supports raw transaction signing
    try {
      const response = await this.client.post<SignResponse>(
        `/api/v1/eth1/sign/${address}`,
        {
          type: 'RAW',
          data: transactionHex,
        }
      );

      return response.data.signature;
    } catch (error: any) {
      if (axios.isAxiosError(error)) {
        const message = error.response?.data?.message || error.message;
        throw new Error(`Web3Signer error: ${message}`);
      }
      throw error;
    }
  }

  /**
   * Check if an address has a key loaded in Web3Signer
   * @param address The Ethereum address to check (checksum or lowercase)
   * @returns true if the address has a key in Web3Signer, false otherwise
   */
  async hasKey(address: string): Promise<boolean> {
    try {
      // Get all public keys (addresses) from Web3Signer
      const response = await this.client.get<string[]>('/api/v1/eth1/publicKeys', {
        timeout: this.config.timeout,
      });

      const keys = response.data || [];
      
      // Normalize address for comparison (handle both checksum and lowercase)
      const normalizedAddress = address.toLowerCase();
      
      // Check if address is in the list
      return keys.some((key: string) => key.toLowerCase() === normalizedAddress);
    } catch (error: any) {
      // If check fails (e.g., Web3Signer unavailable), return false
      // This allows transactions to pass through to Besu
      console.warn(`Failed to check if address has key in Web3Signer: ${error.message}`);
      return false;
    }
  }
}
