import type { CreateCrossChainOrderParams } from '../core/orders/cross-chain.js';
import type { CreateSingleChainOrderParams } from '../core/orders/single-chain.js';
import { ValidationError } from '../errors/index.js';
import type { IChainValidator } from './order-validator.js';

/**
 * BaseValidator - Abstract class implementing common validation logic
 * This follows the Template Method pattern
 */
export abstract class BaseValidator implements IChainValidator {
  abstract isValidAddress(address: string): boolean;
  abstract isValidTokenAddress(tokenAddress: string): boolean;
  abstract isValidAmount(amount: bigint): boolean;
  protected abstract getChainName(): string;
  abstract validateCrossChainOrderFeasability(order: CreateCrossChainOrderParams & { user: string }): Promise<void>;
  abstract validateSingleChainOrderFeasability(order: CreateSingleChainOrderParams & { user: string }): Promise<void>;

  /**
   * Validate source chain specific parameters of the order
   */
  async validateSourceChain(order: CreateCrossChainOrderParams & { user: string }): Promise<void> {
    this.validateTokenAddress(order.sourceTokenAddress);
    this.validateAmount(order.sourceTokenAmount);
    this.validateAmount(order.minStablecoinAmount ?? 1n);
    await this.validateCrossChainOrderFeasability({ ...order, user: order.user });
  }

  async validateSingleChainOrder(order: CreateSingleChainOrderParams & { user: string }): Promise<void> {
    this.validateTokenAddress(order.tokenIn);
    this.validateTokenAddress(order.tokenOut);
    this.validateAmount(order.amountIn);
    this.validateAmount(order.amountOutMin ?? 1n);

    if (order.stopLossMaxOut) {
      this.validateAmount(order.stopLossMaxOut);
    }

    if (order.takeProfitMinOut) {
      this.validateAmount(order.takeProfitMinOut);
    }

    await this.validateSingleChainOrderFeasability(order);
  }

  /**
   * Validate destination chain specific parameters of the order
   */
  validateDestinationChain(order: CreateCrossChainOrderParams): void {
    this.validateAddress(order.destinationAddress);
    this.validateTokenAddress(order.destinationTokenAddress);
    this.validateAmount(order.destinationTokenMinAmount ?? 1n);

    if (order.extraTransfers) {
      for (const extraTransfer of order.extraTransfers) {
        this.validateAddress(extraTransfer.receiver);
        this.validateTokenAddress(extraTransfer.token);
        this.validateAmount(extraTransfer.amount);
      }
    }
  }

  /**
   * Helper methods that throw specific validation errors
   */
  protected validateAddress(address: string): void {
    if (!this.isValidAddress(address)) {
      throw new ValidationError(`Invalid ${this.getChainName()} address: ${address}`);
    }
  }

  protected validateTokenAddress(tokenAddress: string): void {
    if (!this.isValidTokenAddress(tokenAddress)) {
      throw new ValidationError(`Invalid ${this.getChainName()} token address: ${tokenAddress}`);
    }
  }

  protected validateAmount(amount: bigint): void {
    if (!this.isValidAmount(amount)) {
      throw new ValidationError(`Amount must be greater than 0. Value: ${amount}`);
    }
  }
}
