import type { IServiceContainer } from '@stock-bot/handlers';
import { getLogger } from '@stock-bot/logger';
import type { JobPayload, SyncResult } from '../../../types/job-payloads';

const logger = getLogger('enhanced-sync-qm-provider-mappings');

export async function syncQMProviderMappings(
  payload: JobPayload,
  container: IServiceContainer
): Promise<SyncResult> {
  logger.info('Starting QM provider exchange mappings sync...');

  const result: SyncResult = {
    processed: 0,
    created: 0,
    updated: 0,
    skipped: 0,
    errors: 0,
  };

  try {
    const mongoClient = container.mongodb;
    const postgresClient = container.postgres;

    // Start transaction
    await postgresClient.query('BEGIN');

    // Get unique exchange combinations from QM symbols
    const db = mongoClient.getDatabase();
    const pipeline = [
      {
        $group: {
          _id: {
            exchangeCode: '$exchangeCode',
            exchange: '$exchange',
            countryCode: '$countryCode',
          },
          count: { $sum: 1 },
          sampleExchange: { $first: '$exchange' },
        },
      },
      {
        $project: {
          exchangeCode: '$_id.exchangeCode',
          exchange: '$_id.exchange',
          countryCode: '$_id.countryCode',
          count: 1,
          sampleExchange: 1,
        },
      },
    ];

    const qmExchanges = await db.collection('qmSymbols').aggregate(pipeline).toArray();
    logger.info(`Found ${qmExchanges.length} unique QM exchange combinations`);

    for (const exchange of qmExchanges) {
      try {
        // Create provider exchange mapping for QM
        await createProviderExchangeMapping(
          'qm', // provider
          exchange.exchangeCode,
          exchange.sampleExchange || exchange.exchangeCode,
          exchange.countryCode,
          exchange.countryCode === 'CA' ? 'CAD' : 'USD', // Simple currency mapping
          0.8, // good confidence for QM data
          container
        );

        result.processed++;
        result.created++;
      } catch (error) {
        logger.error('Failed to process QM exchange mapping', { error, exchange });
        result.errors++;
      }
    }

    await postgresClient.query('COMMIT');

    logger.info('QM provider exchange mappings sync completed', result);
    return result;
  } catch (error) {
    const postgresClient = container.postgres;
    await postgresClient.query('ROLLBACK');
    logger.error('QM provider exchange mappings sync failed', { error });
    throw error;
  }
}

async function createProviderExchangeMapping(
  provider: string,
  providerExchangeCode: string,
  providerExchangeName: string,
  countryCode: string | null,
  currency: string | null,
  confidence: number,
  container: IServiceContainer
): Promise<void> {
  if (!providerExchangeCode) {
    return;
  }

  const postgresClient = container.postgres;

  // Check if mapping already exists
  const existingMapping = await findProviderExchangeMapping(
    provider,
    providerExchangeCode,
    container
  );
  if (existingMapping) {
    // Don't override existing mappings to preserve manual work
    return;
  }

  // Find or create master exchange
  const masterExchange = await findOrCreateMasterExchange(
    providerExchangeCode,
    providerExchangeName,
    countryCode,
    currency,
    container
  );

  // Create the provider exchange mapping
  const query = `
    INSERT INTO provider_exchange_mappings 
    (provider, provider_exchange_code, provider_exchange_name, master_exchange_id, 
     country_code, currency, confidence, active, auto_mapped)
    VALUES ($1, $2, $3, $4, $5, $6, $7, false, true)
    ON CONFLICT (provider, provider_exchange_code) DO NOTHING
  `;

  await postgresClient.query(query, [
    provider,
    providerExchangeCode,
    providerExchangeName,
    masterExchange.id,
    countryCode,
    currency,
    confidence,
  ]);
}

async function findProviderExchangeMapping(
  provider: string,
  providerExchangeCode: string,
  container: IServiceContainer
): Promise<any> {
  const postgresClient = container.postgres;
  const query =
    'SELECT * FROM provider_exchange_mappings WHERE provider = $1 AND provider_exchange_code = $2';
  const result = await postgresClient.query(query, [provider, providerExchangeCode]);
  return result.rows[0] || null;
}

async function findOrCreateMasterExchange(
  providerCode: string,
  providerName: string,
  countryCode: string | null,
  currency: string | null,
  container: IServiceContainer
): Promise<any> {
  const postgresClient = container.postgres;

  // First, try to find exact match
  let masterExchange = await findExchangeByCode(providerCode, container);

  if (masterExchange) {
    return masterExchange;
  }

  // Try to find by similar codes (basic mapping)
  const basicMapping = getBasicExchangeMapping(providerCode);
  if (basicMapping) {
    masterExchange = await findExchangeByCode(basicMapping, container);
    if (masterExchange) {
      return masterExchange;
    }
  }

  // Create new master exchange (inactive by default)
  const query = `
    INSERT INTO exchanges (code, name, country, currency, active)
    VALUES ($1, $2, $3, $4, false)
    ON CONFLICT (code) DO UPDATE SET
      name = COALESCE(EXCLUDED.name, exchanges.name),
      country = COALESCE(EXCLUDED.country, exchanges.country),
      currency = COALESCE(EXCLUDED.currency, exchanges.currency)
    RETURNING id, code, name, country, currency
  `;

  const result = await postgresClient.query(query, [
    providerCode,
    providerName || providerCode,
    countryCode || 'US',
    currency || 'USD',
  ]);

  return result.rows[0];
}

function getBasicExchangeMapping(providerCode: string): string | null {
  const mappings: Record<string, string> = {
    NYE: 'NYSE',
    NAS: 'NASDAQ',
    TO: 'TSX',
    LN: 'LSE',
    LON: 'LSE',
  };

  return mappings[providerCode.toUpperCase()] || null;
}

async function findExchangeByCode(code: string, container: IServiceContainer): Promise<any> {
  const postgresClient = container.postgres;
  const query = 'SELECT * FROM exchanges WHERE code = $1';
  const result = await postgresClient.query(query, [code]);
  return result.rows[0] || null;
}
