/**
 * Unit tests for src/services/MarketplaceService.ts
 */

import { MarketplaceService } from '../../../src/services/MarketplaceService';
import { AgentRepository } from '../../../src/repositories/AgentRepository';
import { AgentNotFoundError } from '../../../src/utils/errors';
import { IAgent, IMarketplaceFilters } from '../../../src/types/index';

jest.mock('../../../src/repositories/AgentRepository');

const MockAgentRepo = AgentRepository as jest.MockedClass<typeof AgentRepository>;

const BASE_FILTERS: IMarketplaceFilters = { page: 1, limit: 10 };

function makeAgent(overrides: Partial<IAgent> = {}): IAgent {
  return {
    agentId: 'agent-001',
    organizationId: 'org-001',
    email: 'agent@example.com',
    agentType: 'screener',
    version: 'v1.0.0',
    capabilities: ['resume:read'],
    owner: 'test-team',
    deploymentEnv: 'production',
    status: 'active',
    isPublic: true,
    createdAt: new Date('2026-01-01'),
    updatedAt: new Date('2026-01-02'),
    did: null,
    didCreatedAt: null,
    ...overrides,
  };
}

describe('MarketplaceService', () => {
  let service: MarketplaceService;
  let agentRepo: jest.Mocked<AgentRepository>;

  beforeEach(() => {
    jest.clearAllMocks();
    agentRepo = new MockAgentRepo({} as never) as jest.Mocked<AgentRepository>;
    service = new MarketplaceService(agentRepo);
  });

  describe('listPublicAgents()', () => {
    it('should return mapped agent cards', async () => {
      const agent = makeAgent();
      agentRepo.findPublicAgents = jest.fn().mockResolvedValue({ agents: [agent], total: 1 });

      const result = await service.listPublicAgents(BASE_FILTERS);

      expect(result.data).toHaveLength(1);
      expect(result.data[0].agentId).toBe('agent-001');
      expect(result.total).toBe(1);
      expect(result.page).toBe(1);
      expect(result.limit).toBe(10);
    });

    it('should strip private fields (email, organizationId) from cards', async () => {
      const agent = makeAgent();
      agentRepo.findPublicAgents = jest.fn().mockResolvedValue({ agents: [agent], total: 1 });

      const result = await service.listPublicAgents(BASE_FILTERS);
      const card = result.data[0] as Record<string, unknown>;

      expect(card['email']).toBeUndefined();
      expect(card['organizationId']).toBeUndefined();
    });

    it('should include a minimal DID document when agent has a DID', async () => {
      const agent = makeAgent({ did: 'did:web:sentryagent.ai:agents:agent-001' });
      agentRepo.findPublicAgents = jest.fn().mockResolvedValue({ agents: [agent], total: 1 });

      const result = await service.listPublicAgents(BASE_FILTERS);

      expect(result.data[0].didDocument).not.toBeNull();
      expect(result.data[0].didDocument?.id).toBe('did:web:sentryagent.ai:agents:agent-001');
    });

    it('should return null DID document when agent has no DID', async () => {
      const agent = makeAgent({ did: null });
      agentRepo.findPublicAgents = jest.fn().mockResolvedValue({ agents: [agent], total: 1 });

      const result = await service.listPublicAgents(BASE_FILTERS);

      expect(result.data[0].didDocument).toBeNull();
    });

    it('should return empty data array when no public agents exist', async () => {
      agentRepo.findPublicAgents = jest.fn().mockResolvedValue({ agents: [], total: 0 });

      const result = await service.listPublicAgents(BASE_FILTERS);

      expect(result.data).toHaveLength(0);
      expect(result.total).toBe(0);
    });
  });

  describe('getPublicAgent()', () => {
    it('should return a card for a public agent', async () => {
      const agent = makeAgent();
      agentRepo.findPublicById = jest.fn().mockResolvedValue(agent);

      const card = await service.getPublicAgent('agent-001');

      expect(card.agentId).toBe('agent-001');
      expect(card.owner).toBe('test-team');
    });

    it('should throw AgentNotFoundError when agent is not found', async () => {
      agentRepo.findPublicById = jest.fn().mockResolvedValue(null);

      await expect(service.getPublicAgent('nonexistent')).rejects.toThrow(AgentNotFoundError);
    });
  });
});
