/**
 * Per-user settings for an installed connector.
 *
 * GET  → masked settings + schema + installed flag
 * POST → save settings (preserves stored secrets if client returns
 *        the bullet placeholder unchanged)
 *
 * Secrets (fields with type === 'secret') are encrypted at rest by
 * lib/connectors/registry.ts (AES-256-GCM via lib/crypto.ts) and
 * masked here so the plaintext never crosses the wire on GET.
 */

import { NextResponse } from 'next/server';
import {
  getConnector,
  getInstalledConnector,
  setConnectorConfig,
} from '@/lib/connectors/registry';
import type { ConnectorFieldSchema } from '@/lib/connectors/types';

function settingsSchemaFor(id: string): Record<string, ConnectorFieldSchema> {
  const def = getConnector(id);
  if (!def) return {};
  const merged: Record<string, ConnectorFieldSchema> = {};
  if (def.settings) Object.assign(merged, def.settings);
  // 1:N suites: union of each entry's settings
  for (const entry of def.connectors || []) {
    if (entry.settings) Object.assign(merged, entry.settings);
  }
  return merged;
}

function defaultsFor(id: string): Record<string, any> {
  const schema = settingsSchemaFor(id);
  const out: Record<string, any> = {};
  for (const [k, v] of Object.entries(schema)) {
    if (v.default !== undefined) out[k] = v.default;
  }
  return out;
}

const SECRET_MASK = '••••••••';

/**
 * Walk `value` against `schema`, applying `mask` (plaintext → ••••) or
 * `restore` (•••• → stored plaintext). Recurses into `type: 'instances'`
 * arrays so per-row sub-secrets get the same mask treatment as flat
 * top-level secrets. Mirrors the encrypt/decrypt walker in
 * lib/connectors/registry.ts so the on-disk and over-the-wire
 * representations stay symmetric.
 */
function transformFieldSecrets(
  value: any,
  schema: ConnectorFieldSchema | undefined,
  existingValue: any,
  op: 'mask' | 'restore',
): any {
  if (!schema) return value;
  const t = String((schema as any)?.type || '');

  if (t === 'secret' || t === 'password') {
    if (op === 'mask') {
      return typeof value === 'string' && value ? SECRET_MASK : value;
    }
    // restore
    if (value === SECRET_MASK) {
      return typeof existingValue === 'string' ? existingValue : undefined;
    }
    return value;
  }

  if (t === 'instances' && (schema as any).fields) {
    const wasString = typeof value === 'string';
    let rows: any;
    if (wasString) {
      try { rows = JSON.parse(value); } catch { return value; }
    } else {
      rows = value;
    }
    if (!Array.isArray(rows)) return value;

    // Build name→row map of the existing stored value so per-instance
    // mask restoration can find the corresponding row by name.
    let existingRows: any[] = [];
    if (typeof existingValue === 'string') {
      try { existingRows = JSON.parse(existingValue); } catch { existingRows = []; }
    } else if (Array.isArray(existingValue)) {
      existingRows = existingValue;
    }
    const existingByName = new Map<string, any>();
    for (const r of existingRows) {
      if (r && typeof r === 'object' && typeof r.name === 'string') existingByName.set(r.name, r);
    }

    const transformed = rows.map((row: any) => {
      if (!row || typeof row !== 'object') return row;
      const existingRow = (typeof row.name === 'string' && existingByName.get(row.name)) || {};
      const out: any = { ...row };
      for (const [k, sub] of Object.entries((schema as any).fields)) {
        out[k] = transformFieldSecrets(out[k], sub as ConnectorFieldSchema, existingRow[k], op);
      }
      return out;
    });
    return wasString ? JSON.stringify(transformed) : transformed;
  }

  return value;
}

function maskSecrets(settings: Record<string, any>, schema: Record<string, ConnectorFieldSchema>): Record<string, any> {
  const out: Record<string, any> = { ...settings };
  for (const [k, v] of Object.entries(schema)) {
    out[k] = transformFieldSecrets(out[k], v, undefined, 'mask');
  }
  return out;
}

function restoreSecrets(
  incoming: Record<string, any>,
  existing: Record<string, any>,
  schema: Record<string, ConnectorFieldSchema>,
): Record<string, any> {
  const out: Record<string, any> = { ...incoming };
  for (const [k, v] of Object.entries(schema)) {
    const restored = transformFieldSecrets(out[k], v, existing[k], 'restore');
    if (restored === undefined) delete out[k];
    else out[k] = restored;
  }
  return out;
}

export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  const def = getConnector(id);
  if (!def) return NextResponse.json({ error: 'connector not found' }, { status: 404 });
  const inst = getInstalledConnector(id);
  const stored = inst?.config || {};
  const schema = settingsSchemaFor(id);
  const merged = { ...defaultsFor(id), ...stored };
  return NextResponse.json({
    settings: maskSecrets(merged, schema),
    schema,
    installed: !!inst,
  });
}

export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  const def = getConnector(id);
  if (!def) return NextResponse.json({ error: 'connector not found' }, { status: 404 });

  const body = await req.json().catch(() => ({}));
  const settings = body?.settings ?? body;
  if (typeof settings !== 'object' || settings === null) {
    return NextResponse.json({ error: 'settings must be an object' }, { status: 400 });
  }

  const existing = getInstalledConnector(id);
  const schema = settingsSchemaFor(id);
  const merged = restoreSecrets(settings, existing?.config || {}, schema);
  const ok = setConnectorConfig(id, merged);
  if (!ok) return NextResponse.json({ error: 'failed to persist settings' }, { status: 500 });
  return NextResponse.json({ ok: true, settings: maskSecrets(merged, schema) });
}
