import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { PgliteAdapter } from './pglite.js';

describe('PgliteAdapter', () => {
  let adapter: PgliteAdapter;

  beforeEach(async () => {
    // In-memory PGlite instance — no dataDir = memory mode
    adapter = new PgliteAdapter({ type: 'pglite' });
    await adapter.migrate();
  });

  afterEach(async () => {
    await adapter.close();
  });

  describe('CRUD', () => {
    it('creates and reads a record', async () => {
      const created = await adapter.create('users', { name: 'Alice', email: 'alice@test.com' });
      expect(created.id).toBeDefined();
      expect(created.name).toBe('Alice');

      const read = await adapter.read('users', created.id);
      expect(read).not.toBeNull();
      expect(read!.name).toBe('Alice');
      expect(read!.email).toBe('alice@test.com');
    });

    it('returns null for non-existent record', async () => {
      const result = await adapter.read('users', 'does-not-exist');
      expect(result).toBeNull();
    });

    it('updates a record', async () => {
      const created = await adapter.create('users', { name: 'Alice' });
      const updated = await adapter.update('users', created.id, { name: 'Bob' });
      expect(updated).toBe(true);

      const read = await adapter.read('users', created.id);
      expect(read!.name).toBe('Bob');
    });

    it('update returns false for non-existent record', async () => {
      const result = await adapter.update('users', 'does-not-exist', { name: 'X' });
      expect(result).toBe(false);
    });

    it('deletes a record', async () => {
      const created = await adapter.create('users', { name: 'Alice' });
      const deleted = await adapter.delete('users', created.id);
      expect(deleted).toBe(true);

      const read = await adapter.read('users', created.id);
      expect(read).toBeNull();
    });

    it('delete returns false for non-existent record', async () => {
      const result = await adapter.delete('users', 'does-not-exist');
      expect(result).toBe(false);
    });
  });

  describe('find', () => {
    it('finds records with filter', async () => {
      await adapter.create('users', { name: 'Alice', role: 'admin' });
      await adapter.create('users', { name: 'Bob', role: 'user' });
      await adapter.create('users', { name: 'Charlie', role: 'admin' });

      const admins = await adapter.find('users', { role: 'admin' });
      expect(admins).toHaveLength(2);
      expect(admins.map((u) => u.name).sort()).toEqual(['Alice', 'Charlie']);
    });

    it('finds all records without filter', async () => {
      await adapter.create('users', { name: 'Alice' });
      await adapter.create('users', { name: 'Bob' });

      const all = await adapter.find('users');
      expect(all).toHaveLength(2);
    });

    it('supports limit and offset', async () => {
      for (let i = 0; i < 5; i++) {
        await adapter.create('users', { name: `User${i.toString()}`, idx: i });
      }

      const page = await adapter.find('users', undefined, {
        limit: 2,
        offset: 1,
        orderBy: 'created_at',
      });
      expect(page).toHaveLength(2);
    });

    it('findOne returns first match', async () => {
      await adapter.create('users', { name: 'Alice', role: 'admin' });
      await adapter.create('users', { name: 'Bob', role: 'user' });

      const found = await adapter.findOne('users', { role: 'user' });
      expect(found).not.toBeNull();
      expect(found!.name).toBe('Bob');
    });

    it('findOne returns null when no match', async () => {
      const result = await adapter.findOne('users', { role: 'nonexistent' });
      expect(result).toBeNull();
    });
  });

  describe('count', () => {
    it('counts all records', async () => {
      await adapter.create('users', { name: 'Alice' });
      await adapter.create('users', { name: 'Bob' });

      const total = await adapter.count('users');
      expect(total).toBe(2);
    });

    it('counts with filter', async () => {
      await adapter.create('users', { name: 'Alice', role: 'admin' });
      await adapter.create('users', { name: 'Bob', role: 'user' });
      await adapter.create('users', { name: 'Charlie', role: 'admin' });

      const adminCount = await adapter.count('users', { role: 'admin' });
      expect(adminCount).toBe(2);
    });

    it('returns 0 for empty collection', async () => {
      const count = await adapter.count('users');
      expect(count).toBe(0);
    });
  });

  describe('transaction', () => {
    it('commits on success', async () => {
      await adapter.transaction(async (tx) => {
        await tx.create('users', { name: 'Alice' });
        await tx.create('users', { name: 'Bob' });
      });

      const count = await adapter.count('users');
      expect(count).toBe(2);
    });

    it('rolls back on error', async () => {
      await expect(
        adapter.transaction(async (tx) => {
          await tx.create('users', { name: 'Alice' });
          throw new Error('rollback test');
        }),
      ).rejects.toThrow('rollback test');

      const count = await adapter.count('users');
      expect(count).toBe(0);
    });
  });

  describe('migrate', () => {
    it('creates all tables', async () => {
      // migrate() was already called in beforeEach — verify tables exist
      const collections = [
        'users',
        'sessions',
        'accounts',
        'projects',
        'missions',
        'tasks',
        'agents',
        'conversations',
        'messages',
        'preferences',
        'insights',
        'skills',
        'events',
        'routing_rules',
        'provider_credentials',
        'agent_logs',
        'teams',
        'team_members',
        'mission_tasks',
        'tickets',
        'summarization_jobs',
        'appreciations',
        'verifications',
      ];

      for (const collection of collections) {
        // Should not throw
        const count = await adapter.count(collection);
        expect(count).toBe(0);
      }
    });

    it('is idempotent', async () => {
      await adapter.migrate();
      await adapter.migrate();
      // Should not throw
      const count = await adapter.count('users');
      expect(count).toBe(0);
    });
  });
});
