/**
 * DrizzleInitiativeRepository Tests
 *
 * Tests for the Initiative repository adapter.
 */

import { describe, it, expect, beforeEach } from 'vitest';
import { DrizzleInitiativeRepository } from './initiative.js';
import { createTestDatabase } from './test-helpers.js';
import type { DrizzleDatabase } from '../../index.js';

describe('DrizzleInitiativeRepository', () => {
  let db: DrizzleDatabase;
  let repo: DrizzleInitiativeRepository;

  beforeEach(() => {
    db = createTestDatabase();
    repo = new DrizzleInitiativeRepository(db);
  });

  describe('create', () => {
    it('should create an initiative with generated id and timestamps', async () => {
      const initiative = await repo.create({
        name: 'Test Initiative',
      });

      expect(initiative.id).toBeDefined();
      expect(initiative.id.length).toBeGreaterThan(0);
      expect(initiative.name).toBe('Test Initiative');
      expect(initiative.status).toBe('active');
      expect(initiative.createdAt).toBeInstanceOf(Date);
      expect(initiative.updatedAt).toBeInstanceOf(Date);
    });

    it('should use provided status', async () => {
      const initiative = await repo.create({
        name: 'Completed Initiative',
        status: 'completed',
      });

      expect(initiative.status).toBe('completed');
    });
  });

  describe('findById', () => {
    it('should return null for non-existent initiative', async () => {
      const result = await repo.findById('non-existent-id');
      expect(result).toBeNull();
    });

    it('should find an existing initiative', async () => {
      const created = await repo.create({
        name: 'Find Me',
      });

      const found = await repo.findById(created.id);
      expect(found).not.toBeNull();
      expect(found!.id).toBe(created.id);
      expect(found!.name).toBe('Find Me');
    });
  });

  describe('findAll', () => {
    it('should return empty array initially', async () => {
      const all = await repo.findAll();
      expect(all).toEqual([]);
    });

    it('should return all initiatives', async () => {
      await repo.create({ name: 'Initiative 1' });
      await repo.create({ name: 'Initiative 2' });
      await repo.create({ name: 'Initiative 3' });

      const all = await repo.findAll();
      expect(all.length).toBe(3);
    });
  });

  describe('update', () => {
    it('should update fields and updatedAt', async () => {
      const created = await repo.create({
        name: 'Original Name',
        status: 'active',
      });

      // Small delay to ensure updatedAt differs
      await new Promise((resolve) => setTimeout(resolve, 10));

      const updated = await repo.update(created.id, {
        name: 'Updated Name',
        status: 'completed',
      });

      expect(updated.name).toBe('Updated Name');
      expect(updated.status).toBe('completed');
      expect(updated.updatedAt.getTime()).toBeGreaterThanOrEqual(created.updatedAt.getTime());
    });

    it('should throw for non-existent initiative', async () => {
      await expect(
        repo.update('non-existent-id', { name: 'New Name' })
      ).rejects.toThrow('Initiative not found');
    });
  });

  describe('delete', () => {
    it('should delete an existing initiative', async () => {
      const created = await repo.create({ name: 'To Delete' });

      await repo.delete(created.id);

      const found = await repo.findById(created.id);
      expect(found).toBeNull();
    });

    it('should throw for non-existent initiative', async () => {
      await expect(repo.delete('non-existent-id')).rejects.toThrow(
        'Initiative not found'
      );
    });
  });

  describe('findByStatus', () => {
    it('should return empty array for no matches', async () => {
      await repo.create({ name: 'Active 1', status: 'active' });

      const completed = await repo.findByStatus('completed');
      expect(completed).toEqual([]);
    });

    it('should filter by status', async () => {
      await repo.create({ name: 'Active 1', status: 'active' });
      await repo.create({ name: 'Active 2', status: 'active' });
      await repo.create({ name: 'Completed', status: 'completed' });
      await repo.create({ name: 'Archived', status: 'archived' });

      const active = await repo.findByStatus('active');
      expect(active).toHaveLength(2);
      expect(active.every((i) => i.status === 'active')).toBe(true);

      const completed = await repo.findByStatus('completed');
      expect(completed).toHaveLength(1);
      expect(completed[0].name).toBe('Completed');

      const archived = await repo.findByStatus('archived');
      expect(archived).toHaveLength(1);
      expect(archived[0].name).toBe('Archived');
    });
  });
});
