/**
 * One-shot migration: lift connector rows out of plugin-configs.json
 * into connector-configs.json.
 *
 * Pre-v0.9 Forge stored connector configs under plugin-configs.json
 * with `category: connector` on the plugin def. From v0.9 connectors
 * are an independent subsystem; configs live in connector-configs.json
 * and manifests come from the remote forge-connectors registry (no
 * built-in fallback).
 *
 * Migration moves the encrypted config blob (PAT, base_url, etc.)
 * keyed by KNOWN_CONNECTOR_IDS — the set of connector ids that used
 * to be built in. Manifests are NOT written here; they arrive on the
 * next syncRegistry() call. If the user is offline at upgrade time,
 * the marketplace will be empty until they reconnect — config rows
 * are preserved so re-install restores everything.
 *
 * Idempotent: once a connector id has been moved, its row is gone
 * from plugin-configs.json and subsequent calls are no-ops.
 */

import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { getDataDir } from '../dirs';

/**
 * IDs that used to ship as built-in connectors in lib/builtin-plugins/.
 * Add to this list when promoting a new built-in connector into the
 * marketplace, but only for the upgrade window — once your user base
 * has restarted with the new code, the row is gone and the entry
 * becomes inert.
 */
const KNOWN_CONNECTOR_IDS = ['mantis', 'gitlab', 'teams', 'pmdb', 'github-api'];

function pluginConfigsPath(): string {
  return join(getDataDir(), 'plugin-configs.json');
}

function connectorConfigsPath(): string {
  return join(getDataDir(), 'connector-configs.json');
}

function ensureDir(p: string): void {
  if (!existsSync(p)) mkdirSync(p, { recursive: true, mode: 0o700 });
}

interface ConnectorConfigRow {
  config: Record<string, unknown>;
  /**
   * Version recorded at migration time. We don't know what version
   * the user was running before — write '0.0.0' so the next sync,
   * which sees the registry version > 0.0.0, flags an update and
   * pulls the manifest. The marketplace UI shows it as "Update
   * available" briefly until that completes.
   */
  installed_version: string;
  enabled?: boolean;
}

export function migrateConnectorConfigs(): { migrated: number; skipped: number } {
  const oldPath = pluginConfigsPath();
  if (!existsSync(oldPath)) return { migrated: 0, skipped: 0 };

  let oldStore: Record<string, any>;
  try { oldStore = JSON.parse(readFileSync(oldPath, 'utf-8')); }
  catch { return { migrated: 0, skipped: 0 }; }

  const newPath = connectorConfigsPath();
  let newStore: Record<string, ConnectorConfigRow> = {};
  if (existsSync(newPath)) {
    try { newStore = JSON.parse(readFileSync(newPath, 'utf-8')); }
    catch {}
  }

  let migrated = 0;
  let skipped = 0;
  const toRemoveFromOld: string[] = [];

  for (const id of KNOWN_CONNECTOR_IDS) {
    const oldRow = oldStore[id];
    if (!oldRow) {
      skipped += 1;
      continue;
    }
    if (!newStore[id]) {
      newStore[id] = {
        config: oldRow.config || {},
        installed_version: '0.0.0',
        enabled: oldRow.enabled !== false,
      };
      migrated += 1;
    }
    toRemoveFromOld.push(id);
  }

  if (toRemoveFromOld.length > 0) {
    ensureDir(getDataDir());
    writeFileSync(newPath, JSON.stringify(newStore, null, 2), { mode: 0o600 });

    for (const id of toRemoveFromOld) delete oldStore[id];
    writeFileSync(oldPath, JSON.stringify(oldStore, null, 2), { mode: 0o600 });

    console.log(
      `[connectors] migration: moved ${migrated} row(s) from plugin-configs.json. ` +
        'Manifests will arrive on the next registry sync.',
    );
  }

  return { migrated, skipped };
}
