import { beforeEach, describe, expect, it } from 'bun:test';
import { SimpleMongoDBClient } from '../src/simple-mongodb';

describe('MongoDBClient', () => {
  let client: SimpleMongoDBClient;
  const config = {
    uri: 'mongodb://localhost:27017',
    database: 'test-db',
  };

  beforeEach(() => {
    client = new SimpleMongoDBClient(config);
  });

  describe('connection', () => {
    it('should connect on first operation', async () => {
      const results = await client.find('test-collection', {});

      expect(results).toBeDefined();
      expect(results).toEqual([]);
    });

    it('should handle health check', async () => {
      // Connect first by doing an operation
      await client.find('test', {});

      const health = await client.healthCheck();

      expect(health.status).toBe('healthy');
      expect(health.isConnected).toBe(true);
    });

    it('should disconnect properly', async () => {
      await client.find('test', {});
      await client.disconnect();

      const health = await client.healthCheck();
      expect(health.isConnected).toBe(false);
    });
  });

  describe('CRUD operations', () => {
    it('should find documents', async () => {
      await client.insert('users', { id: 1, active: true });
      await client.insert('users', { id: 2, active: true });
      await client.insert('users', { id: 3, active: false });

      const results = await client.find('users', { active: true });

      expect(results).toHaveLength(2);
      expect(results[0].active).toBe(true);
      expect(results[1].active).toBe(true);
    });

    it('should find one document', async () => {
      await client.insert('users', { id: 1, name: 'Test' });
      await client.insert('users', { id: 2, name: 'Other' });

      const result = await client.findOne('users', { id: 1 });

      expect(result).toBeDefined();
      expect(result.id).toBe(1);
      expect(result.name).toBe('Test');
    });

    it('should insert documents', async () => {
      const doc = { name: 'Test User', email: 'test@example.com' };

      await client.insert('users', doc);

      const result = await client.findOne('users', { email: 'test@example.com' });
      expect(result).toBeDefined();
      expect(result.name).toBe('Test User');
    });

    it('should insert many documents', async () => {
      const docs = [{ name: 'User 1' }, { name: 'User 2' }];

      await client.insertMany('users', docs);

      const all = await client.find('users', {});
      expect(all).toHaveLength(2);
    });

    it('should update documents', async () => {
      await client.insert('users', { id: 1, active: true });

      const updated = await client.update('users', { id: 1 }, { $set: { active: false } });

      expect(updated).toBe(1);
      const result = await client.findOne('users', { id: 1 });
      expect(result.active).toBe(false);
    });

    it('should update many documents', async () => {
      await client.insert('users', { id: 1, active: true });
      await client.insert('users', { id: 2, active: true });
      await client.insert('users', { id: 3, active: false });

      const updated = await client.updateMany(
        'users',
        { active: true },
        { $set: { status: 'active' } }
      );

      expect(updated).toBe(2);
      const activeUsers = await client.find('users', { status: 'active' });
      expect(activeUsers).toHaveLength(2);
    });

    it('should delete documents', async () => {
      await client.insert('users', { id: 1 });
      await client.insert('users', { id: 2 });

      const deleted = await client.delete('users', { id: 1 });

      expect(deleted).toBe(1);
      const remaining = await client.find('users', {});
      expect(remaining).toHaveLength(1);
      expect(remaining[0].id).toBe(2);
    });

    it('should delete many documents', async () => {
      await client.insert('users', { id: 1, active: true });
      await client.insert('users', { id: 2, active: false });
      await client.insert('users', { id: 3, active: false });

      const deleted = await client.deleteMany('users', { active: false });

      expect(deleted).toBe(2);
      const remaining = await client.find('users', {});
      expect(remaining).toHaveLength(1);
      expect(remaining[0].active).toBe(true);
    });
  });

  describe('batch operations', () => {
    it('should perform batch upsert', async () => {
      const docs = [
        { id: 1, name: 'User 1' },
        { id: 2, name: 'User 2' },
      ];

      await client.batchUpsert('users', docs, ['id']);

      const all = await client.find('users', {});
      expect(all).toHaveLength(2);

      // Update existing
      await client.batchUpsert('users', [{ id: 1, name: 'Updated User 1' }], ['id']);

      const updated = await client.findOne('users', { id: 1 });
      expect(updated.name).toBe('Updated User 1');
    });

    it('should handle empty batch', async () => {
      await client.batchUpsert('users', [], ['id']);

      const all = await client.find('users', {});
      expect(all).toHaveLength(0);
    });
  });

  describe('utility methods', () => {
    it('should count documents', async () => {
      await client.insert('users', { active: true });
      await client.insert('users', { active: true });
      await client.insert('users', { active: false });

      const count = await client.count('users', { active: true });

      expect(count).toBe(2);
    });

    it('should create indexes', async () => {
      await client.createIndex('users', { email: 1 }, { unique: true });

      // Simple implementation doesn't throw, just no-op
      expect(true).toBe(true);
    });
  });

  describe('error handling', () => {
    it('should handle disconnected state', async () => {
      await client.disconnect();

      // Simple implementation auto-reconnects
      const results = await client.find('users', {});
      expect(results).toBeDefined();
    });

    it('should return empty array for non-existent collection', async () => {
      const results = await client.find('non-existent', {});
      expect(results).toEqual([]);
    });
  });
});
