import { Router } from 'express';
import pick from 'lodash/pick';
import { Op } from 'sequelize';

import { authenticate } from '../libs/security';
import { ExchangeRateProvider } from '../store/models/exchange-rate-provider';
import { TokenDataProvider } from '../libs/exchange-rate/token-data-provider';
import { CoinGeckoProvider } from '../libs/exchange-rate/coingecko-provider';
import { CoinMarketCapProvider } from '../libs/exchange-rate/coinmarketcap-provider';

const router = Router();
const auth = authenticate({ component: true, roles: ['owner', 'admin'], ensureLogin: true });
const allowedStatuses = ['active', 'degraded', 'paused', 'inactive'];

router.get('/', auth, async (_req, res) => {
  const providers = await ExchangeRateProvider.findAll({
    order: [
      ['priority', 'ASC'],
      ['created_at', 'ASC'],
    ],
  });

  // Mask sensitive config data (API keys) before sending to client
  const maskedProviders = providers.map((p) => ({
    ...p.toJSON(),
    config: ExchangeRateProvider.maskConfig(p.config),
  }));

  res.json({ data: maskedProviders });
});

router.post('/', auth, async (req, res) => {
  const {
    name,
    type = 'token-data',
    enabled = true,
    priority = 1,
    status = 'active',
    config = {},
    // eslint-disable-next-line @typescript-eslint/naming-convention
    paused_reason = null,
  } = req.body;
  if (!name) {
    res.status(400).json({ error: 'name is required' });
    return;
  }
  if (Number.isNaN(Number(priority))) {
    res.status(400).json({ error: 'priority must be a number' });
    return;
  }
  if (!allowedStatuses.includes(status)) {
    res.status(400).json({ error: 'invalid status' });
    return;
  }

  const provider = await ExchangeRateProvider.create({
    name,
    type,
    enabled: !!enabled,
    priority: Number(priority),
    status,
    paused_reason: status === 'paused' ? paused_reason : null,
    config: ExchangeRateProvider.encryptConfig(config),
  });

  res.json({ data: provider });
});

router.put('/:id', auth, async (req, res) => {
  const provider = await ExchangeRateProvider.findByPk(req.params.id);
  if (!provider) {
    res.status(404).json({ error: 'Provider not found' });
    return;
  }

  // Note: 'type' is intentionally excluded - cannot be changed after creation
  const updates = pick(req.body, ['name', 'enabled', 'priority', 'status', 'paused_reason', 'config']);
  if (updates.priority !== undefined && Number.isNaN(Number(updates.priority))) {
    res.status(400).json({ error: 'priority must be a number' });
    return;
  }
  if (updates.status && !allowedStatuses.includes(updates.status)) {
    res.status(400).json({ error: 'invalid status' });
    return;
  }

  // Encrypt config if it's being updated
  if (updates.config) {
    const existingConfig = ExchangeRateProvider.decryptConfig(provider.config) || {};
    const mergedConfig = { ...existingConfig, ...updates.config };
    if (updates.config.api_key === '') {
      delete mergedConfig.api_key;
    }
    updates.config = ExchangeRateProvider.encryptConfig(mergedConfig);
  }

  await provider.update(updates);
  res.json({ data: provider });
});

// TECHNICAL DEBT: Exchange rate provider management bypasses AFS layer
// TODO: Migrate to AFS-based provider management when AFS layer is available
// Context: This CRUD API was built before AFS implementation
// Impact: UI is tightly coupled to backend schema, not replayable via AFS paths
// Migration Path: Implement $afs:/admin/providers/*.json views
// Decision: Accepted as pragmatic completion of existing system (2026-01-12)
// Reference: ai/intent/20260112-token-data-provider-management-alignment.md

/**
 * DELETE endpoint - Remove an exchange rate provider
 * Safety constraints:
 * 1. Cannot delete provider that is currently in use
 * 2. Cannot delete the last enabled provider
 */
router.delete('/:id', auth, async (req, res) => {
  const provider = await ExchangeRateProvider.findByPk(req.params.id);
  if (!provider) {
    res.status(404).json({ error: 'Provider not found' });
    return;
  }

  // Safety Check 1: Get active provider (same logic as frontend)
  const allProviders = await ExchangeRateProvider.findAll({
    order: [
      ['priority', 'ASC'],
      ['created_at', 'ASC'],
    ],
  });

  const activeProviders = allProviders
    .filter((p) => p.enabled && p.status !== 'paused')
    .sort((a, b) => a.priority - b.priority);

  const activeProviderId = activeProviders[0]?.id || null;

  if (provider.id === activeProviderId) {
    res.status(400).json({
      error: 'Cannot delete provider that is currently in use',
    });
    return;
  }

  // Safety Check 2: Ensure we're not deleting the last enabled provider
  const otherEnabledCount = await ExchangeRateProvider.count({
    where: {
      id: { [Op.ne]: req.params.id },
      enabled: true,
      status: { [Op.ne]: 'paused' },
    },
  });

  if (otherEnabledCount === 0 && provider.enabled && provider.status !== 'paused') {
    res.status(400).json({
      error:
        'Cannot delete the last enabled provider. Please ensure at least one other provider is enabled before deleting this one.',
    });
    return;
  }

  // All checks passed, safe to delete
  await provider.destroy();

  res.json({ success: true });
});

/**
 * POST /test-connection - Test provider connection without saving
 * Validates provider configuration by attempting to fetch a test rate
 */
router.post('/test-connection', auth, async (req, res) => {
  const { type, config = {}, provider_id: providerId } = req.body;

  // Validate provider type
  const allowedTypes = ['token-data', 'coingecko', 'coinmarketcap'];
  if (!type || !allowedTypes.includes(type)) {
    res.status(400).json({ error: 'Invalid provider type' });
    return;
  }

  // Decrypt config if it contains encrypted api_key
  const decryptedConfig = ExchangeRateProvider.decryptConfig(config) || {};
  let effectiveConfig = decryptedConfig;
  if (providerId) {
    const provider = await ExchangeRateProvider.findByPk(providerId);
    if (provider) {
      const existingConfig = ExchangeRateProvider.decryptConfig(provider.config) || {};
      if (decryptedConfig.api_key === '') {
        delete existingConfig.api_key;
      }
      const mergedConfig = {
        ...existingConfig,
        ...decryptedConfig,
      };
      effectiveConfig = mergedConfig;
    }
  }

  // Create provider instance based on type
  let provider;
  try {
    switch (type) {
      case 'token-data':
        provider = new TokenDataProvider(effectiveConfig || undefined);
        break;
      case 'coingecko':
        provider = new CoinGeckoProvider(effectiveConfig || undefined);
        break;
      case 'coinmarketcap':
        provider = new CoinMarketCapProvider(effectiveConfig || undefined);
        break;
      default:
        res.status(400).json({ error: 'Invalid provider type' });
        return;
    }
  } catch (error: any) {
    res.status(200).json({
      success: false,
      error: error.message || 'Failed to initialize provider',
    });
    return;
  }

  const testSymbol = type === 'token-data' ? 'ABT' : 'ETH';
  const startTime = Date.now();

  try {
    const result = await provider.fetch(testSymbol);
    const responseTime = Date.now() - startTime;

    res.json({
      success: true,
      responseTime,
      rate: result.rate,
      timestamp: result.timestamp_ms,
      symbol: testSymbol,
    });
  } catch (error: any) {
    const responseTime = Date.now() - startTime;
    res.status(200).json({
      success: false,
      responseTime,
      error: error.message || 'Connection test failed',
      symbol: testSymbol,
    });
  }
});

export default router;
