import { AUCTIONEER_URL } from '../constants.js';
import { NetworkError } from '../errors/index.js';
import type { ApiResponse, ApiUserOrders } from '../types/api.js';
import type {
  CrossChainOrderPrepared,
  CrossChainUserIntentRequest,
  SingleChainOrderPrepared,
  SingleChainUserIntentRequest,
} from '../types/intent.js';
import { Parsers } from '../utils/parsers.js';
import { CrossChainOrder, type CreateCrossChainOrderParams } from './orders/cross-chain.js';
import { SingleChainOrder, type CreateSingleChainOrderParams } from './orders/single-chain.js';

/**
 * Base SDK providing common functionality for all blockchain implementations
 *
 * This abstract class serves as the foundation for chain-specific SDK implementations
 * (EVM, Solana, Sui), providing shared functionality for creating and sending orders
 * across different blockchains.
 */
export abstract class BaseSDK {
  public abstract getOrders(): Promise<ApiUserOrders>;
  public abstract getUserAddress(): Promise<string>;
  protected abstract prepareCrossChainOrder(order: CrossChainOrder): Promise<CrossChainOrderPrepared>;
  protected abstract prepareSingleChainOrder(order: SingleChainOrder): Promise<SingleChainOrderPrepared>;

  public async createSingleChainOrder(params: CreateSingleChainOrderParams): Promise<SingleChainOrderPrepared> {
    const userAddress = await this.getUserAddress();

    const order = await SingleChainOrder.create({
      ...params,
      user: userAddress,
    });

    return this.prepareSingleChainOrder(order);
  }

  public async createCrossChainOrder(params: CreateCrossChainOrderParams): Promise<CrossChainOrderPrepared> {
    const userAddress = await this.getUserAddress();

    const order = await CrossChainOrder.create({
      ...params,
      user: userAddress,
    });

    return this.prepareCrossChainOrder(order);
  }

  public async createAndSendSingleChainOrder(params: CreateSingleChainOrderParams): Promise<ApiResponse> {
    const preparedOrder = await this.createSingleChainOrder(params);
    return BaseSDK.sendSingleChainOrder(preparedOrder);
  }

  public static async sendSingleChainOrder(preparedOrder: SingleChainOrderPrepared): Promise<ApiResponse> {
    const intentRequest = preparedOrder.order.toIntentRequest(preparedOrder.preparedData);
    const body = JSON.stringify(intentRequest, Parsers.bigIntReplacer);

    const url = `${AUCTIONEER_URL}/user_intent/single_chain/limit_order`;
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body,
    });

    if (!response.ok) {
      const errorJson: ApiResponse = await response.json();

      const extraErrorData = errorJson.extra_error_data;
      throw new NetworkError(`Server error ${extraErrorData}`);
    }

    return response.json();
  }

  public static async validateCrossChainOrder(intentRequest: CrossChainUserIntentRequest): Promise<void> {
    const body = JSON.stringify(intentRequest, Parsers.bigIntReplacer);

    const url = `${AUCTIONEER_URL}/validate_intent/cross_chain/limit_order`;
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body,
    });

    if (!response.ok) {
      const errorJson: ApiResponse = await response.json();

      const mainError = errorJson.error;
      const extraErrorData = errorJson.extra_error_data;
      throw new NetworkError(`Validation error: ${mainError}. Extra error data: ${extraErrorData}`);
    }

    return;
  }

  public static async validateSingleChainOrder(intentRequest: SingleChainUserIntentRequest): Promise<void> {
    const body = JSON.stringify(intentRequest, Parsers.bigIntReplacer);

    const url = `${AUCTIONEER_URL}/validate_intent/single_chain/limit_order`;
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body,
    });

    if (!response.ok) {
      const errorJson: ApiResponse = await response.json();

      const mainError = errorJson.error;
      const extraErrorData = errorJson.extra_error_data;
      throw new NetworkError(`Validation error: ${mainError}. Extra error data: ${extraErrorData}`);
    }

    return;
  }

  public async createAndSendCrossChainOrder(params: CreateCrossChainOrderParams): Promise<ApiResponse> {
    const preparedOrder = await this.createCrossChainOrder(params);
    return BaseSDK.sendCrossChainOrder(preparedOrder);
  }

  public static async sendCrossChainOrder(preparedOrder: CrossChainOrderPrepared): Promise<ApiResponse> {
    const intentRequest = preparedOrder.order.toIntentRequest(preparedOrder.preparedData);
    const body = JSON.stringify(intentRequest, Parsers.bigIntReplacer);

    const url = `${AUCTIONEER_URL}/user_intent/cross_chain/limit_order`;
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body,
    });

    if (!response.ok) {
      const errorJson: ApiResponse = await response.json();

      const mainError = errorJson.error;
      const extraErrorData = errorJson.extra_error_data;
      throw new NetworkError(
        `Failed to send cross-chain order. Main error: ${mainError}. Extra error data: ${extraErrorData}`,
      );
    }

    return response.json();
  }
}
