/**
 * Synor Wallet Client
 */

import type {
  WalletConfig,
  Wallet,
  WalletType,
  CreateWalletResult,
  StealthAddress,
  Transaction,
  SignedTransaction,
  SignMessageOptions,
  SignedMessage,
  UTXO,
  Balance,
  BalanceResponse,
  GetUtxosOptions,
  ImportWalletOptions,
  BuildTransactionOptions,
  FeeEstimate,
  Priority,
} from './types';

const DEFAULT_ENDPOINT = 'https://wallet.synor.cc/api/v1';

/**
 * Synor Wallet SDK error.
 */
export class WalletError extends Error {
  constructor(
    message: string,
    public statusCode?: number,
    public code?: string
  ) {
    super(message);
    this.name = 'WalletError';
  }
}

/**
 * Main Synor Wallet client.
 *
 * @example
 * ```ts
 * const wallet = new SynorWallet({ apiKey: 'sk_...' });
 *
 * // Create a new wallet
 * const { wallet: w, mnemonic } = await wallet.createWallet();
 * console.log('Address:', w.address);
 * console.log('Mnemonic:', mnemonic); // Store securely!
 *
 * // Get balance
 * const balance = await wallet.getBalance(w.address);
 * console.log('Balance:', balance.native.total);
 * ```
 */
export class SynorWallet {
  private config: Required<Omit<WalletConfig, 'derivationPath'>> & { derivationPath?: string };

  constructor(config: WalletConfig) {
    this.config = {
      apiKey: config.apiKey,
      endpoint: config.endpoint ?? DEFAULT_ENDPOINT,
      network: config.network ?? 'mainnet',
      timeout: config.timeout ?? 30000,
      debug: config.debug ?? false,
      derivationPath: config.derivationPath,
    };
  }

  /**
   * Create a new wallet.
   *
   * @param type - Wallet type (standard, stealth)
   * @returns Created wallet and mnemonic phrase
   */
  async createWallet(type: WalletType = 'standard'): Promise<CreateWalletResult> {
    const response = await this.request('POST', '/wallets', {
      type,
      network: this.config.network,
      derivationPath: this.config.derivationPath,
    });

    return {
      wallet: response.wallet,
      mnemonic: response.mnemonic,
    };
  }

  /**
   * Import a wallet from mnemonic phrase.
   *
   * @param options - Import options including mnemonic
   * @returns Imported wallet
   */
  async importWallet(options: ImportWalletOptions): Promise<Wallet> {
    const response = await this.request('POST', '/wallets/import', {
      mnemonic: options.mnemonic,
      passphrase: options.passphrase,
      type: options.type ?? 'standard',
      network: this.config.network,
      derivationPath: this.config.derivationPath,
    });

    return response.wallet;
  }

  /**
   * Get wallet by ID.
   *
   * @param walletId - Wallet ID
   * @returns Wallet details
   */
  async getWallet(walletId: string): Promise<Wallet> {
    const response = await this.request('GET', `/wallets/${walletId}`);
    return response.wallet;
  }

  /**
   * List all wallets for this account.
   *
   * @returns List of wallets
   */
  async listWallets(): Promise<Wallet[]> {
    const response = await this.request('GET', '/wallets');
    return response.wallets;
  }

  /**
   * Get address at a specific index for a wallet.
   *
   * @param walletId - Wallet ID
   * @param index - Derivation index
   * @returns Address at the index
   */
  async getAddress(walletId: string, index: number = 0): Promise<string> {
    const response = await this.request('GET', `/wallets/${walletId}/addresses/${index}`);
    return response.address;
  }

  /**
   * Generate a stealth address for receiving private payments.
   *
   * @param walletId - Wallet ID
   * @returns Stealth address details
   */
  async getStealthAddress(walletId: string): Promise<StealthAddress> {
    const response = await this.request('POST', `/wallets/${walletId}/stealth`);
    return response.stealthAddress;
  }

  /**
   * Sign a transaction.
   *
   * @param walletId - Wallet ID
   * @param transaction - Transaction to sign
   * @returns Signed transaction
   */
  async signTransaction(walletId: string, transaction: Transaction): Promise<SignedTransaction> {
    const response = await this.request('POST', `/wallets/${walletId}/sign`, {
      transaction,
    });

    return response.signedTransaction;
  }

  /**
   * Sign a message.
   *
   * @param walletId - Wallet ID
   * @param options - Message signing options
   * @returns Signed message
   */
  async signMessage(walletId: string, options: SignMessageOptions): Promise<SignedMessage> {
    const response = await this.request('POST', `/wallets/${walletId}/sign-message`, {
      message: options.message,
      format: options.format ?? 'text',
    });

    return response;
  }

  /**
   * Verify a signed message.
   *
   * @param message - Original message
   * @param signature - Signature to verify
   * @param address - Expected signer address
   * @returns True if signature is valid
   */
  async verifyMessage(message: string, signature: string, address: string): Promise<boolean> {
    const response = await this.request('POST', '/verify-message', {
      message,
      signature,
      address,
    });

    return response.valid;
  }

  /**
   * Get balance for an address.
   *
   * @param address - Address to check
   * @param includeTokens - Include token balances
   * @returns Balance information
   */
  async getBalance(address: string, includeTokens: boolean = false): Promise<BalanceResponse> {
    const response = await this.request('GET', `/balances/${address}`, undefined, {
      includeTokens: includeTokens.toString(),
    });

    return response;
  }

  /**
   * Get UTXOs for an address.
   *
   * @param address - Address to query
   * @param options - Query options
   * @returns List of UTXOs
   */
  async getUtxos(address: string, options?: GetUtxosOptions): Promise<UTXO[]> {
    const params: Record<string, string> = {};
    if (options?.minConfirmations) {
      params.minConfirmations = options.minConfirmations.toString();
    }
    if (options?.minAmount) {
      params.minAmount = options.minAmount;
    }

    const response = await this.request('GET', `/utxos/${address}`, undefined, params);
    return response.utxos;
  }

  /**
   * Build a transaction (without signing).
   *
   * @param walletId - Wallet ID
   * @param options - Transaction building options
   * @returns Unsigned transaction
   */
  async buildTransaction(walletId: string, options: BuildTransactionOptions): Promise<Transaction> {
    const response = await this.request('POST', `/wallets/${walletId}/build-tx`, {
      to: options.to,
      amount: options.amount,
      feeRate: options.feeRate,
      utxos: options.utxos,
      changeAddress: options.changeAddress,
    });

    return response.transaction;
  }

  /**
   * Build and sign a transaction in one step.
   *
   * @param walletId - Wallet ID
   * @param options - Transaction building options
   * @returns Signed transaction
   */
  async sendTransaction(walletId: string, options: BuildTransactionOptions): Promise<SignedTransaction> {
    const tx = await this.buildTransaction(walletId, options);
    return this.signTransaction(walletId, tx);
  }

  /**
   * Estimate transaction fee.
   *
   * @param priority - Priority level
   * @returns Fee estimate
   */
  async estimateFee(priority: Priority = 'medium'): Promise<FeeEstimate> {
    const response = await this.request('GET', '/fees/estimate', undefined, {
      priority,
    });

    return response;
  }

  /**
   * Get all fee estimates.
   *
   * @returns Fee estimates for all priority levels
   */
  async getAllFeeEstimates(): Promise<FeeEstimate[]> {
    const response = await this.request('GET', '/fees/estimate/all');
    return response.estimates;
  }

  /**
   * Make an API request.
   */
  private async request(
    method: string,
    path: string,
    body?: unknown,
    params?: Record<string, string>
  ): Promise<any> {
    let url = `${this.config.endpoint}${path}`;

    if (params && Object.keys(params).length > 0) {
      const searchParams = new URLSearchParams(params);
      url += `?${searchParams.toString()}`;
    }

    if (this.config.debug) {
      console.log(`[SynorWallet] ${method} ${url}`);
    }

    const response = await fetch(url, {
      method,
      headers: {
        'Authorization': `Bearer ${this.config.apiKey}`,
        'Content-Type': 'application/json',
        'X-Network': this.config.network,
      },
      body: body ? JSON.stringify(body) : undefined,
      signal: AbortSignal.timeout(this.config.timeout),
    });

    if (!response.ok) {
      const error = await response.json().catch(() => ({ message: response.statusText }));
      throw new WalletError(
        error.message || 'Request failed',
        response.status,
        error.code
      );
    }

    return response.json();
  }
}
