import createQueue from '../libs/queue';
import logger from '../libs/logger';
import { ExchangeRateProvider } from '../store/models/exchange-rate-provider';
import { getExchangeRateService } from '../libs/exchange-rate';
import { createEvent } from '../libs/audit';

// Representative tokens to test for health checks
// Note: USDC/USD are stablecoins pegged to USD, they don't need exchange rate provider checks
const REPRESENTATIVE_TOKENS = ['ABT', 'ETH'];

interface HealthCheckJob {
  type: 'health_check';
  timestamp: number;
}

interface HealthCheckResult {
  providerId: string;
  providerName: string;
  success: boolean;
  testedTokens: string[];
  failedTokens: string[];
  error?: string;
}

/**
 * Perform health check on all active exchange rate providers
 */
async function performHealthCheck(): Promise<HealthCheckResult[]> {
  logger.info('Starting exchange rate provider health check');

  const providers = await ExchangeRateProvider.findAll({
    where: { enabled: true },
    order: [['priority', 'ASC']],
  });

  if (providers.length === 0) {
    logger.warn('No enabled exchange rate providers found');
    return [];
  }

  const results: HealthCheckResult[] = [];
  const exchangeRateService = getExchangeRateService();

  // Note: Sequential provider testing is intentional for health check isolation
  // eslint-disable-next-line no-restricted-syntax
  for (const provider of providers) {
    const result: HealthCheckResult = {
      providerId: provider.id,
      providerName: provider.name,
      success: true,
      testedTokens: [],
      failedTokens: [],
    };

    let hasSuccess = false;

    // eslint-disable-next-line no-restricted-syntax
    for (const symbol of REPRESENTATIVE_TOKENS) {
      try {
        // Clear cache to force fresh fetch
        exchangeRateService.clearCache(symbol);

        // Test fetching rate
        // eslint-disable-next-line no-await-in-loop
        await exchangeRateService.getRate(symbol);

        result.testedTokens.push(symbol);
        hasSuccess = true;

        logger.debug('Health check success', {
          provider: provider.name,
          symbol,
        });
      } catch (error: any) {
        result.failedTokens.push(symbol);

        logger.warn('Health check failed for token', {
          provider: provider.name,
          symbol,
          error: error.message,
        });
      }
    }

    result.success = hasSuccess;
    if (!hasSuccess) {
      result.error = `Failed to fetch rates for all representative tokens: ${REPRESENTATIVE_TOKENS.join(', ')}`;
    }

    results.push(result);
  }

  return results;
}

/**
 * Send alert notification when all providers are unavailable
 */
async function sendProviderAlert(results: HealthCheckResult[]): Promise<void> {
  const allProvidersFailed = results.every((r) => !r.success);

  if (!allProvidersFailed) {
    return;
  }

  const alertData = {
    severity: 'critical',
    title: 'All Exchange Rate Providers Unavailable',
    message: 'All enabled exchange rate providers have failed health checks. Dynamic pricing is currently unavailable.',
    timestamp: new Date().toISOString(),
    providers: results.map((r) => ({
      id: r.providerId,
      name: r.providerName,
      failedTokens: r.failedTokens,
      error: r.error,
    })),
    impact: 'Users cannot create or view dynamic pricing quotes',
    action: 'Please check provider configurations and network connectivity',
  };

  // Log critical alert
  logger.error('CRITICAL: All exchange rate providers unavailable', alertData);

  // Create audit event - this will trigger webhooks and emit event internally
  try {
    await createEvent('System', 'exchange_rate.providers_unavailable', alertData, {});
  } catch (err) {
    logger.error('Failed to create audit event for provider alert', { error: err });
  }
}

/**
 * Log health check results
 *
 * Note: We don't call recordFailure/recordSuccess here because
 * exchangeRateService.getRate() already handles provider status updates internally.
 * Calling them here would cause duplicate updates (failure_count += 2 instead of 1).
 */
function logHealthCheckResults(results: HealthCheckResult[]): void {
  for (const result of results) {
    if (result.success) {
      logger.info('Provider health check passed', {
        provider: result.providerName,
        testedTokens: result.testedTokens,
      });
    } else {
      logger.warn('Provider health check failed', {
        provider: result.providerName,
        failedTokens: result.failedTokens,
        error: result.error,
      });
    }
  }
}

/**
 * Main health check job handler
 */
async function handleHealthCheckJob(job: HealthCheckJob): Promise<void> {
  logger.info('Executing exchange rate provider health check job', { job });

  try {
    const results = await performHealthCheck();

    // Log health check results (provider status already updated by service.getRate())
    logHealthCheckResults(results);

    // Send alert if all providers failed
    await sendProviderAlert(results);

    const failedCount = results.filter((r) => !r.success).length;
    const successCount = results.length - failedCount;

    logger.info('Health check completed', {
      total: results.length,
      success: successCount,
      failed: failedCount,
      timestamp: job.timestamp,
    });
  } catch (error: any) {
    logger.error('Health check job failed', {
      error: error.message,
      stack: error.stack,
    });
    throw error;
  }
}

// Create the queue with scheduled job support
export const exchangeRateHealthQueue = createQueue<HealthCheckJob>({
  name: 'exchange-rate-health',
  onJob: handleHealthCheckJob,
  options: {
    concurrency: 1,
    maxRetries: 3,
    retryDelay: 60 * 1000, // 1 minute
    enableScheduledJob: true,
  },
});

// Schedule health checks every 6 hours
const HEALTH_CHECK_INTERVAL = 6 * 60 * 60; // 6 hours in seconds

/**
 * Initialize and schedule periodic health checks
 */
export function scheduleHealthChecks(): void {
  const scheduleNext = () => {
    const now = Math.floor(Date.now() / 1000);
    const nextRun = now + HEALTH_CHECK_INTERVAL;

    exchangeRateHealthQueue.push({
      job: {
        type: 'health_check',
        timestamp: Date.now(),
      },
      runAt: nextRun,
      persist: true,
    });

    logger.info('Scheduled next exchange rate health check', {
      nextRunAt: new Date(nextRun * 1000).toISOString(),
      intervalHours: HEALTH_CHECK_INTERVAL / 3600,
    });
  };

  // Schedule initial check
  scheduleNext();

  // Listen for completed checks and schedule next one
  exchangeRateHealthQueue.on('finished', () => {
    scheduleNext();
  });

  logger.info('Exchange rate health check scheduling initialized', {
    intervalHours: HEALTH_CHECK_INTERVAL / 3600,
  });
}

// Export for explicit initialization in index.ts
export function startExchangeRateHealthQueue(): void {
  scheduleHealthChecks();
}
