import { ChainType, chainIdToChainTypeMap, type SupportedChain } from '../chains.js';
import { EvmValidator } from '../core/evm/validator.js';
import type { CreateCrossChainOrderParams } from '../core/orders/cross-chain.js';
import type { CreateSingleChainOrderParams } from '../core/orders/single-chain.js';
import { SolanaValidator } from '../core/solana/validator.js';
import { SuiValidator } from '../core/sui/validator.js';
import { ValidationError } from '../errors/index.js';

export interface IAddressValidator {
  isValidAddress(address: string): boolean;
}

export interface ITokenValidator {
  isValidTokenAddress(tokenAddress: string): boolean;
}

export interface IAmountValidator {
  isValidAmount(amount: bigint): boolean;
}

export interface IDeadlineValidator {
  isValidDeadline(deadline: number): boolean;
}

export interface IChainValidator extends IAddressValidator, ITokenValidator, IAmountValidator {
  validateSourceChain(order: CreateCrossChainOrderParams & { user: string }): Promise<void>;
  validateDestinationChain(order: CreateCrossChainOrderParams): void;
  validateSingleChainOrder(order: CreateSingleChainOrderParams & { user: string }): Promise<void>;
}

/**
 * CommonValidator - Handles validations shared across chains
 */
export class CommonValidator implements IDeadlineValidator {
  isValidDeadline(deadline: number): boolean {
    return deadline > Math.floor(Date.now() / 1000);
  }

  validateDeadline(deadline: number): void {
    if (!this.isValidDeadline(deadline)) {
      throw new ValidationError('Deadline must be in the future');
    }
  }
}

export class ValidatorFactory {
  private static validators: Map<ChainType, IChainValidator> = new Map();

  static getValidator(chainType: ChainType): IChainValidator {
    if (!this.validators.has(chainType)) {
      switch (chainType) {
        case ChainType.EVM:
          this.validators.set(chainType, new EvmValidator());
          break;
        case ChainType.Solana:
          this.validators.set(chainType, new SolanaValidator());
          break;
        case ChainType.Sui:
          this.validators.set(chainType, new SuiValidator());
          break;
        default:
          throw new ValidationError(`Chain type ${chainType} validation is not implemented.`);
      }
    }

    return this.validators.get(chainType)!;
  }

  static getValidatorForChain(chainId: SupportedChain): IChainValidator {
    const chainType = chainIdToChainTypeMap[chainId];
    return this.getValidator(chainType);
  }
}

export class CrossChainOrderValidator {
  private readonly commonValidator = new CommonValidator();

  async validateOrder(order: CreateCrossChainOrderParams & { user: string }): Promise<void> {
    const sourceValidator = ValidatorFactory.getValidatorForChain(order.sourceChainId);

    await sourceValidator.validateSourceChain({ ...order, user: order.user });

    const destinationValidator = ValidatorFactory.getValidatorForChain(order.destinationChainId);

    destinationValidator.validateDestinationChain(order);

    this.commonValidator.validateDeadline(order.deadline);
  }
}

export class SingleChainOrderValidator {
  private readonly commonValidator = new CommonValidator();

  async validateOrder(order: CreateSingleChainOrderParams & { user: string }): Promise<void> {
    this.commonValidator.validateDeadline(order.deadline);

    const chainValidator = ValidatorFactory.getValidatorForChain(order.chainId);

    chainValidator.validateSingleChainOrder(order);
  }
}
