/**
 * Repository tests for tokens module — in-memory datastore.
 */

import { describe, it, expect, beforeEach } from 'vitest';
import { MemoryDatastoreProvider } from '@bytelyst/datastore';
import { setProvider } from '../../lib/datastore.js';
import { list, listByUser, getById, create, revoke, remove, countActive } from './repository.js';
import type { ApiTokenDoc } from './types.js';

const baseToken: ApiTokenDoc = {
  id: 'tok_1',
  productId: 'lysnrai',
  userId: 'user_1',
  userName: 'Test User',
  name: 'Test Token',
  tokenType: 'user_api',
  environment: 'prod',
  tokenHash: 'hash_abc',
  prefix: 'lysnr_',
  status: 'active',
  scopes: ['read'],
  createdAt: '2026-02-16T00:00:00Z',
  expiresAt: '2027-02-16T00:00:00Z',
  lastUsed: '2026-02-16T00:00:00Z',
};

describe('tokens repository', () => {
  beforeEach(() => {
    setProvider(new MemoryDatastoreProvider());
  });

  describe('list', () => {
    it('returns tokens without hashes', async () => {
      await create(baseToken);
      const result = await list('lysnrai');
      expect(result).toHaveLength(1);
      expect(result[0]).not.toHaveProperty('tokenHash');
    });

    it('returns empty array when no tokens', async () => {
      const result = await list('lysnrai');
      expect(result).toEqual([]);
    });
  });

  describe('listByUser', () => {
    it('returns tokens for user without hashes', async () => {
      await create(baseToken);
      const result = await listByUser('user_1', 'lysnrai');
      expect(result).toHaveLength(1);
      expect(result[0]).not.toHaveProperty('tokenHash');
    });
  });

  describe('getById', () => {
    it('returns token when found', async () => {
      await create(baseToken);
      const result = await getById('tok_1');
      expect(result).not.toBeNull();
      expect(result!.id).toBe('tok_1');
    });

    it('returns null when not found', async () => {
      const result = await getById('tok_1');
      expect(result).toBeNull();
    });
  });

  describe('create', () => {
    it('creates and returns token without hash', async () => {
      const result = await create(baseToken);
      expect(result).not.toHaveProperty('tokenHash');
      expect(result.id).toBe('tok_1');
    });
  });

  describe('revoke', () => {
    it('revokes an existing token', async () => {
      await create(baseToken);
      const result = await revoke('tok_1', 'lysnrai');
      expect(result).toBe(true);
      const updated = await getById('tok_1');
      expect(updated!.status).toBe('revoked');
    });

    it('returns false when token not found', async () => {
      const result = await revoke('tok_1', 'lysnrai');
      expect(result).toBe(false);
    });

    it('returns false when token belongs to another product', async () => {
      await create(baseToken);
      const result = await revoke('tok_1', 'chronomind');
      expect(result).toBe(false);
    });
  });

  describe('remove', () => {
    it('deletes and returns true', async () => {
      await create(baseToken);
      const result = await remove('tok_1', 'lysnrai');
      expect(result).toBe(true);
    });

    it('returns false when token belongs to another product', async () => {
      await create(baseToken);
      const result = await remove('tok_1', 'chronomind');
      expect(result).toBe(false);
    });
  });

  describe('countActive', () => {
    it('returns count', async () => {
      await create(baseToken);
      const result = await countActive('lysnrai');
      expect(result).toBe(1);
    });

    it('returns 0 when no results', async () => {
      const result = await countActive('lysnrai');
      expect(result).toBe(0);
    });
  });
});
