import { ref, type Ref } from 'vue';
import axios from 'axios';
import { chainConfigService } from '@/services/chainConfig';

const RETRY_BASE_DELAY_MS = 1_500;
const RETRY_MAX_DELAY_MS = 60_000;

export interface RetryManager {
  schedule: () => void;
  clear: () => void;
  reset: () => void;
  retryDelayMs: Ref<number>;
}

/**
 * Creates a retry manager with exponential backoff for async operations.
 *
 * @param loadFn - The async function to retry (receives activeChainId as argument)
 * @param activeChainId - Ref containing the current chain ID to pass to loadFn
 * @param baseDelay - Initial retry delay in milliseconds (default: 1500ms)
 * @param maxDelay - Maximum retry delay in milliseconds (default: 60000ms)
 * @returns RetryManager with schedule, clear, and reset methods
 *
 * @example
 * const retry = createRetryManager(loadPositions, activeChainId);
 * try {
 *   await loadPositions(chainId);
 *   retry.reset(); // Reset on success
 * } catch (error) {
 *   retry.schedule(); // Schedule retry with backoff
 * }
 */
export function createRetryManager(
  loadFn: (chainId: number) => Promise<void>,
  activeChainId: Ref<number>,
  baseDelay = RETRY_BASE_DELAY_MS,
  maxDelay = RETRY_MAX_DELAY_MS
): RetryManager {
  const retryDelayMs = ref(baseDelay);
  let retryTimer: number | null = null;

  function clear() {
    if (retryTimer !== null) {
      clearTimeout(retryTimer);
      retryTimer = null;
    }
  }

  function schedule() {
    if (typeof window === 'undefined' || retryTimer !== null) {
      return;
    }
    const delay = retryDelayMs.value;
    retryTimer = window.setTimeout(async () => {
      retryTimer = null;
      await loadFn(activeChainId.value);
    }, delay);
    retryDelayMs.value = Math.min(retryDelayMs.value * 2, maxDelay);
  }

  function reset() {
    retryDelayMs.value = baseDelay;
  }

  return { schedule, clear, reset, retryDelayMs };
}

/**
 * Formats GraphQL/Axios errors into user-friendly messages.
 *
 * @param error - The error object from axios or other source
 * @returns Formatted error message string
 */
export function formatGraphqlError(error: unknown): string {
  if (axios.isAxiosError(error)) {
    const responseErrors = (error.response?.data as { errors?: unknown[] })?.errors;
    if (Array.isArray(responseErrors) && responseErrors.length > 0) {
      return responseErrors.map((err: unknown) => (err as { message?: string })?.message ?? 'GraphQL error').join(', ');
    }
    if (error.response?.status) {
      return `GraphQL request failed with status ${error.response.status}`;
    }
    if (error.message) {
      return error.message;
    }
  }
  if (error instanceof Error && error.message) {
    return error.message;
  }
  return 'Unknown GraphQL error';
}

/**
 * Resolves a GraphQL endpoint URL for a given chain ID.
 *
 * @param chainId - The blockchain network ID
 * @param endpointOverride - Optional endpoint URL to use instead of default
 * @returns The GraphQL endpoint URL
 * @throws Error if endpoint is not configured for the chain
 */
export function resolveGraphqlEndpoint(chainId: number, endpointOverride?: string): string {
  const normalized = endpointOverride?.trim();
  if (normalized) {
    return normalized;
  }
  return chainConfigService.getEndpoint(chainId, 'graphql');
}
