const API_BASE_URL =
  typeof window !== "undefined" &&
  window.location.href.includes("stage.enclave.money")
    ? "https://enclave-b2b-api-stage-160004845895.asia-south1.run.app"
    : "https://hyperapp.in";

// Import necessary types from services.ts
import { QuoteType, QuoteChoice } from "./services";

// Define action interface
export interface IAction {
  action:
    | string
    | {
        encodedData: string;
        targetContractAddress: string;
        value: string;
      }[];
}

// API request interfaces
export interface getSourceTokensFromIdentifierRequest {
  body: {
    chainId: number;
    tokenAddress: string;
  };
}

export interface CalculateQuoteAmountInRequest {
  body: {
    username: string;
    fromTokens: UserBalance[];
    toToken: {
      tokenAddress: string;
      chainId: number;
    };
    quoteType: QuoteType;
    action?: IAction["action"];
  };
}

export interface CalculateQuoteAmountOutRequest {
  body: {
    username: string;
    fromTokens: getSourceTokensFromIdentifierRequest["body"][];
    toToken: {
      tokenAddress: string;
      chainId: number;
      outputAmount: string;
    };
    quoteType: QuoteType;
    action?: IAction["action"];
  };
}

// Add interfaces for calculateQuoteAmountInWithAction function
export interface UserBalance {
  chainId: number;
  address: string;
  balance: string;
}

export interface CalculateQuoteAmountInInput {
  fromTokens: UserBalance[];
  toToken: {
    tokenAddress: string;
    chainId: number;
  };
  action: IAction["action"];
}

export interface CalculateQuoteAmountInParams {
  username: string;
  fromTokens: UserBalance[];
  toToken: {
    tokenAddress: string;
    chainId: number;
  };
  quoteType: QuoteType;
  action?: IAction["action"];
}

export interface ModifiedSpendPlan {
  total_fees: string;
  spend_distribution: Record<string, string>;
  total_withdrawn: string;
  remaining_balance: string;
  enclave_fee_distribution: Record<string, string>;
  enclave_fee: string;
}

export interface QuoteDetailsWithAction {
  success: boolean;
  provider: string;
  fees: string;
  estimatedTime: number;
  inputDetails: {
    amount: string;
    amountUsd: string | number;
  };
  outputDetails: {
    amount: string;
    amountFormatted: string;
    amountUsd: string;
    minimumAmount: string;
  };
  rate: string;
  modifiedSpendPlan: ModifiedSpendPlan;
}

export interface CalculateQuoteResponse {
  success: boolean;
  bestQuote: QuoteDetailsWithAction;
  fastestQuote: QuoteDetailsWithAction;
}

// Add interfaces for calculateQuoteAmountOut function
export interface CalculateQuoteAmountOutInput {
  fromTokens: getSourceTokensFromIdentifierRequest["body"][];
  toToken: {
    tokenAddress: string;
    chainId: number;
    outputAmount: string;
  };
  quoteType: QuoteType;
  action?: IAction["action"];
}

export interface CalculateQuoteAmountOutParams {
  username: string;
  fromTokens: getSourceTokensFromIdentifierRequest["body"][];
  toToken: {
    tokenAddress: string;
    chainId: number;
    outputAmount: string;
  };
  quoteType: QuoteType;
  action?: IAction["action"];
}

// Add interfaces for executeSwapAmountIn function
export interface ExecuteSwapAmountInRequest {
  body: {
    username: string;
    fromTokens: UserBalance[];
    toToken: {
      tokenAddress: string;
      chainId: number;
    };
    isHeadless: boolean;
    quoteChoice: QuoteChoice;
    quoteType: QuoteType;
    quoteId?: string;
    action?: IAction["action"];
  };
}

export interface ExecuteSwapAmountInInput {
  fromTokens: UserBalance[];
  toToken: {
    tokenAddress: string;
    chainId: number;
  };
  isHeadless: boolean;
  quoteChoice: QuoteChoice;
  quoteType: QuoteType;
  quoteId?: string;
  action?: IAction["action"];
}

export interface ExecuteSwapAmountInParams {
  username: string;
  fromTokens: UserBalance[];
  toToken: {
    tokenAddress: string;
    chainId: number;
  };
  isHeadless: boolean;
  quoteChoice: QuoteChoice;
  quoteType: QuoteType;
  quoteId?: string;
  action?: IAction["action"];
}

export interface ExecuteSwapAmountInResponse {
  success: boolean;
  transactionDetails?: {
    multiTransactionId: string;
    overallStatus: string;
  };
  // Add other response fields as needed
}

// Add interfaces for executeSwapAmountOut function
export interface ExecuteSwapAmountOutRequest {
  body: {
    username: string;
    fromTokens: getSourceTokensFromIdentifierRequest["body"][];
    toToken: {
      tokenAddress: string;
      chainId: number;
      outputAmount: string;
    };
    isHeadless: boolean;
    quoteChoice: QuoteChoice;
    quoteType: QuoteType;
    quoteId?: string;
    action?: IAction["action"];
  };
}

export interface ExecuteSwapAmountOutInput {
  fromTokens: UserBalance[];
  toToken: {
    tokenAddress: string;
    chainId: number;
    outputAmount: string;
  };
  isHeadless: boolean;
  quoteChoice: QuoteChoice;
  quoteType: QuoteType;
  quoteId?: string;
  action?: IAction["action"];
}

export interface ExecuteSwapAmountOutParams {
  username: string;
  fromTokens: getSourceTokensFromIdentifierRequest["body"][];
  toToken: {
    tokenAddress: string;
    chainId: number;
    outputAmount: string;
  };
  isHeadless: boolean;
  quoteChoice: QuoteChoice;
  quoteType: QuoteType;
  quoteId?: string;
  action?: IAction["action"];
}

export interface ExecuteSwapAmountOutResponse {
  success: boolean;
  transactionDetails?: {
    multiTransactionId: string;
    overallStatus: string;
  };
  // Add other response fields as needed
}

// Unified interface for calculateQuote function
export interface CalculateQuoteInput {
  fromTokens: UserBalance[];
  toToken: {
    tokenAddress: string;
    chainId: number;
    outputAmount?: string; // Optional for AMOUNT_IN, required for AMOUNT_OUT
  };
  quoteType: QuoteType;
  action?: IAction["action"];
}

export interface CalculateQuoteParams {
  username: string;
  fromTokens: UserBalance[] | getSourceTokensFromIdentifierRequest["body"][];
  toToken: {
    tokenAddress: string;
    chainId: number;
    outputAmount?: string;
  };
  quoteType: QuoteType;
  action?: IAction["action"];
}

// Unified interface for executeSwap function
export interface ExecuteSwapInput {
  fromTokens: UserBalance[];
  toToken: {
    tokenAddress: string;
    chainId: number;
    outputAmount?: string; // Optional for AMOUNT_IN, required for AMOUNT_OUT
  };
  isHeadless: boolean;
  quoteChoice: QuoteChoice;
  quoteType: QuoteType;
  quoteId?: string;
  action?: IAction["action"];
}

export interface ExecuteSwapParams {
  username: string;
  fromTokens: UserBalance[] | getSourceTokensFromIdentifierRequest["body"][];
  toToken: {
    tokenAddress: string;
    chainId: number;
    outputAmount?: string;
  };
  isHeadless: boolean;
  quoteChoice: QuoteChoice;
  quoteType: QuoteType;
  quoteId?: string;
  action?: IAction["action"];
}

export type ExecuteSwapResponse =
  | ExecuteSwapAmountInResponse
  | ExecuteSwapAmountOutResponse;

// Add interfaces for getQuoteForActionAmountOut function
export interface CalculateQuoteForActionAmountOutRequest {
  body: {
    username: string;
    fromTokens?: getSourceTokensFromIdentifierRequest["body"][];
    toToken: {
      tokenAddress: string;
      chainId: number;
    };
    outputAmount: string;
    quoteType: QuoteType;
    action?: IAction["action"];
  };
}

export interface GetQuoteForActionInput {
  fromTokens?: getSourceTokensFromIdentifierRequest["body"][];
  toToken: {
    tokenAddress: string;
    chainId: number;
  };
  outputAmount: string;
  quoteType: QuoteType;
  action?: IAction["action"];
}

export interface GetQuoteForActionParams {
  username: string;
  fromTokens?: getSourceTokensFromIdentifierRequest["body"][];
  toToken: {
    tokenAddress: string;
    chainId: number;
  };
  outputAmount: string;
  quoteType: QuoteType;
  action?: IAction["action"];
}

export interface GetQuoteForActionResponse {
  success: boolean;
  bestQuote: QuoteDetailsWithAction;
  fastestQuote: QuoteDetailsWithAction;
}

// Add interfaces for executeAction function
export interface ExecuteActionAmountOutRequest {
  body: {
    quoteChoice: QuoteChoice;
    quoteId: string;
  };
}

export interface ExecuteActionInput {
  quoteChoice: QuoteChoice;
  quoteId: string;
  username: string;
  fromTokens?: getSourceTokensFromIdentifierRequest["body"][];
  toToken?: {
    tokenAddress: string;
    chainId: number;
  };
  outputAmount?: string;
  action?: IAction["action"];
}

export interface ExecuteActionParams {
  quoteChoice: QuoteChoice;
  quoteId: string;
  username: string;
  fromTokens?: getSourceTokensFromIdentifierRequest["body"][];
  toToken?: {
    tokenAddress: string;
    chainId: number;
  };
  outputAmount?: string;
  action?: IAction["action"];
}

export interface ExecuteActionResponse {
  success: boolean;
  transactionDetails?: {
    multiTransactionId: string;
    overallStatus: string;
  };
  // Add other response fields as needed
}

/**
 * Calculates quote amount in with action
 * @param params The request parameters
 * @param apiKey The wallet SDK key for authorization
 * @returns The calculate quote amount in with action response
 */
export const calculateQuoteAmountIn = async (
  params: CalculateQuoteAmountInParams,
  apiKey: string
): Promise<CalculateQuoteResponse | null> => {
  try {
    // Construct the request body in the format expected by the API
    const requestBody: CalculateQuoteAmountInRequest = {
      body: {
        username: params.username,
        fromTokens: params.fromTokens,
        toToken: params.toToken,
        quoteType: params.quoteType,
        ...(params.action && { action: params.action }),
      },
    };

    const response = await fetch(`${API_BASE_URL}/sdk/calculateQuoteAmountIn`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: apiKey,
      },
      body: JSON.stringify(requestBody),
    });

    if (!response.ok) {
      throw new Error("Failed to calculate quote amount in");
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error("Error calculating quote amount in:", error);
    return null;
  }
};

/**
 * Calculates quote amount out
 * @param params The request parameters
 * @param apiKey The wallet SDK key for authorization
 * @returns The calculate quote amount out response
 */
export const calculateQuoteAmountOut = async (
  params: CalculateQuoteAmountOutParams,
  apiKey: string
): Promise<CalculateQuoteResponse | null> => {
  try {
    // Use the appropriate endpoint based on whether action is provided
    const endpoint = params.action
      ? `${API_BASE_URL}/sdk/calculateQuoteAmountOutWithAction`
      : `${API_BASE_URL}/sdk/calculateQuoteAmountOut`;

    // Construct the request body in the format expected by the API
    const requestBody: CalculateQuoteAmountOutRequest = {
      body: {
        username: params.username,
        fromTokens: params.fromTokens,
        toToken: params.toToken,
        quoteType: params.quoteType,
        ...(params.action && { action: params.action }),
      },
    };

    const response = await fetch(endpoint, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: apiKey,
      },
      body: JSON.stringify(requestBody),
    });

    if (!response.ok) {
      throw new Error("Failed to calculate quote amount out");
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error("Error calculating quote amount out:", error);
    return null;
  }
};

/**
 * Executes swap amount in
 * @param params The request parameters
 * @param apiKey The wallet SDK key for authorization
 * @returns The execute swap amount in response
 */
export const executeSwapAmountIn = async (
  params: ExecuteSwapAmountInParams,
  apiKey: string
): Promise<ExecuteSwapAmountInResponse | null> => {
  try {
    // Get userSession from SDK instance
    const sdkInstance = (window as any).__walletSDKInstance;
    const userSession =
      sdkInstance?.getUserSession?.() || sdkInstance?.userSession;

    // Determine auth type based on userSession
    const authType = userSession?.authMethod || "passkey";
    const authToken = userSession?.token;

    // Construct the request body in the format expected by the API
    const requestBody: ExecuteSwapAmountInRequest = {
      body: {
        username: params.username,
        fromTokens: params.fromTokens,
        toToken: params.toToken,
        isHeadless: params.isHeadless,
        quoteChoice: params.quoteChoice,
        quoteType: params.quoteType,
        ...(params.quoteId && { quoteId: params.quoteId }),
        ...(params.action && { action: params.action }),
      },
    };

    const response = await fetch(`${API_BASE_URL}/sdk/executeSwapAmountIn`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-user-auth": authType,
        "x-user-authToken": authToken,
        Authorization: apiKey,
      },
      body: JSON.stringify(requestBody),
    });

    if (!response.ok) {
      throw new Error("Failed to execute swap amount in");
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error("Error executing swap amount in:", error);
    return null;
  }
};

/**
 * Executes swap amount out
 * @param params The request parameters
 * @param apiKey The wallet SDK key for authorization
 * @returns The execute swap amount out response
 */
export const executeSwapAmountOut = async (
  params: ExecuteSwapAmountOutParams,
  apiKey: string
): Promise<ExecuteSwapAmountOutResponse | null> => {
  try {
    // Get userSession from SDK instance
    const sdkInstance = (window as any).__walletSDKInstance;
    const userSession =
      sdkInstance?.getUserSession?.() || sdkInstance?.userSession;

    // Determine auth type based on userSession
    const authType = userSession?.authMethod || "passkey";
    const authToken = userSession?.token;

    // Construct the request body in the format expected by the API
    const requestBody: ExecuteSwapAmountOutRequest = {
      body: {
        username: params.username,
        fromTokens: params.fromTokens,
        toToken: params.toToken,
        isHeadless: params.isHeadless,
        quoteChoice: params.quoteChoice,
        quoteType: params.quoteType,
        ...(params.quoteId && { quoteId: params.quoteId }),
        ...(params.action && { action: params.action }),
      },
    };

    const response = await fetch(`${API_BASE_URL}/sdk/executeSwapAmountOut`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-user-auth": authType,
        "x-user-authToken": authToken,
        Authorization: apiKey,
      },
      body: JSON.stringify(requestBody),
    });

    if (!response.ok) {
      throw new Error("Failed to execute swap amount out");
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error("Error executing swap amount out:", error);
    return null;
  }
};

/**
 * Unified execute swap function that routes to appropriate endpoint based on quoteType
 * @param params The request parameters
 * @param apiKey The wallet SDK key for authorization
 * @returns The execute swap response
 */
export const executeSwapQuote = async (
  params: ExecuteSwapParams,
  apiKey: string
): Promise<ExecuteSwapResponse | null> => {
  try {
    if (params.quoteType === QuoteType.AMOUNT_IN) {
      // For AMOUNT_IN, use executeSwapAmountIn
      const amountInParams: ExecuteSwapAmountInParams = {
        username: params.username,
        fromTokens: params.fromTokens as UserBalance[],
        toToken: {
          tokenAddress: params.toToken.tokenAddress,
          chainId: params.toToken.chainId,
        },
        isHeadless: params.isHeadless,
        quoteChoice: params.quoteChoice,
        quoteType: params.quoteType,
        quoteId: params.quoteId,
        action: params.action,
      };
      return await executeSwapAmountIn(amountInParams, apiKey);
    } else if (params.quoteType === QuoteType.AMOUNT_OUT) {
      // For AMOUNT_OUT, use executeSwapAmountOut
      if (!params.toToken.outputAmount) {
        throw new Error("outputAmount is required for AMOUNT_OUT quote type");
      }

      const amountOutParams: ExecuteSwapAmountOutParams = {
        username: params.username,
        fromTokens:
          Array.isArray(params.fromTokens) &&
          params.fromTokens.length > 0 &&
          "balance" in params.fromTokens[0]
            ? (params.fromTokens as UserBalance[]).map((token) => ({
                chainId: token.chainId,
                tokenAddress: token.address,
              }))
            : (params.fromTokens as getSourceTokensFromIdentifierRequest["body"][]),
        toToken: {
          tokenAddress: params.toToken.tokenAddress,
          chainId: params.toToken.chainId,
          outputAmount: params.toToken.outputAmount,
        },
        isHeadless: params.isHeadless,
        quoteChoice: params.quoteChoice,
        quoteType: params.quoteType,
        quoteId: params.quoteId,
        action: params.action,
      };
      return await executeSwapAmountOut(amountOutParams, apiKey);
    } else {
      throw new Error(`Unsupported quote type: ${params.quoteType}`);
    }
  } catch (error) {
    console.error("Error executing swap:", error);
    return null;
  }
};

/**
 * Unified calculate quote function that routes to appropriate endpoint based on quoteType
 * @param params The request parameters
 * @param apiKey The wallet SDK key for authorization
 * @returns The calculate quote response
 */
export const calculateQuote = async (
  params: CalculateQuoteParams,
  apiKey: string
): Promise<CalculateQuoteResponse | null> => {
  try {
    if (params.quoteType === QuoteType.AMOUNT_IN) {
      // For AMOUNT_IN, use calculateQuoteAmountIn
      const amountInParams: CalculateQuoteAmountInParams = {
        username: params.username,
        fromTokens: params.fromTokens as UserBalance[],
        toToken: {
          tokenAddress: params.toToken.tokenAddress,
          chainId: params.toToken.chainId,
        },
        quoteType: params.quoteType,
        action: params.action,
      };
      return await calculateQuoteAmountIn(amountInParams, apiKey);
    } else if (params.quoteType === QuoteType.AMOUNT_OUT) {
      // For AMOUNT_OUT, use calculateQuoteAmountOut
      if (!params.toToken.outputAmount) {
        throw new Error("outputAmount is required for AMOUNT_OUT quote type");
      }

      const amountOutParams: CalculateQuoteAmountOutParams = {
        username: params.username,
        fromTokens:
          Array.isArray(params.fromTokens) &&
          params.fromTokens.length > 0 &&
          "balance" in params.fromTokens[0]
            ? (params.fromTokens as UserBalance[]).map((token) => ({
                chainId: token.chainId,
                tokenAddress: token.address,
              }))
            : (params.fromTokens as getSourceTokensFromIdentifierRequest["body"][]),
        toToken: {
          tokenAddress: params.toToken.tokenAddress,
          chainId: params.toToken.chainId,
          outputAmount: params.toToken.outputAmount,
        },
        quoteType: params.quoteType,
        action: params.action,
      };
      return await calculateQuoteAmountOut(amountOutParams, apiKey);
    } else {
      throw new Error(`Unsupported quote type: ${params.quoteType}`);
    }
  } catch (error) {
    console.error("Error calculating quote:", error);
    return null;
  }
};

/**
 * Gets quote for action amount out
 * @param params The request parameters
 * @param apiKey The wallet SDK key for authorization
 * @returns The get quote for action response
 */
export const getQuoteForActionAmountOut = async (
  params: GetQuoteForActionParams,
  apiKey: string
): Promise<GetQuoteForActionResponse | null> => {
  try {
    // Construct the request body in the format expected by the API
    const requestBody: CalculateQuoteForActionAmountOutRequest = {
      body: {
        username: params.username,
        ...(params.fromTokens && { fromTokens: params.fromTokens }),
        toToken: params.toToken,
        outputAmount: params.outputAmount,
        quoteType: params.quoteType,
        ...(params.action && { action: params.action }),
      },
    };

    const response = await fetch(
      `${API_BASE_URL}/sdk/getQuoteForActionAmountOut`,
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: apiKey,
        },
        body: JSON.stringify(requestBody),
      }
    );

    if (!response.ok) {
      throw new Error("Failed to get quote for action amount out");
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error("Error getting quote for action amount out:", error);
    return null;
  }
};

/**
 * Executes action amount out
 * @param params The request parameters
 * @param apiKey The wallet SDK key for authorization
 * @returns The execute action response
 */
export const executeActionAmountOut = async (
  params: ExecuteActionParams,
  apiKey: string
): Promise<ExecuteActionResponse | null> => {
  try {
    // Get userSession from SDK instance
    const sdkInstance = (window as any).__walletSDKInstance;
    const userSession =
      sdkInstance?.getUserSession?.() || sdkInstance?.userSession;

    // Determine auth type based on userSession
    const authType = userSession?.authMethod || "passkey";
    const authToken = userSession?.token;

    // Construct the request body in the format expected by the API
    const requestBody: ExecuteActionAmountOutRequest = {
      body: params,
    };

    const response = await fetch(`${API_BASE_URL}/sdk/executeActionAmountOut`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-user-auth": authType,
        "x-user-authToken": authToken,
        Authorization: apiKey,
      },
      body: JSON.stringify(requestBody),
    });

    if (!response.ok) {
      throw new Error("Failed to execute action amount out");
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error("Error executing action amount out:", error);
    return null;
  }
};
