import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';

describe('seedAdmin', () => {
  let seedAdmin, originalPassword;

  beforeEach(async () => {
    vi.resetModules();
    originalPassword = process.env.INITIAL_PASSWORD;
    delete process.env.INITIAL_PASSWORD;
  });

  afterEach(() => {
    if (originalPassword !== undefined) {
      process.env.INITIAL_PASSWORD = originalPassword;
    } else {
      delete process.env.INITIAL_PASSWORD;
    }
    vi.restoreAllMocks();
  });

  describe('when INITIAL_PASSWORD is not set', () => {
    it('does not create admin and returns early', async () => {
      const createUserSpy = vi.fn();
      const seed = await freshSeedModule({
        findByUsername: vi.fn().mockResolvedValue(null),
        createUser: createUserSpy,
      });
      await seed();
      expect(createUserSpy).not.toHaveBeenCalled();
    });
  });

  describe('when admin already exists', () => {
    it('skips creation', async () => {
      process.env.INITIAL_PASSWORD = 'admin123';
      const createUserSpy = vi.fn();
      const seed = await freshSeedModule({
        findByUsername: vi.fn().mockResolvedValue({ id: 'existing-admin', username: 'admin' }),
        createUser: createUserSpy,
      });
      await seed();
      expect(createUserSpy).not.toHaveBeenCalled();
    });
  });

  describe('when admin does not exist and INITIAL_PASSWORD is set', () => {
    it('creates admin user with correct role and approval', async () => {
      process.env.INITIAL_PASSWORD = 'admin123';
      const adminId = 'admin-uuid-123';
      const updateRoleSpy = vi.fn();
      const updateApprovalSpy = vi.fn();
      const createDefaultsSpy = vi.fn();

      const seed = await freshSeedModule({
        findByUsername: vi.fn().mockResolvedValue(null),
        createUser: vi.fn().mockResolvedValue({ id: adminId, username: 'admin', role: 'user', is_active: true, is_approved: false }),
        updateRole: updateRoleSpy,
        updateApproval: updateApprovalSpy,
        createDefaults: createDefaultsSpy,
      });

      await seed();

      expect(updateRoleSpy).toHaveBeenCalledWith(adminId, 'admin');
      expect(updateApprovalSpy).toHaveBeenCalledWith(adminId, true);
      expect(createDefaultsSpy).toHaveBeenCalledWith(adminId);
    });
  });

  describe('idempotency', () => {
    it('only runs seeding logic once per process', async () => {
      process.env.INITIAL_PASSWORD = 'admin123';
      const createUserSpy = vi.fn().mockResolvedValue({ id: 'admin-1', username: 'admin' });
      const seed = await freshSeedModule({
        findByUsername: vi.fn().mockResolvedValue(null),
        createUser: createUserSpy,
        updateRole: vi.fn(),
        updateApproval: vi.fn(),
        createDefaults: vi.fn(),
      });

      // First call
      await seed();
      // Second call — internal `seeded` flag should prevent re-execution
      await seed();

      expect(createUserSpy).toHaveBeenCalledTimes(1);
    });
  });

  describe('error handling', () => {
    it('catches findByUsername errors (table not ready)', async () => {
      const seed = await freshSeedModule({
        findByUsername: vi.fn().mockRejectedValue(new Error('no such table: users')),
      });
      // Should not throw
      await expect(seed()).resolves.not.toThrow();
    });

    it('catches createUser errors gracefully', async () => {
      process.env.INITIAL_PASSWORD = 'admin123';
      const seed = await freshSeedModule({
        findByUsername: vi.fn().mockResolvedValue(null),
        createUser: vi.fn().mockRejectedValue(new Error('duplicate key')),
        updateRole: vi.fn(),
        updateApproval: vi.fn(),
        createDefaults: vi.fn(),
      });
      // Should not throw
      await expect(seed()).resolves.not.toThrow();
    });
  });
});

// Helper: create a fresh seedAdmin module with mocked repo imports
async function freshSeedModule(mocks = {}) {
  const {
    findByUsername = vi.fn(),
    createUser = vi.fn(),
    updateRole = vi.fn(),
    updateApproval = vi.fn(),
    createDefaults = vi.fn(),
  } = mocks;

  // Mock the repo modules
  vi.doMock('@/lib/db/repos/usersRepo', () => ({
    findByUsername,
    createUser,
    findById: vi.fn(),
    updateRole,
    updateApproval,
  }));

  vi.doMock('@/lib/db/repos/rateLimitRepo', () => ({
    createDefaults,
  }));

  // Mock password hashing
  vi.doMock('@/lib/auth/password', () => ({
    hashPassword: vi.fn((pw) => `hashed:${pw}`),
  }));

  const module = await import('./seedAdmin.js');
  return module.seedAdmin;
}
