import { resolvePackageRegistry } from '../utils/config-store.js';
import { resolveRegistryRoot } from '../utils/package-store.js';
import { searchPackageDiscoveryIndex } from '../utils/discovery-index.js';
import {
  searchNpmRegistry,
  type INpmRegistrySearchResult,
} from '../utils/npm-registry-client.js';

export interface ISearchCommandOptions {
  readonly cwd?: string;
  readonly registryDir?: string;
  readonly registry?: string;
  readonly credentialsPath?: string;
  readonly limit?: number;
}

export interface IPackageSearchResult extends INpmRegistrySearchResult {
  readonly displayName?: string;
  readonly inferredKind?: string;
  readonly declaredKind?: string;
  readonly matchedFields?: readonly string[];
  readonly score?: number;
}

export interface ISearchCommandResult {
  readonly query: string;
  readonly registry: string;
  readonly registrySource: 'explicit' | 'env' | 'config' | 'default' | 'local';
  readonly results: readonly IPackageSearchResult[];
}

export async function searchCommand(
  query: string,
  options: ISearchCommandOptions = {},
): Promise<ISearchCommandResult> {
  const cwd = options.cwd ?? process.cwd();
  const limit = normalizeLimit(options.limit);
  if (options.registry && options.registryDir) {
    throw new Error('MX_REGISTRY_INVALID: use either --registry or --registry-dir, not both');
  }
  if (options.registryDir) {
    const registryRoot = resolveRegistryRoot(cwd, options.registryDir);
    const results = searchPackageDiscoveryIndex(
      registryRoot,
      { text: query.trim() },
      limit,
    ).map((entry): IPackageSearchResult => ({
      packageName: entry.packageName,
      version: entry.version,
      ...(entry.description ? { description: entry.description } : {}),
      ...(entry.displayName ? { displayName: entry.displayName } : {}),
      ...(entry.inferredKind ? { inferredKind: entry.inferredKind } : {}),
      ...(entry.declaredKind ? { declaredKind: entry.declaredKind } : {}),
      keywords: entry.keywords,
      matchedFields: entry.matchedFields,
      score: entry.score,
    }));
    return {
      query: query.trim(),
      registry: registryRoot,
      registrySource: 'local',
      results,
    };
  }
  const registry = resolvePackageRegistry(options.registry, { cwd });
  const results = await searchNpmRegistry(
    query,
    registry.registry,
    cwd,
    options.credentialsPath,
    limit,
  );
  return {
    query: query.trim(),
    registry: registry.registry,
    registrySource: registry.source,
    results,
  };
}

function normalizeLimit(value: number | undefined): number {
  if (value === undefined) {
    return 20;
  }
  if (!Number.isInteger(value) || value <= 0 || value > 100) {
    throw new Error('MX_SEARCH_INVALID: --limit must be an integer from 1 to 100');
  }
  return value;
}
