// lib/connections/health-check-verify.js — generic "Test connection" verifier.
//
// The curated VERIFIERS map in app/api/connections/[service]/verify/route.js
// covers the 9 seeded services with bespoke logic. This is the FALLBACK: any
// connection manifest that declares a `health_check` block gets a working "Test
// connection" with no edits to the route — closing the verify_supported:false gap
// for Tool-Smith-authored connectors and the new OAuth connections.
//
// SECURITY: the resolved credential is injected into the outbound request and is
// NEVER returned, logged, or echoed. The result carries only non-secret display
// metadata (account_label, last_4 fingerprint). Error reasons are generic codes,
// never the credential value.
//
// health_check shape (declared in the connection manifest):
//   {
//     endpoint: 'https://api.example.com/me',   // required, https
//     method: 'GET',                             // optional, default GET
//     expect_status: 200,                        // optional, default: any 2xx
//     auth: {                                    // optional credential injection
//       in: 'header',                            //   'header' | 'query'
//       name: 'Authorization',                   //   header/param name
//       prefix: 'Bearer ',                       //   value prefix
//       credential: 'META_ACCESS_TOKEN'          //   key into the credentials map
//     }
//   }

import { wrapFetch } from '../egress.js';

function isHttpsUrl(value) {
  if (typeof value !== 'string') return false;
  try {
    return new URL(value).protocol === 'https:';
  } catch {
    return false;
  }
}

function last4(value) {
  const s = String(value || '');
  return s.length >= 4 ? s.slice(-4) : null;
}

/**
 * Run a manifest-declared health check.
 *
 * @param {object} healthCheck      manifest.health_check
 * @param {object} credentials      { NAME: value } resolved for this attempt
 * @param {object} [opts]           { fetchFn, serviceKey, agentId }
 * @returns {Promise<{ ok, reason?, account_label?, last_4?, summary? }>}
 */
export async function verifyViaHealthCheck(healthCheck, credentials = {}, opts = {}) {
  if (!healthCheck || typeof healthCheck !== 'object') {
    return { ok: false, reason: 'unsupported' };
  }
  if (!isHttpsUrl(healthCheck.endpoint)) {
    return { ok: false, reason: 'unsupported' };
  }

  const method = /^(GET|POST|PUT|PATCH|DELETE|HEAD)$/.test(String(healthCheck.method || ''))
    ? String(healthCheck.method)
    : 'GET';

  let url;
  try {
    url = new URL(healthCheck.endpoint);
  } catch {
    return { ok: false, reason: 'unsupported' };
  }

  const headers = { Accept: 'application/json' };
  let injectedCredentialValue = null;

  const auth = healthCheck.auth;
  if (auth && typeof auth === 'object' && auth.credential) {
    const value = credentials[auth.credential];
    if (typeof value !== 'string' || !value.trim()) {
      // The credential the health check needs wasn't provided — treat as missing.
      return { ok: false, reason: 'missing_field', field: auth.credential };
    }
    injectedCredentialValue = value.trim();
    const composed = `${auth.prefix || ''}${injectedCredentialValue}`;
    if (auth.in === 'query') {
      url.searchParams.set(auth.name || 'access_token', composed);
    } else {
      headers[auth.name || 'Authorization'] = composed;
    }
  }

  const fetchFn = opts.fetchFn || wrapFetch(`connection.verify.health.${opts.serviceKey || 'generic'}`, opts.agentId || null, fetch);

  let res;
  try {
    res = await fetchFn(url.toString(), { method, headers });
  } catch {
    // Never surface the underlying error string — it could contain the URL with
    // an injected query credential.
    return { ok: false, reason: 'network' };
  }

  const expect = Number.isInteger(healthCheck.expect_status) ? healthCheck.expect_status : null;
  const passed = expect != null ? res.status === expect : (res.status >= 200 && res.status < 300);

  if (!passed) {
    if (res.status === 401 || res.status === 403) return { ok: false, reason: 'unauthorized' };
    return { ok: false, reason: 'invalid_key' };
  }

  return {
    ok: true,
    account_label: opts.accountLabel || (opts.serviceKey ? String(opts.serviceKey) : 'Connected'),
    last_4: injectedCredentialValue ? last4(injectedCredentialValue) : null,
    summary: { health_check: true, status: res.status },
  };
}
