// lib/tools/adapters/connector-definitions.js
//
// Adapters for `create_connector` / `update_connector` (Option D). Both call the
// connector sole-writer (upsertConnector). The strict connector-validator is the
// authority on shape; approval/role gates fire in executeRegistryTool first.
//
// On create, a paired kind:'connection' marketplace item is auto-authored so the
// owner can drop the API key into the vault once. Supplying a credential is the
// ONE human step — it is providing a secret, NOT approving code. The connector
// itself is live the instant it is written; its endpoints become callable as soon
// as the referenced secret is present in the vault.

import { getDb, nowIso } from '../../db.js';
import { upsertConnector, getConnector, archiveConnector } from '../connectors/registry.js';

function asActingUserId(options) {
  return options?.acting_on_behalf_of_user_id || options?.user_id || null;
}

function hasAuthoringAuthority(options = {}) {
  if (options.owner_trusted) return true;
  if (options.actor_role === 'owner' || options.actor_role === 'admin') return true;
  const userId = asActingUserId(options);
  if (!userId || userId === 'system') return false;
  try {
    const row = getDb()
      .prepare("SELECT role FROM org_memberships WHERE user_id=? AND org_id=COALESCE(?, 'personal-org')")
      .get(userId, options.org_id || 'personal-org');
    return row?.role === 'owner' || row?.role === 'admin';
  } catch {
    return false;
  }
}

function insufficientRole() {
  return {
    ok: false,
    status: 'error',
    error: 'insufficient_role',
    message: 'Connector authoring requires owner/admin authority in addition to tool approval.',
  };
}

function structuredErrorFromUpsert(err) {
  if (err?.code === 'spec_invalid') {
    return { ok: false, status: 'error', error: 'spec_invalid', errors: err.errors || [err.message] };
  }
  return null;
}

/**
 * Author (idempotently) the paired kind:'connection' marketplace item so the
 * setup assistant can collect the API key. Best-effort: a failure here never
 * fails the connector write (the connector is the source of truth).
 */
function ensureConnectionItem({ slug, title, secretRefs, toolSlugs }) {
  if (!secretRefs.length) return; // auth.type === 'none' — nothing to collect.
  try {
    const db = getDb();
    const id = `connection:${slug}`;
    const manifest = {
      service_key: slug,
      setup_kind: 'api_key',
      benefit: `Unlocks the ${slug} connector endpoints.`,
      credentials: secretRefs.map((name) => ({
        name,
        label: name,
        type: 'password',
        required: true,
        help_text: `API credential for the ${slug} connector. Stored encrypted in the secrets vault.`,
      })),
      verify_endpoint: null,
      verify_supported: false,
      deep_link: null,
      supports_scopes: ['global', 'project', 'agent'],
      unlocks_tool_slugs: toolSlugs,
      engines: [],
      logo_glyph: slug,
    };
    const tags = JSON.stringify(['connection', slug]);
    const now = nowIso();
    const existing = db.prepare("SELECT id FROM marketplace_items WHERE kind='connection' AND slug=?").get(slug);
    if (existing) {
      db.prepare(`UPDATE marketplace_items
          SET title=?, description=?, manifest_json=?, tags=? WHERE id=?`)
        .run(title || slug, `Connect ${slug}`, JSON.stringify(manifest), tags, existing.id);
    } else {
      db.prepare(`INSERT INTO marketplace_items
          (id, kind, slug, title, description, category, author, source_kind, source_url, version, manifest_json, risk_summary, tags, created_at)
        VALUES (?, 'connection', ?, ?, ?, 'connector', 'tool-smith', 'authored', NULL, '1.0', ?, ?, ?, ?)`)
        .run(id, slug, title || slug, `Connect ${slug}`, JSON.stringify(manifest),
          'Provide an API credential — no code runs.', tags, now);
      db.prepare('INSERT OR IGNORE INTO marketplace_installations (item_id, installed) VALUES (?, 0)').run(id);
    }
  } catch {
    // Non-fatal — the connector exists; the key can still be set via the vault.
  }
}

export async function createConnector(input, options = {}) {
  if (!hasAuthoringAuthority(options)) return insufficientRole();
  const manifest = input?.manifest;
  if (!manifest || typeof manifest !== 'object') {
    return { ok: false, status: 'error', error: 'spec_required', message: 'manifest required (object)' };
  }
  if (!manifest.slug || typeof manifest.slug !== 'string') {
    return { ok: false, error: 'slug_required', message: 'manifest.slug required (string)' };
  }
  if (getConnector(manifest.slug)) {
    return { ok: false, status: 'error', error: 'slug_in_use', slug: manifest.slug };
  }
  try {
    const r = upsertConnector(manifest, {
      created_by_user_id: asActingUserId(options),
      created_by_agent_id: options?.agent_id || null,
    });
    ensureConnectionItem({ slug: r.slug, title: manifest.title, secretRefs: r.secret_refs, toolSlugs: r.tool_slugs });
    return {
      ok: true,
      status: 'complete',
      slug: r.slug,
      endpoints: r.endpoints,
      tool_slugs: r.tool_slugs,
      secret_refs: r.secret_refs,
      setup_required: r.secret_refs.length > 0,
      warnings: r.warnings || [],
    };
  } catch (err) {
    const structured = structuredErrorFromUpsert(err);
    if (structured) return structured;
    throw err;
  }
}

export async function updateConnector(input, options = {}) {
  if (!hasAuthoringAuthority(options)) return insufficientRole();
  const slug = typeof input?.slug === 'string' ? input.slug.trim() : '';
  const manifest = input?.manifest;
  if (!slug) return { ok: false, status: 'error', error: 'slug_required', message: 'slug required (string)' };
  if (!manifest || typeof manifest !== 'object') {
    return { ok: false, status: 'error', error: 'spec_required', message: 'manifest required (object)' };
  }
  if (!getConnector(slug)) return { ok: false, status: 'error', error: 'not_found', slug };
  const cloned = { ...manifest, slug };
  try {
    const r = upsertConnector(cloned, {
      created_by_user_id: asActingUserId(options),
      created_by_agent_id: options?.agent_id || null,
    });
    ensureConnectionItem({ slug: r.slug, title: manifest.title, secretRefs: r.secret_refs, toolSlugs: r.tool_slugs });
    return {
      ok: true, status: 'complete', slug: r.slug, endpoints: r.endpoints,
      tool_slugs: r.tool_slugs, secret_refs: r.secret_refs, warnings: r.warnings || [],
    };
  } catch (err) {
    const structured = structuredErrorFromUpsert(err);
    if (structured) return structured;
    throw err;
  }
}

export { archiveConnector };
