/**
 * Tests for the operation management system
 */

import { describe, it, expect, beforeEach } from 'bun:test';
import type { Logger, MongoDBClient } from '@stock-bot/types';
import { 
  BaseOperationProvider, 
  OperationRegistry, 
  OperationTracker,
  type OperationConfig,
  type ProviderConfig
} from '../../../src/shared/operation-manager';

// Mock implementations
class MockLogger implements Partial<Logger> {
  info = () => {};
  debug = () => {};
  warn = () => {};
  error = () => {};
}

class MockMongoDB implements Partial<MongoDBClient> {
  collection = () => ({
    createIndex: async () => ({}),
    countDocuments: async () => 100,
    bulkWrite: async () => ({ modifiedCount: 1 }),
    aggregate: () => ({
      toArray: async () => []
    })
  } as any);
  
  updateOne = async () => ({});
  find = async () => [];
  findOne = async () => null;
}

// Test provider implementation
class TestProvider extends BaseOperationProvider {
  getProviderConfig(): ProviderConfig {
    return {
      name: 'test',
      collectionName: 'testSymbols',
      symbolField: 'symbol'
    };
  }

  getOperations(): OperationConfig[] {
    return [
      {
        name: 'test_operation',
        type: 'standard',
        defaultStaleHours: 24
      },
      {
        name: 'test_crawl',
        type: 'crawl',
        defaultStaleHours: 48,
        requiresFinishedFlag: true
      },
      {
        name: 'test_intraday_crawl',
        type: 'intraday_crawl',
        defaultStaleHours: 1,
        requiresFinishedFlag: true
      }
    ];
  }
}

describe('Operation Management System', () => {
  let logger: Logger;
  let mongodb: MongoDBClient;
  let registry: OperationRegistry;
  let provider: TestProvider;

  beforeEach(() => {
    logger = new MockLogger() as Logger;
    mongodb = new MockMongoDB() as MongoDBClient;
    registry = new OperationRegistry({ mongodb, logger });
    provider = new TestProvider({ mongodb, logger });
  });

  describe('BaseOperationProvider', () => {
    it('should get provider config', () => {
      const config = provider.getProviderConfig();
      expect(config.name).toBe('test');
      expect(config.collectionName).toBe('testSymbols');
      expect(config.symbolField).toBe('symbol');
    });

    it('should get operations', () => {
      const operations = provider.getOperations();
      expect(operations).toHaveLength(3);
      expect(operations[0].name).toBe('test_operation');
      expect(operations[1].type).toBe('crawl');
      expect(operations[2].type).toBe('intraday_crawl');
    });

    it('should get operation by name', () => {
      const op = provider.getOperation('test_operation');
      expect(op).toBeDefined();
      expect(op?.name).toBe('test_operation');
    });

    it('should validate operation exists', () => {
      expect(() => provider.validateOperation('test_operation')).not.toThrow();
      expect(() => provider.validateOperation('invalid')).toThrow();
    });

    it('should get default stale hours', () => {
      expect(provider.getDefaultStaleHours('test_operation')).toBe(24);
      expect(provider.getDefaultStaleHours('test_crawl')).toBe(48);
      expect(provider.getDefaultStaleHours('unknown')).toBe(24); // default
    });
  });

  describe('OperationTracker', () => {
    it('should initialize tracker', async () => {
      const tracker = new OperationTracker({ mongodb, logger }, provider);
      await tracker.initialize();
      // Should create indexes without error
    });

    it('should update symbol operation', async () => {
      const tracker = new OperationTracker({ mongodb, logger }, provider);
      await tracker.initialize();
      
      await tracker.updateSymbolOperation('TEST', 'test_operation', {
        status: 'success',
        recordCount: 100,
        lastRecordDate: new Date()
      });
      // Should update without error
    });
  });

  describe('OperationRegistry', () => {
    it('should register provider', async () => {
      await registry.registerProvider(provider);
      expect(registry.hasProvider('test')).toBe(true);
    });

    it('should get provider', async () => {
      await registry.registerProvider(provider);
      const retrieved = registry.getProvider('test');
      expect(retrieved).toBe(provider);
    });

    it('should throw for unknown provider', () => {
      expect(() => registry.getProvider('unknown')).toThrow();
    });

    it('should get provider operations', async () => {
      await registry.registerProvider(provider);
      const operations = registry.getProviderOperations('test');
      expect(operations).toHaveLength(3);
    });

    it('should update operation through registry', async () => {
      await registry.registerProvider(provider);
      
      await registry.updateOperation('test', 'TEST', 'test_operation', {
        status: 'success',
        recordCount: 50
      });
      // Should update without error
    });

    it('should find operations by type', async () => {
      await registry.registerProvider(provider);
      
      const crawlOps = registry.findOperationsByType('crawl');
      expect(crawlOps).toHaveLength(1);
      expect(crawlOps[0].provider).toBe('test');
      expect(crawlOps[0].operation.name).toBe('test_crawl');
    });
  });

  describe('Integration', () => {
    it('should work end-to-end', async () => {
      // Register provider
      await registry.registerProvider(provider);
      
      // Update operation
      await registry.updateOperation('test', 'SYMBOL1', 'test_operation', {
        status: 'success',
        recordCount: 100
      });
      
      // Get stats (with mocked data)
      const stats = await registry.getOperationStats('test', 'test_operation');
      expect(stats.totalSymbols).toBe(100);
    });
  });
});