/**
 * Marketplace Service for SentryAgent.ai AgentIdP.
 * Business logic for the public Agent Marketplace.
 * Only exposes agents where is_public = true AND status = active.
 */

import { AgentRepository } from '../repositories/AgentRepository.js';
import { AgentNotFoundError } from '../utils/errors.js';
import {
  IMarketplaceFilters,
  IMarketplaceAgentCard,
  IPaginatedMarketplaceResponse,
  IMinimalDIDDocument,
  IAgent,
} from '../types/index.js';

/**
 * Builds a minimal W3C DID Document from agent data when no key material is present.
 * Used as a fallback when the agent has a DID assigned but no key record exists yet,
 * or when constructing the document purely from agent identity data.
 *
 * @param agent - The agent record.
 * @returns A minimal DID document, or null if the agent has no DID.
 */
function buildMinimalDIDDocument(agent: IAgent): IMinimalDIDDocument | null {
  if (!agent.did) {
    return null;
  }

  return {
    '@context': [
      'https://www.w3.org/ns/did/v1',
      'https://w3id.org/security/suites/jws-2020/v1',
    ],
    id: agent.did,
    controller: agent.did,
    verificationMethod: [],
    authentication: [],
  };
}

/**
 * Maps an IAgent record to the public IMarketplaceAgentCard shape.
 * Strips all private fields (email, organizationId, internal status details).
 *
 * @param agent - The internal agent record.
 * @returns The public-facing marketplace agent card.
 */
function mapAgentToCard(agent: IAgent): IMarketplaceAgentCard {
  return {
    agentId: agent.agentId,
    agentType: agent.agentType,
    version: agent.version,
    capabilities: agent.capabilities,
    owner: agent.owner,
    deploymentEnv: agent.deploymentEnv,
    did: agent.did ?? null,
    didDocument: buildMinimalDIDDocument(agent),
    publishedAt: agent.updatedAt,
  };
}

/**
 * Service for the public Agent Marketplace.
 * All methods enforce the is_public = true constraint via the repository.
 */
export class MarketplaceService {
  /**
   * @param agentRepository - The agent data repository.
   */
  constructor(private readonly agentRepository: AgentRepository) {}

  /**
   * Returns a paginated, optionally filtered list of public agents.
   * Supports free-text search (?q=), capability filter (?capability=),
   * and publisher filter (?publisher=).
   *
   * @param filters - Marketplace filter and pagination criteria.
   * @returns Paginated public agent card listing.
   */
  async listPublicAgents(filters: IMarketplaceFilters): Promise<IPaginatedMarketplaceResponse> {
    const { agents, total } = await this.agentRepository.findPublicAgents(filters);
    return {
      data: agents.map(mapAgentToCard),
      total,
      page: filters.page,
      limit: filters.limit,
    };
  }

  /**
   * Returns a single public agent with its DID document and agent card.
   * Only succeeds when the agent is both public (is_public = true) and active.
   *
   * @param agentId - The agent UUID to retrieve.
   * @returns The public marketplace agent card.
   * @throws AgentNotFoundError if the agent does not exist, is private, or is not active.
   */
  async getPublicAgent(agentId: string): Promise<IMarketplaceAgentCard> {
    const agent = await this.agentRepository.findPublicById(agentId);
    if (!agent) {
      throw new AgentNotFoundError(agentId);
    }
    return mapAgentToCard(agent);
  }
}
