import type { ApiClient } from '../api-client.js';

export const RESOURCE_ALIASES: Record<string, string> = {
  server: 'servers',
  srv: 'servers',
  project: 'projects',
  proj: 'projects',
  instance: 'instances',
  inst: 'instances',
  secret: 'secrets',
  sec: 'secrets',
  template: 'templates',
  tpl: 'templates',
  user: 'users',
  group: 'groups',
  rbac: 'rbac',
  'rbac-definition': 'rbac',
  'rbac-binding': 'rbac',
  prompt: 'prompts',
  prompts: 'prompts',
  promptrequest: 'promptrequests',
  promptrequests: 'promptrequests',
  pr: 'promptrequests',
  // PR-2: shared revision + proposal queue (replaces promptrequests in
  // PR-7). Lookup goes through /api/v1/proposals and /api/v1/revisions.
  proposal: 'proposals',
  proposals: 'proposals',
  prop: 'proposals',
  revision: 'revisions',
  revisions: 'revisions',
  rev: 'revisions',
  // PR-3: skill resource. Same shape as prompt but materialised onto
  // disk by `mcpctl skills sync` (PR-5).
  skill: 'skills',
  skills: 'skills',
  sk: 'skills',
  serverattachment: 'serverattachments',
  serverattachments: 'serverattachments',
  sa: 'serverattachments',
  proxymodel: 'proxymodels',
  proxymodels: 'proxymodels',
  pm: 'proxymodels',
  mcptoken: 'mcptokens',
  mcptokens: 'mcptokens',
  token: 'mcptokens',
  tokens: 'mcptokens',
  secretbackend: 'secretbackends',
  secretbackends: 'secretbackends',
  sb: 'secretbackends',
  llm: 'llms',
  llms: 'llms',
  agent: 'agents',
  agents: 'agents',
  personality: 'personalities',
  personalities: 'personalities',
  thread: 'threads',
  threads: 'threads',
  // v5: durable inference task queue. URL prefix is `/api/v1/inference-tasks`
  // (multi-word for clarity); the operator typically wants short
  // forms like `mcpctl get tasks`. All variants normalize to the
  // canonical plural so URL construction works uniformly.
  task: 'inference-tasks',
  tasks: 'inference-tasks',
  'inference-task': 'inference-tasks',
  'inference-tasks': 'inference-tasks',
  all: 'all',
};

export function resolveResource(name: string): string {
  const lower = name.toLowerCase();
  return RESOURCE_ALIASES[lower] ?? lower;
}

/** Resolve a name-or-ID to an ID. CUIDs pass through; names are looked up. */
export async function resolveNameOrId(
  client: ApiClient,
  resource: string,
  nameOrId: string,
): Promise<string> {
  // CUIDs start with 'c' followed by 24+ alphanumeric chars
  if (/^c[a-z0-9]{24}/.test(nameOrId)) {
    return nameOrId;
  }
  // Users resolve by email, not name
  if (resource === 'users') {
    const items = await client.get<Array<{ id: string; email: string }>>(`/api/v1/${resource}`);
    const match = items.find((item) => item.email === nameOrId);
    if (match) return match.id;
    throw new Error(`user '${nameOrId}' not found`);
  }
  const items = await client.get<Array<Record<string, unknown>>>(`/api/v1/${resource}`);
  const match = items.find((item) => {
    // Instances use server.name, other resources use name directly
    if (resource === 'instances') {
      const server = item.server as { name?: string } | undefined;
      return server?.name === nameOrId;
    }
    return item.name === nameOrId;
  });
  if (match) return match.id as string;
  throw new Error(`${resource.replace(/s$/, '')} '${nameOrId}' not found`);
}

/** Strip internal/read-only fields from an API response to make it apply-compatible. */
export function stripInternalFields(obj: Record<string, unknown>): Record<string, unknown> {
  const result = { ...obj };
  for (const key of ['id', 'createdAt', 'updatedAt', 'version', 'ownerId', 'summary', 'chapters', 'linkStatus', 'serverId']) {
    delete result[key];
  }

  // McpToken-specific: promote projectName → project; drop secret/derived fields
  if ('tokenHash' in result || 'tokenPrefix' in result) {
    delete result.tokenHash;
    delete result.tokenPrefix;
    delete result.lastUsedAt;
    delete result.revokedAt;
    delete result.status;
    delete result.ownerEmail;
    if (typeof result.projectName === 'string') {
      result.project = result.projectName;
      delete result.projectName;
      delete result.projectId;
    }
  }

  // Rename linkTarget → link for cleaner YAML
  if ('linkTarget' in result) {
    result.link = result.linkTarget;
    delete result.linkTarget;
    // Linked prompts: strip content (it's fetched from the link source, not static)
    if (result.link) {
      delete result.content;
    }
  }

  // Convert project servers join array → string[] of server names
  if ('servers' in result && Array.isArray(result.servers)) {
    const entries = result.servers as Array<{ server?: { name: string } }>;
    if (entries.length > 0 && entries[0]?.server) {
      result.servers = entries.map((e) => e.server!.name);
    } else if (entries.length === 0) {
      result.servers = [];
    } else {
      delete result.servers;
    }
  }

  // Convert prompt projectId CUID → project name string
  if ('project' in result && typeof result.project === 'object' && result.project !== null) {
    const proj = result.project as { name: string };
    result.project = proj.name;
    delete result.projectId;
  }

  // Strip remaining relationship objects
  if ('owner' in result && typeof result.owner === 'object') {
    delete result.owner;
  }
  if ('members' in result && Array.isArray(result.members)) {
    delete result.members;
  }

  // Normalize proxyModel: resolve from gated when empty, then drop deprecated gated field
  if ('gated' in result || 'proxyModel' in result) {
    if (!result.proxyModel) {
      result.proxyModel = result.gated === false ? 'content-pipeline' : 'default';
    }
    delete result.gated;
  }

  // Strip null values last (null = unset, omitting from YAML is cleaner and equivalent)
  for (const key of Object.keys(result)) {
    if (result[key] === null) {
      delete result[key];
    }
  }

  return result;
}
