import { ethers, JsonRpcProvider, TransactionRequest } from 'ethers';
import type { WalletObject } from '@ocap/wallet';

/**
 * Remote Signer that uses remote signing service for Ethereum transactions
 * Implements required methods from AbstractSigner
 */
export class RemoteSigner extends ethers.AbstractSigner {
  private wallet: WalletObject;

  constructor(wallet: WalletObject, provider?: JsonRpcProvider) {
    super(provider);
    this.wallet = wallet;
  }

  /**
   * Returns the address of the signer
   * Required by AbstractSigner
   */
  getAddress(): Promise<string> {
    return Promise.resolve(this.wallet.address);
  }

  /**
   * Signs a transaction and returns the signed serialized transaction
   * Required by AbstractSigner
   */
  async signTransaction(transaction: TransactionRequest): Promise<string> {
    // Populate transaction fields (nonce, gasLimit, chainId, etc.)
    const populatedTx = await this.populateTransaction(transaction);

    // Remove 'from' field as it's not allowed in unsigned transactions
    // The 'from' address is derived from the signature
    const { from, ...txWithoutFrom } = populatedTx;

    // Serialize unsigned transaction
    const unsignedTx = ethers.Transaction.from(txWithoutFrom);
    const serialized = unsignedTx.unsignedSerialized;

    // Hash and sign via remote service using signETH (Ethereum-specific signing)
    const hash = ethers.keccak256(serialized);
    const signature = await this.wallet.signETH(hash, false); // hashBeforeSign=false because we already hashed

    // Attach signature and return signed transaction
    unsignedTx.signature = ethers.Signature.from(signature);
    return unsignedTx.serialized;
  }

  /**
   * Signs a message and returns the signature
   * Required for message signing operations
   */
  async signMessage(message: string | Uint8Array): Promise<string> {
    // Convert message to bytes if it's a string
    const messageBytes = typeof message === 'string' ? ethers.toUtf8Bytes(message) : message;

    // Calculate the Ethereum signed message hash
    const messageHash = ethers.hashMessage(messageBytes);

    // Sign via remote service using signETH (Ethereum-specific signing)
    const signature = await this.wallet.signETH(messageHash, false); // hashBeforeSign=false because hashMessage already hashed

    // Return signature as hex string
    return ethers.hexlify(signature);
  }

  /**
   * Signs typed data (EIP-712)
   * Optional but useful for signing structured data
   */
  async signTypedData(
    domain: ethers.TypedDataDomain,
    types: Record<string, ethers.TypedDataField[]>,
    value: Record<string, any>
  ): Promise<string> {
    // Calculate the EIP-712 hash
    const hash = ethers.TypedDataEncoder.hash(domain, types, value);

    // Sign via remote service using signETH (Ethereum-specific signing)
    const signature = await this.wallet.signETH(hash, false); // hashBeforeSign=false because hash is already computed

    // Return signature as hex string
    return ethers.hexlify(signature);
  }

  /**
   * Connects the signer to a new provider
   * Required by AbstractSigner
   */
  connect(provider: JsonRpcProvider): RemoteSigner {
    return new RemoteSigner(this.wallet, provider);
  }
}
