import crypto from 'node:crypto';
import fastify, { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import cors from '@fastify/cors';
import helmet from '@fastify/helmet';
import cookie from '@fastify/cookie';
import type { SendRequest, Channel, ApplicationConfig, RoutingConfig, NormalizedMessage } from '@zyphon/shared-types';
import { DailyLogReader, type DailyLogWriter } from '@zyphon/logger';
import { createAdapter } from './core/adapter-factory.js';
import type { DispatchService } from './messaging/dispatch-service.js';
import type { ApplicationRegistry } from './core/application-registry.js';
import type { RoutingEngine } from './core/routing-engine.js';
import type { CredentialVault } from './core/credential-vault.js';
import type { CircuitBreakerManager } from './core/circuit-breaker.js';
import type { ProviderRegistry } from './core/provider-registry.js';
import type { EventBus } from './realtime/event-bus.js';
import type { AdminAuthManager } from './auth/admin-auth.js';
import type { MetricsCollector } from './core/metrics.js';
import type { ConfigLoader } from '@zyphon/config-loader';
import {
  MessageTemplateSchema,
  DltFileSchema,
  SuppressionsFileSchema,
  AlertsFileSchema,
  ChannelStrategyEnum,
} from '@zyphon/config-schema';
import { buildAdminCatalogResponse } from './catalog/provider-catalog.js';
import {
  guessProviderNameFromId,
  loadProviderArchive,
  upsertArchivedProvider,
} from './core/provider-archive.js';

export interface GatewayServerOptions {
  dispatchService: DispatchService;
  appRegistry: ApplicationRegistry;
  routingEngine: RoutingEngine;
  vault: CredentialVault;
  circuitBreaker: CircuitBreakerManager;
  providerRegistry: ProviderRegistry;
  eventBus: EventBus;
  adminAuth: AdminAuthManager;
  metrics: MetricsCollector;
  logWriter: DailyLogWriter;
  logReader: DailyLogReader;
  configLoader: ConfigLoader;
  /** Used for deleted-provider name archive (insights history). */
  stateDir?: string;
}

export function createGatewayServers(opts: GatewayServerOptions): {
  publicApp: FastifyInstance;
  adminApp: FastifyInstance;
} {
  // 1. PUBLIC API SERVER
  const publicApp = fastify({
    logger: false,
    disableRequestLogging: true,
  });

  publicApp.register(cors, { origin: true });
  publicApp.register(helmet, { contentSecurityPolicy: false });

  // Public Healthcheck
  publicApp.get('/api/v1/health', async () => {
    return {
      status: 'ok',
      uptime: process.uptime(),
      timestamp: Date.now(),
    };
  });

  // Message Dispatch Endpoint
  publicApp.post('/api/v1/messages', async (request: FastifyRequest, reply: FastifyReply) => {
    const rawApiKey = (request.headers['x-api-key'] as string) ||
      (request.headers.authorization?.startsWith('Bearer ')
        ? request.headers.authorization.slice(7)
        : undefined);

    const idempotencyKey = request.headers['x-idempotency-key'] as string | undefined;
    const query = request.query as Record<string, string | undefined>;
    const sync = query.sync === 'false' ? false : true;

    const payload = request.body as SendRequest;
    if (!payload || !payload.channel || !payload.recipient || !payload.content) {
      return reply.status(400).send({
        error: {
          code: 'VALIDATION_FAILED',
          message: 'Payload requires channel, recipient, and content',
        },
      });
    }

    const result = await opts.dispatchService.dispatch(payload, {
      apiKey: rawApiKey,
      idempotencyKey,
      sync,
      clientIp: extractClientIp(request),
    });

    if (result.cacheHit) {
      reply.header('X-Cache', 'HIT');
    }

    opts.metrics.record({
      channel: payload.channel,
      provider: result.response.provider,
      status: result.response.status === 'delivered' ? 'delivered' : result.response.status === 'accepted' ? 'accepted' : 'failed',
      latencyMs: result.response.latency_ms,
      timestamp: Date.now(),
    });

    return reply.status(result.statusCode).send(result.response);
  });

  // Message Status Lookup
  publicApp.get('/api/v1/messages/:id', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
    const messageId = request.params.id;
    const res = await opts.logReader.query({ query: messageId, limit: 1 });
    if (!res.records || res.records.length === 0) {
      return reply.status(404).send({
        error: {
          code: 'NOT_FOUND',
          message: `Message '${messageId}' not found`,
        },
      });
    }
    return reply.send(res.records[0]);
  });

  const handleInboundWebhook = async (
    providerId: string,
    request: FastifyRequest,
    reply: FastifyReply,
  ) => {
    const adapter = opts.providerRegistry.getAdapter(providerId);

    if (adapter && adapter.parseWebhook) {
      const event = adapter.parseWebhook({
        headers: request.headers,
        body: request.body,
        query: request.query as any,
      });

      if (event) {
        opts.logWriter.write({
          ev: 'delivery',
          msgId: event.message_id,
          status: event.status,
          ts: event.timestamp,
          provider: event.provider,
          details: event.raw as any,
        });

        opts.eventBus.publish('delivery_updated', event);
      }
    }

    return reply.status(200).send({ received: true });
  };

  // Inbound Webhooks (v5 shape + legacy alias)
  publicApp.post(
    '/webhooks/:type/:id',
    async (request: FastifyRequest<{ Params: { type: string; id: string } }>, reply: FastifyReply) => {
      return handleInboundWebhook(request.params.id, request, reply);
    },
  );

  publicApp.post('/webhooks/:provider', async (request: FastifyRequest<{ Params: { provider: string } }>, reply: FastifyReply) => {
    return handleInboundWebhook(request.params.provider, request, reply);
  });

  // 2. ADMIN API SERVER
  const adminApp = fastify({
    logger: false,
    disableRequestLogging: true,
  });

  adminApp.register(cors, { origin: true, credentials: true });
  adminApp.register(helmet, { contentSecurityPolicy: false });
  adminApp.register(cookie);

  const isProduction = process.env.NODE_ENV === 'production';
  const sessionCookieOptions = {
    path: '/',
    httpOnly: true,
    sameSite: 'lax' as const,
    secure: process.env.ZYPHON_SECURE_COOKIES === 'true',
  };

  // Admin Health
  adminApp.get('/admin/openapi.json', async (_request, reply) => {
    try {
      const { loadOpenApiSpec } = await import('./openapi/load-spec.js');
      return loadOpenApiSpec();
    } catch (err: unknown) {
      const message = err instanceof Error ? err.message : 'OpenAPI spec unavailable';
      return reply.status(503).send({ error: { code: 'OPENAPI_UNAVAILABLE', message } });
    }
  });

  adminApp.get('/admin/health', async () => {
    const providerHealths = await opts.providerRegistry.checkAllHealth();
    return {
      status: 'ok',
      version: '1.0.0',
      uptime: process.uptime(),
      providers: providerHealths,
    };
  });

  // Prometheus Metrics endpoint
  adminApp.get('/metrics', async (_request, reply) => {
    reply.header('Content-Type', 'text/plain; version=0.0.4');
    return reply.send(opts.metrics.toPrometheusFormat());
  });

  // Admin Login
  adminApp.post('/admin/auth/login', async (request: FastifyRequest, reply: FastifyReply) => {
    const body = request.body as { password?: string };
    if (!body || !body.password) {
      return reply.status(400).send({ error: { code: 'VALIDATION_FAILED', message: 'Password is required' } });
    }

    const authResult = opts.adminAuth.authenticate(body.password, request.ip);
    if (!authResult.success) {
      return reply.status(401).send({ error: { code: 'UNAUTHORIZED', message: authResult.error || 'Authentication failed' } });
    }

    const csrfToken = crypto.randomBytes(32).toString('hex');

    reply.setCookie('zyphon_session', authResult.token!, sessionCookieOptions);
    reply.setCookie('zyphon_csrf', csrfToken, {
      ...sessionCookieOptions,
      httpOnly: false,
    });

    return reply.send({ success: true, token: authResult.token, csrfToken, role: authResult.role });
  });

  // Admin Logout
  adminApp.post('/admin/auth/logout', async (request: FastifyRequest, reply: FastifyReply) => {
    const token = extractAdminToken(request);
    if (token) {
      opts.adminAuth.invalidateSession(token);
    }
    reply.clearCookie('zyphon_session', { path: '/' });
    reply.clearCookie('zyphon_csrf', { path: '/' });
    return reply.send({ success: true });
  });

  // Admin Me
  adminApp.get('/admin/auth/me', async (request: FastifyRequest, reply: FastifyReply) => {
    const token = extractAdminToken(request);
    const session = token ? opts.adminAuth.validateSession(token) : null;
    if (!session) {
      return reply.status(401).send({ authenticated: false });
    }
    return reply.send({ authenticated: true, role: session.role });
  });

  // Admin Auth Middleware Hook for protected routes
  adminApp.addHook('onRequest', async (request: FastifyRequest, reply: FastifyReply) => {
    const path = request.url.split('?')[0];
    if (
      path === '/admin/health' ||
      path === '/admin/openapi.json' ||
      path === '/metrics' ||
      path === '/admin/auth/login' ||
      path === '/admin/auth/logout' ||
      path === '/admin/auth/me'
    ) {
      return; // Skip public admin endpoints
    }

    const token = extractAdminToken(request);
    const session = token ? opts.adminAuth.validateSession(token) : null;
    if (!session) {
      return reply.status(401).send({
        error: { code: 'UNAUTHORIZED', message: 'Admin session required' },
      });
    }

    (request as FastifyRequest & { adminSession?: typeof session }).adminSession = session;

    const method = request.method.toUpperCase();
    const isMutating = method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'DELETE';
    const csrfExempt = path === '/admin/auth/login';

    if (isMutating && !csrfExempt) {
      if (session.role === 'viewer') {
        return reply.status(403).send({
          error: { code: 'FORBIDDEN', message: 'Viewer role is read-only' },
        });
      }
    }
  });

  // Admin: Applications CRUD
  adminApp.get('/admin/applications', async () => {
    const apps = opts.appRegistry.getAllApplications();
    return apps.map((app) => ({
      ...app,
      keys: opts.appRegistry.getAppKeySummaries(app.id),
      usage: (opts.appRegistry as any).quotaManager?.getUsage(app.id),
    }));
  });

  adminApp.post('/admin/applications', async (request: FastifyRequest, reply: FastifyReply) => {
    const body = request.body as Partial<ApplicationConfig> & { id: string; name: string };
    if (!body.id || !body.name) {
      return reply.status(400).send({ error: { code: 'VALIDATION_FAILED', message: 'App id and name required' } });
    }

    const newApp: ApplicationConfig = {
      id: body.id,
      name: body.name,
      enabled: body.enabled !== false,
      source: body.source || 'manual',
      channels: body.channels || ['email'],
      quota: body.quota || { per_min: 120, per_day: 10000 },
      bindings: body.bindings || {},
      created_at: new Date().toISOString(),
    };

    const currentApps = opts.appRegistry.getAllApplications().filter((a) => a.id !== newApp.id);
    const nextApps = [...currentApps, newApp];
    opts.appRegistry.setApplications(nextApps);
    try {
      opts.configLoader.atomicSaveConfig('applications.yaml', { applications: nextApps });
    } catch {}

    opts.eventBus.publish('audit_event', {
      action: 'APPLICATION_CREATED',
      app_id: newApp.id,
    });

    return reply.status(201).send(newApp);
  });

  adminApp.put('/admin/applications/:id', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
    const id = request.params.id;
    const body = request.body as Partial<ApplicationConfig>;
    const app = opts.appRegistry.getApplication(id);
    if (!app) {
      return reply.status(404).send({ error: { code: 'NOT_FOUND', message: `App '${id}' not found` } });
    }

    const updated: ApplicationConfig = {
      ...app,
      ...body,
      id, // Preserve ID
      updated_at: new Date().toISOString(),
    };

    const currentApps = opts.appRegistry.getAllApplications().map((a) => (a.id === id ? updated : a));
    opts.appRegistry.setApplications(currentApps);
    try {
      opts.configLoader.atomicSaveConfig('applications.yaml', { applications: currentApps });
    } catch {}

    opts.eventBus.publish('audit_event', {
      action: 'APPLICATION_UPDATED',
      app_id: id,
    });

    return reply.send(updated);
  });

  adminApp.delete('/admin/applications/:id', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
    const id = request.params.id;
    const currentApps = opts.appRegistry.getAllApplications().filter((a) => a.id !== id);
    opts.appRegistry.setApplications(currentApps);
    try {
      opts.configLoader.atomicSaveConfig('applications.yaml', { applications: currentApps });
    } catch {}

    opts.eventBus.publish('audit_event', {
      action: 'APPLICATION_DELETED',
      app_id: id,
    });

    return reply.send({ success: true });
  });

  adminApp.patch('/admin/applications/:id', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
    const id = request.params.id;
    const body = request.body as Partial<ApplicationConfig>;
    const app = opts.appRegistry.getApplication(id);
    if (!app) {
      return reply.status(404).send({ error: { code: 'NOT_FOUND', message: `App '${id}' not found` } });
    }

    const dedupedBindings = body.bindings
      ? Object.fromEntries(
          Object.entries(body.bindings).map(([ch, list]) => [
            ch,
            Array.from(new Set((list || []).filter(Boolean))),
          ]),
        )
      : app.bindings;

    const updated: ApplicationConfig = {
      ...app,
      ...body,
      ...(dedupedBindings ? { bindings: dedupedBindings } : {}),
      id,
      updated_at: new Date().toISOString(),
    };

    const currentApps = opts.appRegistry.getAllApplications().map((a) => (a.id === id ? updated : a));
    opts.appRegistry.setApplications(currentApps);
    try {
      opts.configLoader.atomicSaveConfig('applications.yaml', { applications: currentApps });
    } catch {}

    opts.eventBus.publish('audit_event', {
      action: 'APPLICATION_UPDATED',
      app_id: id,
    });

    return reply.send(updated);
  });

  adminApp.post('/admin/applications/:id/keys', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
    const id = request.params.id;
    const app = opts.appRegistry.getApplication(id);
    if (!app) {
      return reply.status(404).send({ error: { code: 'NOT_FOUND', message: `App '${id}' not found` } });
    }

    const generated = opts.appRegistry.generateKey(id);
    opts.eventBus.publish('audit_event', {
      action: 'API_KEY_GENERATED',
      app_id: id,
      key_prefix: generated.key_prefix,
    });

    return reply.status(201).send(generated);
  });

  adminApp.post('/admin/applications/:id/rotate-key', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
    const id = request.params.id;
    const app = opts.appRegistry.getApplication(id);
    if (!app) {
      return reply.status(404).send({ error: { code: 'NOT_FOUND', message: `App '${id}' not found` } });
    }

    const generated = opts.appRegistry.generateKey(id);
    opts.eventBus.publish('audit_event', {
      action: 'API_KEY_ROTATED',
      app_id: id,
      key_prefix: generated.key_prefix,
    });

    return reply.send({
      app_id: id,
      api_key: generated.api_key,
      key_prefix: generated.key_prefix,
    });
  });

  // Admin: Providers
  adminApp.get('/admin/providers', async () => {
    const adapters = opts.providerRegistry.getAllAdapters();
    const states = opts.circuitBreaker.getAllStates();
    const routing = opts.routingEngine.getConfig();
    const allApps = opts.appRegistry.getAllApplications();
    let provConfigs: any[] = [];
    try {
      provConfigs = opts.configLoader.loadProvidersFile() || [];
    } catch {
      try {
        provConfigs = opts.configLoader.loadClusterConfig().providers || [];
      } catch {}
    }

    // Compute real per-provider sent/failed message counts from daily logs (never health check probes)
    const sentByProvider: Record<string, number> = {};
    const failedByProvider: Record<string, number> = {};
    try {
      const recent = await opts.logReader.query({ limit: 500 });
      for (const r of recent.records as any[]) {
        if ('ev' in r || 'event' in r) continue;
        const provId = r.provider;
        if (!provId) continue;
        if (r.status === 'delivered' || r.status === 'sent') {
          sentByProvider[provId] = (sentByProvider[provId] || 0) + 1;
        } else if (r.status === 'failed' || r.status === 'blocked') {
          failedByProvider[provId] = (failedByProvider[provId] || 0) + 1;
        }
      }
    } catch {}

    const mapped = adapters.map((a) => {
      const regDef = opts.providerRegistry.getDefinition(a.id);
      const pConf = provConfigs.find((p) => p.id === a.id) || regDef;
      const cb = states[a.id];
      const consecutiveFailures = cb?.consecutive_failures || 0;
      const openUntil = cb?.open_until || 0;
      const circuitState = cb?.circuit_state || 'CLOSED';

      let status: 'Connected' | 'Paused briefly' | 'Needs attention' = 'Connected';
      if (circuitState === 'OPEN' && openUntil && Date.now() < openUntil) {
        status = 'Paused briefly';
      } else if (consecutiveFailures > 0) {
        status = 'Needs attention';
      }

      const lastError =
        typeof cb?.health?.message === 'string' && cb.health.message.trim()
          ? cb.health.message.trim()
          : undefined;

      // Strictly sanitize: NEVER return any secret/password/token/key fields in config!
      const rawCfg = (pConf?.config || {}) as Record<string, any>;
      const sanitizedCfg: Record<string, any> = {};
      for (const [k, v] of Object.entries(rawCfg)) {
        if (!/secret|password|token|key/i.test(k)) {
          sanitizedCfg[k] = v;
        }
      }

      const defaultType = a.id.includes('smtp')
        ? 'smtp'
        : a.id.includes('sendgrid')
        ? 'sendgrid'
        : a.id.includes('ses')
        ? 'ses'
        : a.id.includes('zoho')
        ? 'zoho'
        : a.id.includes('meta')
        ? 'meta'
        : a.id.includes('wa')
        ? 'twilio-wa'
        : a.channel === 'sms'
        ? 'twilio'
        : 'mock';

      const channelChain = routing.default?.[a.channel] || [];
      const orderIndex = channelChain.indexOf(a.id);

      const usedBy = allApps
        .filter((app) => (app.bindings?.[a.channel] || []).includes(a.id))
        .map((app) => app.name);

      const vaultName = opts.vault.getMeta(a.id)?.name;
      const displayName =
        (typeof (pConf as any)?.name === 'string' && (pConf as any).name.trim()) ||
        (typeof vaultName === 'string' && vaultName.trim()) ||
        (pConf as any)?.id ||
        a.id;

      return {
        id: a.id,
        channel: a.channel,
        type: pConf?.adapter || defaultType,
        name: displayName,
        config: sanitizedCfg,
        secret_set: opts.vault.hasSecret(a.id),
        status,
        sent: sentByProvider[a.id] || 0,
        failed: failedByProvider[a.id] || 0,
        cf: consecutiveFailures,
        until: openUntil,
        circuit: cb || { circuit_state: 'CLOSED' },
        used_by: usedBy,
        ...(lastError ? { last_error: lastError } : {}),
        _orderIndex: orderIndex === -1 ? 999 : orderIndex,
      };
    });

    mapped.sort((a, b) => a._orderIndex - b._orderIndex);
    return mapped.map(({ _orderIndex: _ignored, ...rest }) => rest);
  });

  adminApp.post('/admin/providers', async (request: FastifyRequest, reply: FastifyReply) => {
    const body = request.body as {
      id?: string;
      channel: Channel;
      type: string;
      name: string;
      config?: Record<string, any>;
      secrets?: Record<string, any>;
    };

    if (!body || !body.channel || !body.type || !body.name) {
      return reply.status(400).send({
        error: { code: 'VALIDATION_FAILED', message: 'Channel, type, and name are required' },
      });
    }

    const id = body.id || `${body.type}-${Date.now().toString(36)}`;

    // 1. Vault: Store secrets
    if (body.secrets && Object.keys(body.secrets).length > 0) {
      opts.vault.setSecret(id, body.secrets, {
        channel: body.channel,
        type: body.type,
        name: body.name,
      });
    }

    // 2. Providers YAML: Store non-secret config (do not require full cluster load)
    let clusterProviders: any[] = [];
    try {
      clusterProviders = opts.configLoader.loadProvidersFile() || [];
    } catch (err: any) {
      return reply.status(500).send({
        error: {
          code: 'CONFIG_READ_FAILED',
          message: err?.message || 'Failed to read providers.yaml',
        },
      });
    }

    const newProvDef = {
      id,
      channel: body.channel,
      adapter: body.type === 'meta' ? 'meta-cloud' : body.type,
      enabled: true,
      name: body.name,
      config: body.config || {},
    };

    const updatedProviders = [...clusterProviders.filter((p: any) => p.id !== id), newProvDef];
    try {
      opts.configLoader.atomicSaveConfig('providers.yaml', { providers: updatedProviders });
    } catch (err: any) {
      return reply.status(500).send({
        error: {
          code: 'CONFIG_WRITE_FAILED',
          message: err?.message || 'Failed to save providers.yaml',
        },
      });
    }

    // 3. Register adapter in ProviderRegistry
    const adapter = createAdapter(newProvDef);
    opts.providerRegistry.registerAdapter(adapter, newProvDef as any);

    // 4. Append to default routing chain for this channel
    const routing = opts.routingEngine.getConfig();
    const currentChain = routing.default[body.channel] ? [...routing.default[body.channel]] : [];
    if (!currentChain.includes(id)) {
      currentChain.push(id);
      const updatedRouting = {
        ...routing,
        default: {
          ...routing.default,
          [body.channel]: currentChain,
        },
      };
      try {
        opts.configLoader.atomicSaveConfig('routing.yaml', { routing: updatedRouting });
      } catch {}
      opts.routingEngine.updateConfig(updatedRouting);
    }

    opts.eventBus.publish('audit_event', { action: 'PROVIDER_CREATED', provider_id: id });

    return reply.status(201).send({
      id,
      channel: body.channel,
      type: body.type,
      name: body.name,
      config: body.config || {},
      secret_set: opts.vault.hasSecret(id),
      status: 'Connected',
      sent: 0,
      failed: 0,
      cf: 0,
    });
  });

  adminApp.patch('/admin/providers/:id', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
    const id = request.params.id;
    const body = request.body as {
      name?: string;
      config?: Record<string, any>;
      secrets?: Record<string, any>;
      enabled?: boolean;
    };

    let clusterProviders: any[] = [];
    try {
      clusterProviders = opts.configLoader.loadProvidersFile() || [];
    } catch (err: any) {
      return reply.status(500).send({
        error: {
          code: 'CONFIG_READ_FAILED',
          message: err?.message || 'Failed to read providers.yaml',
        },
      });
    }

    const existingIndex = clusterProviders.findIndex((p: any) => p.id === id);
    const existing = existingIndex !== -1 ? clusterProviders[existingIndex] : null;
    if (!existing) {
      return reply.status(404).send({
        error: { code: 'NOT_FOUND', message: `Provider '${id}' not found` },
      });
    }

    const nextName =
      typeof body.name === 'string' && body.name.trim() ? body.name.trim() : existing.name || id;

    // 1. Vault: Update secrets if provided (ignoring empty strings to preserve existing)
    if (body.secrets && Object.keys(body.secrets).length > 0) {
      const currentSecrets = opts.vault.getSecret(id) || {};
      const mergedSecrets = { ...currentSecrets };
      for (const [k, v] of Object.entries(body.secrets)) {
        if (v !== undefined && v !== '') {
          mergedSecrets[k] = v;
        }
      }
      // Strip vault meta keys that were merged into decrypted credentials
      const secretOnly: Record<string, string | number | boolean> = {};
      for (const [k, v] of Object.entries(mergedSecrets)) {
        if (!['channel', 'type', 'name', 'provider_id'].includes(k)) {
          secretOnly[k] = v as string | number | boolean;
        }
      }
      opts.vault.setSecret(id, secretOnly, {
        channel: existing.channel || 'email',
        type: existing.adapter || 'smtp',
        name: nextName,
      });
    } else if (opts.vault.hasSecret(id) && nextName !== (existing.name || id)) {
      // Name-only edit: keep encrypted secrets, refresh display name in vault meta
      opts.vault.updateMeta(id, { name: nextName });
    }

    // 2. Update config in YAML
    const updated = {
      ...existing,
      name: nextName,
      enabled: body.enabled !== undefined ? body.enabled : existing.enabled,
      config: { ...(existing.config || {}), ...(body.config || {}) },
    };
    clusterProviders[existingIndex] = updated;
    try {
      opts.configLoader.atomicSaveConfig('providers.yaml', { providers: clusterProviders });
    } catch (err: any) {
      return reply.status(500).send({
        error: {
          code: 'CONFIG_WRITE_FAILED',
          message: err?.message || 'Failed to save providers.yaml',
        },
      });
    }

    // Re-register adapter so in-memory definition matches YAML
    const adapter = createAdapter(updated);
    opts.providerRegistry.registerAdapter(adapter, updated as any);

    opts.eventBus.publish('audit_event', { action: 'PROVIDER_UPDATED', provider_id: id });

    const rawCfg = (updated.config || {}) as Record<string, unknown>;
    const sanitizedCfg: Record<string, unknown> = {};
    for (const [k, v] of Object.entries(rawCfg)) {
      if (!/secret|password|token|key/i.test(k)) sanitizedCfg[k] = v;
    }

    return reply.send({
      id,
      name: nextName,
      config: sanitizedCfg,
      secret_set: opts.vault.hasSecret(id),
      status: 'Connected',
    });
  });

  adminApp.delete('/admin/providers/:id', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
    const id = request.params.id;

    // Capture display name before teardown so Insights can still label historical traffic.
    let archivedName = id;
    let archivedAdapter: string | undefined;
    let archivedChannel: string | undefined;
    let providersList: any[] = [];
    try {
      providersList = opts.configLoader.loadProvidersFile() || [];
    } catch {
      try {
        providersList = opts.configLoader.loadClusterConfig().providers || [];
      } catch {}
    }
    const existing = providersList.find((p: any) => p.id === id) as any;
    if (existing) {
      archivedName = existing.name || id;
      archivedAdapter = existing.adapter;
      archivedChannel = existing.channel;
    } else {
      const def = opts.providerRegistry.getDefinition(id) as any;
      archivedName = def?.name || id;
      archivedAdapter = def?.adapter;
      archivedChannel = def?.channel;
    }
    if (opts.stateDir) {
      upsertArchivedProvider(opts.stateDir, {
        id,
        name: archivedName,
        adapter: archivedAdapter,
        channel: archivedChannel,
        deleted_at: Date.now(),
      });
    }

    // 1. Vault
    opts.vault.deleteSecret(id);

    // 2. Provider Registry
    opts.providerRegistry.unregisterAdapter(id);

    // 3. Providers YAML
    try {
      const updatedProviders = providersList.filter((p: any) => p.id !== id);
      opts.configLoader.atomicSaveConfig('providers.yaml', { providers: updatedProviders });
    } catch {}

    // 4. Routing YAML
    const routing = opts.routingEngine.getConfig();
    const updatedDefault: Record<string, string[]> = {};
    for (const [ch, chain] of Object.entries(routing.default)) {
      updatedDefault[ch] = (chain || []).filter((provId) => provId !== id);
    }
    const updatedRouting = {
      ...routing,
      default: updatedDefault as any,
    };
    try {
      opts.configLoader.atomicSaveConfig('routing.yaml', { routing: updatedRouting });
    } catch {}
    opts.routingEngine.updateConfig(updatedRouting);

    // 5. App bindings
    const apps = opts.appRegistry.getAllApplications();
    let appsChanged = false;
    const updatedApps = apps.map((app) => {
      if (!app.bindings) return app;
      let modified = false;
      const newBindings: Record<string, string[]> = {};
      for (const [ch, chain] of Object.entries(app.bindings)) {
        if (chain) {
          newBindings[ch] = chain.filter((provId) => provId !== id);
          if (newBindings[ch].length !== chain.length) modified = true;
        }
      }
      if (modified) {
        appsChanged = true;
        return { ...app, bindings: newBindings };
      }
      return app;
    });
    if (appsChanged) {
      opts.appRegistry.setApplications(updatedApps);
      try {
        opts.configLoader.atomicSaveConfig('applications.yaml', { applications: updatedApps });
      } catch {}
    }

    opts.eventBus.publish('audit_event', { action: 'PROVIDER_DELETED', provider_id: id });

    return reply.send({ success: true });
  });

  adminApp.post('/admin/providers/:id/test', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
    const id = request.params.id;
    const health = await opts.providerRegistry.checkHealth(id);
    if (health.status === 'healthy') {
      // Clear sticky "Needs attention" after a successful connectivity check.
      opts.circuitBreaker.recordSuccess(id, health.latency_ms || 0);
    } else {
      opts.circuitBreaker.recordFailure(id, health.latency_ms || 0, health.message);
    }
    return reply.send({
      ok: health.status === 'healthy',
      latency_ms: health.latency_ms,
      error: health.message,
    });
  });

  adminApp.post('/admin/providers/:id/test-send', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
    const id = request.params.id;
    const adapter = opts.providerRegistry.getAdapter(id);
    if (!adapter) {
      return reply.status(404).send({ error: { code: 'NOT_FOUND', message: `Provider '${id}' not found` } });
    }

    const body = (request.body || {}) as {
      to?: string;
      recipient?: string;
      subject?: string;
      body?: string;
      template_id?: string;
      app?: string;
      variables?: Record<string, string>;
    };
    const recipient = body.to || body.recipient || (adapter.channel === 'email' ? 'test@example.com' : '+919876543210');
    const creds = opts.providerRegistry.getResolvedCredentials(id);
    const allApps = opts.appRegistry.getAllApplications();
    const boundApp = allApps.find((a) => (a.bindings?.[adapter.channel] || []).includes(id));
    const defaultApp = boundApp || allApps[0];
    const appLabel = body.app || defaultApp?.name || defaultApp?.id || 'Test dispatch';

    const t0 = Date.now();
    const messageId = 'test_' + Date.now().toString(36);
    const requestId = 'req_' + Date.now().toString(36);

    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), 10000);

    try {
      const dltTemplateId =
        body.template_id ||
        (creds.template_id ? String(creds.template_id) : undefined);
      const msgSubject = body.subject || 'Zyphon Test Message';
      const msgBody = body.body || 'This is a test message dispatched via Zyphon Console.';

      const normalized: NormalizedMessage = {
        message_id: messageId,
        request_id: requestId,
        application_id: appLabel,
        channel: adapter.channel,
        recipient,
        content: {
          type: 'text',
          subject: msgSubject,
          body: msgBody,
          ...(dltTemplateId ? { template: dltTemplateId } : {}),
          ...(body.variables ? { variables: body.variables } : {}),
        },
        metadata: {
          test: true,
          ...(dltTemplateId ? { dlt_template_id: dltTemplateId } : {}),
          ...(creds.sender_id ? { dlt_header: String(creds.sender_id) } : {}),
          ...(body.variables ? { template_variables: body.variables } : {}),
        },
        created_at: Date.now(),
      };

      const result = await adapter.send(normalized, creds, controller.signal);
      const latency = Date.now() - t0;

      const providerDisplayName = (() => {
        try {
          const conf = (opts.configLoader.loadProvidersFile() || []).find(
            (p: any) => p.id === id,
          ) as any;
          return conf?.name || (opts.providerRegistry.getDefinition(id) as any)?.name || id;
        } catch {
          return id;
        }
      })();

      opts.logWriter.write({
        type: 'message',
        message_id: messageId,
        request_id: requestId,
        ts: Date.now(),
        application: appLabel,
        channel: adapter.channel,
        provider: id,
        provider_name: providerDisplayName,
        status: result.success ? 'delivered' : 'failed',
        latency_ms: latency,
        attempts: [
          {
            provider: id,
            attempt_number: 1,
            ok: result.success,
            latency_ms: latency,
            error: result.error?.message || null,
          },
        ],
        provider_response: result.raw_response || null,
        error: result.error ? { code: result.error.code, message: result.error.message } : null,
        recipient_masked: recipient,
        failover: false,
        ...(adapter.channel === 'email' ? { subject: msgSubject } : {}),
        body_preview: msgBody.slice(0, 600),
      });

      opts.metrics.record({
        channel: adapter.channel,
        provider: id,
        status: result.success ? 'delivered' : 'failed',
        latencyMs: latency,
        timestamp: Date.now(),
      });

      if (result.success) {
        opts.circuitBreaker.recordSuccess(id, latency);
      } else {
        opts.circuitBreaker.recordFailure(id, latency, result.error?.message);
      }

      return reply.send({
        ok: result.success,
        message_id: messageId,
        status: result.success ? 'sent' : 'failed',
        error: result.error?.message,
        latency_ms: latency,
        via: id,
      });
    } catch (err: any) {
      const latency = Date.now() - t0;
      return reply.send({
        ok: false,
        message_id: messageId,
        status: 'failed',
        error: err.message || 'Test send failed',
        latency_ms: latency,
        via: id,
      });
    } finally {
      clearTimeout(timer);
    }
  });

  adminApp.put('/admin/channels/:channel/order', async (request: FastifyRequest<{ Params: { channel: Channel } }>, reply: FastifyReply) => {
    const channel = request.params.channel;
    const body = request.body as { order: string[] };
    if (!body || !Array.isArray(body.order)) {
      return reply.status(400).send({ error: { code: 'VALIDATION_FAILED', message: 'order array required' } });
    }

    const routing = opts.routingEngine.getConfig();
    const updatedRouting = {
      ...routing,
      default: {
        ...routing.default,
        [channel]: body.order,
      },
    };

    try {
      opts.configLoader.atomicSaveConfig('routing.yaml', { routing: updatedRouting });
    } catch {}
    opts.routingEngine.updateConfig(updatedRouting);
    opts.eventBus.publish('audit_event', { action: 'ROUTING_ORDER_UPDATED', channel });

    return reply.send({ channel, order: body.order });
  });

  // Admin: Routing
  adminApp.get('/admin/routing', async () => {
    return opts.routingEngine.getConfig();
  });

  adminApp.put('/admin/routing', async (request: FastifyRequest, reply: FastifyReply) => {
    const body = request.body as RoutingConfig;
    opts.routingEngine.updateConfig(body);
    try {
      opts.configLoader.atomicSaveConfig('routing.yaml', { routing: body });
    } catch {}
    opts.eventBus.publish('audit_event', { action: 'ROUTING_UPDATED' });
    return reply.send(opts.routingEngine.getConfig());
  });

  // Admin: Vault Metadata & Secrets
  adminApp.get('/admin/vault', async () => {
    return opts.vault.listMetadata();
  });

  adminApp.put('/admin/vault/:id', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
    const id = request.params.id;
    const body = request.body as {
      secrets: Record<string, string | number | boolean>;
      meta?: Record<string, string | number | boolean>;
    };

    if (!body.secrets) {
      return reply.status(400).send({ error: { code: 'VALIDATION_FAILED', message: 'Secrets object required' } });
    }

    opts.vault.setSecret(id, body.secrets, body.meta || {});
    opts.eventBus.publish('audit_event', { action: 'VAULT_SECRET_UPDATED', provider_id: id });
    return reply.send({ success: true });
  });

  adminApp.delete('/admin/vault/:id', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
    const id = request.params.id;
    const deleted = opts.vault.deleteSecret(id);
    if (deleted) {
      opts.eventBus.publish('audit_event', { action: 'VAULT_SECRET_DELETED', provider_id: id });
    }
    return reply.send({ success: deleted });
  });

  // Admin: Logs
  adminApp.get('/admin/logs', async (request: FastifyRequest) => {
    const query = request.query as any;
    const result = await opts.logReader.query({
      query: query.query,
      status: query.status,
      application: query.app || query.application,
      channel: query.channel,
      provider: query.provider,
      limit: query.limit ? parseInt(query.limit, 10) : 50,
      cursor: query.cursor,
    });

    return {
      records: result.records,
      cursor: result.nextCursor,
      has_more: !!result.nextCursor,
    };
  });

  // Admin: Realtime SSE
  adminApp.get('/admin/events', async (request: FastifyRequest, reply: FastifyReply) => {
    reply.raw.setHeader('Content-Type', 'text/event-stream');
    reply.raw.setHeader('Cache-Control', 'no-cache');
    reply.raw.setHeader('Connection', 'keep-alive');
    reply.raw.setHeader('Access-Control-Allow-Origin', '*');
    reply.raw.flushHeaders();

    const lastEventId = (request.headers['last-event-id'] as string) || (request.query as any)?.lastEventId;
    if (lastEventId) {
      const replay = opts.eventBus.getEventsSince(lastEventId);
      for (const ev of replay) {
        reply.raw.write(`id: ${ev.id}\nevent: ${ev.event}\ndata: ${JSON.stringify(ev.data)}\n\n`);
      }
    }

    const onEvent = (ev: any) => {
      reply.raw.write(`id: ${ev.id}\nevent: ${ev.event}\ndata: ${JSON.stringify(ev.data)}\n\n`);
    };

    opts.eventBus.on('event', onEvent);

    request.raw.on('close', () => {
      opts.eventBus.off('event', onEvent);
    });

    // Return promise that does not resolve until client disconnects
    await new Promise(() => {});
  });

  // Admin: Metrics JSON
  adminApp.get('/admin/metrics', async () => {
    return opts.metrics.getSummary();
  });

  // Admin: Stats (Real Gateway Data only — strictly filtered to TODAY)
  adminApp.get('/admin/stats', async () => {
    const summary = opts.metrics.getSummary();
    const apps = opts.appRegistry.getAllApplications();
    const activeApps = apps.filter((a) => a.enabled).length;

    const todayUtc = new Date().toISOString().slice(0, 10);
    const startOfLocalToday = new Date();
    startOfLocalToday.setHours(0, 0, 0, 0);
    const minTodayTs = startOfLocalToday.getTime();

    let total = summary.total;
    let delivered = summary.delivered;
    let failed = summary.failed;

    try {
      const files = opts.logWriter.listLogFiles();
      const recent = await opts.logReader.query({ limit: 500 });
      const todayMsgRecords = recent.records.filter((r: any) => {
        if ('ev' in r || 'event' in r) return false;
        const ts = Number(r.ts || 0);
        if (!ts) return false;
        const recDateUtc = new Date(ts).toISOString().slice(0, 10);
        return recDateUtc === todayUtc || ts >= minTodayTs;
      });
      if (files.length > 0 || todayMsgRecords.length > 0) {
        total = todayMsgRecords.length;
        delivered = todayMsgRecords.filter((r: any) => r.status === 'delivered' || r.status === 'sent').length;
        failed = todayMsgRecords.filter((r: any) => r.status === 'failed' || r.status === 'blocked').length;
      }
    } catch {
      // fallback to in-memory summary
    }

    const finished = delivered + failed;
    const successRate = finished > 0 ? Math.round((delivered / finished) * 100) : null;

    return {
      sent_today: total,
      success_rate: successRate,
      failed_today: failed,
      active_apps: activeApps,
    };
  });

  // Admin: Messages (Search & Filter from daily logs + ring buffer)
  adminApp.get('/admin/messages', async (request: FastifyRequest) => {
    const query = request.query as any;
    const limit = query.limit ? parseInt(query.limit, 10) : 50;
    const result = await opts.logReader.query({
      query: query.q || query.query,
      status: query.status,
      channel: query.channel,
      application: query.app,
      limit,
      cursor: query.cursor,
    });

    const appMap = new Map<string, string>();
    for (const a of opts.appRegistry.getAllApplications()) {
      appMap.set(a.id, a.name);
    }

    const messages = result.records
      .filter((r) => !('ev' in r) && !('event' in r))
      .map((r: any) => {
        const att = (r.attempts || []).map((a: any) => ({
          p: a.provider || 'unknown',
          ok: a.ok ?? (a.status === 'success' || a.success === true),
          e: a.error?.message || a.error || '',
        }));
        const rawApp = r.application || 'Direct';
        return {
          id: r.message_id || r.msgId || 'm_' + Math.random().toString(36).slice(2, 8),
          request_id: r.request_id || undefined,
          ts: r.ts || Date.now(),
          app: appMap.get(rawApp) || rawApp,
          channel: r.channel || 'email',
          to: r.recipient_masked || r.recipient || '—',
          subject: r.subject || (r.channel === 'email' ? 'Transactional Notification' : undefined),
          body: r.body_preview || r.body_stored || undefined,
          status: r.status || 'sent',
          via: r.provider || null,
          att,
          attachments: Array.isArray(r.attachments) ? r.attachments : [],
          ms: r.latency_ms || 0,
          note: r.error?.message || '',
        };
      });

    return messages;
  });

  // Admin: Settings
  adminApp.get('/admin/settings', async () => {
    const routing = opts.routingEngine.getConfig();
    return {
      retry: {
        attempts: routing.retry?.max_attempts ?? 2,
        wait: Math.round((routing.retry?.backoff_ms ?? 1000) / 1000),
      },
      circuit_breaker: {
        trip: routing.circuit_breaker?.failure_threshold ?? 3,
        pause: routing.circuit_breaker?.recovery_time_seconds ?? 20,
      },
      retention_days: (opts.logWriter as any).retentionDays ?? 7,
    };
  });

  adminApp.put('/admin/settings', async (request: FastifyRequest, reply: FastifyReply) => {
    const body = request.body as {
      retry?: { attempts?: number; wait?: number };
      circuit_breaker?: { trip?: number; pause?: number };
      retention_days?: number;
    };

    const currentRouting = opts.routingEngine.getConfig();
    const updatedRouting = {
      ...currentRouting,
      retry: {
        max_attempts: body.retry?.attempts ?? currentRouting.retry?.max_attempts ?? 2,
        backoff_ms: (body.retry?.wait ?? 1) * 1000,
      },
      circuit_breaker: {
        failure_threshold: body.circuit_breaker?.trip ?? currentRouting.circuit_breaker?.failure_threshold ?? 3,
        recovery_time_seconds: body.circuit_breaker?.pause ?? currentRouting.circuit_breaker?.recovery_time_seconds ?? 20,
      },
    };

    try {
      opts.configLoader.atomicSaveConfig('routing.yaml', { routing: updatedRouting });
    } catch {}
    opts.routingEngine.updateConfig(updatedRouting);

    if (body.retention_days) {
      if (typeof opts.logWriter.setRetentionDays === 'function') {
        opts.logWriter.setRetentionDays(body.retention_days);
      } else {
        (opts.logWriter as any).retentionDays = body.retention_days;
      }
    }

    opts.eventBus.publish('audit_event', { action: 'SETTINGS_UPDATED' });
    return reply.send({ success: true });
  });

  adminApp.get('/admin/catalog', async (request: FastifyRequest) => {
    const q = (request.query || {}) as { market?: string };
    const market =
      q.market === 'india' || q.market === 'global' || q.market === 'all'
        ? q.market
        : 'all';
    return buildAdminCatalogResponse(market);
  });

  adminApp.put('/admin/channels/:channel/strategy', async (request: FastifyRequest<{ Params: { channel: Channel } }>, reply: FastifyReply) => {
    const channel = request.params.channel;
    const body = request.body as { strategy?: string };
    const parsed = ChannelStrategyEnum.safeParse(body?.strategy);
    if (!parsed.success) {
      return reply.status(400).send({ error: { code: 'VALIDATION_FAILED', message: 'strategy must be failover or round-robin' } });
    }

    const routing = opts.routingEngine.getConfig();
    const updatedRouting = {
      ...routing,
      strategies: {
        email: routing.strategies?.email || 'failover',
        sms: routing.strategies?.sms || 'failover',
        whatsapp: routing.strategies?.whatsapp || 'failover',
        [channel]: parsed.data,
      },
    };
    try {
      opts.configLoader.atomicSaveConfig('routing.yaml', { routing: updatedRouting });
    } catch {}
    opts.routingEngine.updateConfig(updatedRouting);
    return reply.send({ channel, strategy: parsed.data });
  });

  adminApp.get('/admin/templates', async () => {
    try {
      return { templates: opts.configLoader.loadTemplates() };
    } catch {
      return { templates: [] };
    }
  });

  adminApp.post('/admin/templates', async (request: FastifyRequest, reply: FastifyReply) => {
    const body = request.body as Record<string, unknown>;
    let templates: ReturnType<ConfigLoader['loadTemplates']> = [];
    try {
      templates = opts.configLoader.loadTemplates();
    } catch {
      templates = [];
    }
    const candidate = { ...body, id: body.id || `tpl_${Date.now().toString(36)}` };
    const parsed = MessageTemplateSchema.safeParse(candidate);
    if (!parsed.success) {
      return reply.status(400).send({
        error: { code: 'VALIDATION_FAILED', message: parsed.error.errors.map((e) => e.message).join('; ') },
      });
    }
    const updated = [...templates.filter((t) => t.id !== parsed.data.id), parsed.data];
    opts.configLoader.atomicSaveConfig('templates.yaml', { templates: updated });
    return reply.status(201).send(parsed.data);
  });

  adminApp.put('/admin/templates/:id', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
    const id = request.params.id;
    const body = request.body as Record<string, unknown>;
    const templates = opts.configLoader.loadTemplates();
    const existing = templates.find((t) => t.id === id);
    if (!existing) {
      return reply.status(404).send({ error: { code: 'NOT_FOUND', message: `Template '${id}' not found` } });
    }
    const parsed = MessageTemplateSchema.safeParse({ ...existing, ...body, id });
    if (!parsed.success) {
      return reply.status(400).send({
        error: { code: 'VALIDATION_FAILED', message: parsed.error.errors.map((e) => e.message).join('; ') },
      });
    }
    const updated = templates.map((t) => (t.id === id ? parsed.data : t));
    opts.configLoader.atomicSaveConfig('templates.yaml', { templates: updated });
    return reply.send(parsed.data);
  });

  adminApp.delete('/admin/templates/:id', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
    const id = request.params.id;
    const templates = opts.configLoader.loadTemplates();
    const updated = templates.filter((t) => t.id !== id);
    if (updated.length === templates.length) {
      return reply.status(404).send({ error: { code: 'NOT_FOUND', message: `Template '${id}' not found` } });
    }
    opts.configLoader.atomicSaveConfig('templates.yaml', { templates: updated });
    return reply.send({ success: true });
  });

  adminApp.get('/admin/dlt', async (_request, reply) => {
    try {
      return { dlt: opts.configLoader.loadDltConfig() };
    } catch (err: any) {
      return reply.status(400).send({ error: { code: 'VALIDATION_FAILED', message: err.message } });
    }
  });

  adminApp.put('/admin/dlt', async (request: FastifyRequest, reply: FastifyReply) => {
    const body = request.body as { dlt?: unknown };
    const parsed = DltFileSchema.safeParse(body?.dlt ? body : { dlt: body });
    if (!parsed.success) {
      return reply.status(400).send({
        error: { code: 'VALIDATION_FAILED', message: parsed.error.errors.map((e) => e.message).join('; ') },
      });
    }
    opts.configLoader.atomicSaveConfig('dlt.yaml', parsed.data);
    return reply.send(parsed.data);
  });

  adminApp.get('/admin/suppressions', async () => {
    try {
      return { suppressions: opts.configLoader.loadSuppressions() };
    } catch {
      return { suppressions: { sms: [], email: [], whatsapp: [] } };
    }
  });

  adminApp.put('/admin/suppressions', async (request: FastifyRequest, reply: FastifyReply) => {
    const parsed = SuppressionsFileSchema.safeParse(request.body);
    if (!parsed.success) {
      return reply.status(400).send({
        error: { code: 'VALIDATION_FAILED', message: parsed.error.errors.map((e) => e.message).join('; ') },
      });
    }
    opts.configLoader.atomicSaveConfig('suppressions.yaml', parsed.data);
    return reply.send(parsed.data);
  });

  adminApp.get('/admin/alerts', async () => {
    try {
      return { alerts: opts.configLoader.loadAlerts() };
    } catch {
      return { alerts: {} };
    }
  });

  adminApp.put('/admin/alerts', async (request: FastifyRequest, reply: FastifyReply) => {
    const parsed = AlertsFileSchema.safeParse(request.body);
    if (!parsed.success) {
      return reply.status(400).send({
        error: { code: 'VALIDATION_FAILED', message: parsed.error.errors.map((e) => e.message).join('; ') },
      });
    }
    opts.configLoader.atomicSaveConfig('alerts.yaml', parsed.data);
    return reply.send(parsed.data);
  });

  adminApp.get('/admin/team', async () => {
    try {
      return { team: opts.configLoader.loadTeam() };
    } catch {
      return { team: [] };
    }
  });

  adminApp.get('/admin/insights', async () => {
    const summary = opts.metrics.getSummary();
    let logStats = { messages_scanned: 0, days_available: 0 };
    const byChannelFromLogs: Record<string, { total: number; delivered: number; failed: number }> = {};
    const byAppFromLogs: Record<string, { total: number; delivered: number; failed: number; avg_ms: number }> = {};
    const byProviderFromLogs: Record<
      string,
      {
        name: string;
        deleted?: boolean;
        total: number;
        delivered: number;
        failed: number;
        avg_ms: number;
      }
    > = {};
    let logTotal = 0;
    let logDelivered = 0;
    let logFailed = 0;

    try {
      const files = opts.logWriter.listLogFiles();
      logStats.days_available = files.length;
      const recent = await opts.logReader.query({ limit: 500 });
      logStats.messages_scanned = recent.records.length;

      const appMap = new Map<string, string>();
      for (const a of opts.appRegistry.getAllApplications()) {
        appMap.set(a.id, a.name);
      }

      const activeProviderIds = new Set<string>();
      const providerNameMap = new Map<string, string>();
      try {
        const clusterProviders = opts.configLoader.loadProvidersFile() || [];
        for (const p of clusterProviders as any[]) {
          if (p?.id) {
            activeProviderIds.add(p.id);
            providerNameMap.set(p.id, p.name || p.id);
          }
        }
      } catch {}
      for (const a of opts.providerRegistry.getAllAdapters()) {
        activeProviderIds.add(a.id);
        const def = opts.providerRegistry.getDefinition(a.id) as any;
        if (!providerNameMap.has(a.id)) {
          providerNameMap.set(a.id, def?.name || a.id);
        }
      }

      if (opts.stateDir) {
        for (const archived of loadProviderArchive(opts.stateDir)) {
          if (!providerNameMap.has(archived.id)) {
            providerNameMap.set(archived.id, archived.name || archived.id);
          }
        }
      }

      for (const r of recent.records as any[]) {
        if ('ev' in r || 'event' in r) continue;
        logTotal++;
        const isOk = r.status === 'delivered' || r.status === 'sent';
        if (isOk) logDelivered++;
        else logFailed++;

        const ch = r.channel || 'email';
        if (!byChannelFromLogs[ch]) byChannelFromLogs[ch] = { total: 0, delivered: 0, failed: 0 };
        byChannelFromLogs[ch]!.total++;
        if (isOk) byChannelFromLogs[ch]!.delivered++;
        else byChannelFromLogs[ch]!.failed++;

        const appName = appMap.get(r.application) || r.application || 'Direct';
        if (!byAppFromLogs[appName]) byAppFromLogs[appName] = { total: 0, delivered: 0, failed: 0, avg_ms: 0 };
        const aEntry = byAppFromLogs[appName]!;
        aEntry.avg_ms = Math.round((aEntry.avg_ms * aEntry.total + (r.latency_ms || 0)) / (aEntry.total + 1));
        aEntry.total++;
        if (isOk) aEntry.delivered++;
        else aEntry.failed++;

        const prov = r.provider || 'unassigned';
        if (r.provider_name && !providerNameMap.has(prov)) {
          providerNameMap.set(prov, String(r.provider_name));
        }
        const provDisplayName =
          providerNameMap.get(prov) ||
          (typeof r.provider_name === 'string' && r.provider_name.trim()
            ? String(r.provider_name).trim()
            : null) ||
          guessProviderNameFromId(prov) ||
          prov;
        const deleted = prov !== 'unassigned' && !activeProviderIds.has(prov);
        if (!byProviderFromLogs[prov]) {
          byProviderFromLogs[prov] = {
            name: provDisplayName,
            ...(deleted ? { deleted: true } : {}),
            total: 0,
            delivered: 0,
            failed: 0,
            avg_ms: 0,
          };
        } else if (
          byProviderFromLogs[prov]!.name === prov &&
          provDisplayName !== prov
        ) {
          byProviderFromLogs[prov]!.name = provDisplayName;
        }
        const pEntry = byProviderFromLogs[prov]!;
        pEntry.avg_ms = Math.round((pEntry.avg_ms * pEntry.total + (r.latency_ms || 0)) / (pEntry.total + 1));
        pEntry.total++;
        if (isOk) pEntry.delivered++;
        else pEntry.failed++;
      }
    } catch {
      // empty structure when logs unavailable
    }

    const useLogs = logTotal > summary.total;
    const totals = useLogs
      ? { ...summary, total: logTotal, delivered: logDelivered, failed: logFailed }
      : summary;
    const byChannel =
      useLogs && Object.keys(byChannelFromLogs).length > 0
        ? byChannelFromLogs
        : summary.byChannel || {};

    return {
      period: 'retained_logs',
      totals,
      by_channel: byChannel,
      by_app: byAppFromLogs,
      by_provider: byProviderFromLogs,
      log_stats: logStats,
      generated_at: Date.now(),
    };
  });

  adminApp.get('/admin/messages/:id', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
    const messageId = request.params.id;
    const res = await opts.logReader.query({ query: messageId, limit: 20 });
    const record = res.records.find((r: any) => r.message_id === messageId || r.msgId === messageId) || res.records[0];
    if (!record) {
      return reply.status(404).send({ error: { code: 'NOT_FOUND', message: `Message '${messageId}' not found` } });
    }
    return reply.send({
      message: record,
      attempts: (record as any).attempts || [],
    });
  });

  adminApp.get('/admin/logs/files', async () => {
    const appMap = new Map<string, string>();
    for (const a of opts.appRegistry.getAllApplications()) {
      appMap.set(a.id, a.name);
    }

    const rawFiles = opts.logWriter.listLogFiles();
    const files = rawFiles.map((f) => {
      const byApp: Record<string, number> = {};
      let totalRecords = 0;
      try {
        const content = opts.logReader.readLogFile(f.filename);
        if (content?.records) {
          totalRecords = content.records.length;
          for (const r of content.records as any[]) {
            const rawApp = r.application || 'System';
            const appName = appMap.get(rawApp) || rawApp;
            byApp[appName] = (byApp[appName] || 0) + 1;
          }
        }
      } catch {}
      return {
        ...f,
        total_records: totalRecords,
        by_app: byApp,
      };
    });
    return { files };
  });

  adminApp.get('/admin/logs/files/:filename', async (request: FastifyRequest<{ Params: { filename: string } }>, reply: FastifyReply) => {
    const data = opts.logReader.readLogFile(request.params.filename);
    if (!data) {
      return reply.status(404).send({
        error: { code: 'NOT_FOUND', message: `Log file '${request.params.filename}' not found` },
      });
    }

    const query = (request.query || {}) as { app?: string; application?: string };
    const appFilter = (query.app || query.application || '').trim();

    const appMap = new Map<string, string>();
    for (const a of opts.appRegistry.getAllApplications()) {
      appMap.set(a.id, a.name);
    }

    const enrichedRecords = data.records.map((r: any) => {
      if ('application' in r && r.application) {
        return {
          ...r,
          application_id: r.application,
          application: appMap.get(r.application) || r.application,
        };
      }
      return r;
    });

    if (!appFilter) {
      return reply.send({
        filename: data.filename,
        raw: data.raw,
        records: enrichedRecords,
      });
    }

    const filteredRecords = enrichedRecords.filter((r: any) => {
      const recApp = r.application || 'System';
      return recApp === appFilter || r.application_id === appFilter;
    });
    const filteredRaw = filteredRecords.map((r) => JSON.stringify(r)).join('\n');

    return reply.send({
      filename: data.filename,
      raw: filteredRaw,
      records: filteredRecords,
    });
  });

  adminApp.get('/admin/logs/search', async (request: FastifyRequest) => {
    const query = request.query as Record<string, string | undefined>;
    const appFilter = query.app || query.application;
    const result = await opts.logReader.query({
      query: query.q || query.query,
      status: query.status,
      channel: query.channel,
      provider: query.provider,
      limit: query.limit ? parseInt(query.limit, 10) : 150,
      cursor: query.cursor,
    });

    const appMap = new Map<string, string>();
    for (const a of opts.appRegistry.getAllApplications()) {
      appMap.set(a.id, a.name);
    }

    const enriched = result.records
      .map((r: any) => {
        if ('application' in r && r.application) {
          return {
            ...r,
            application_id: r.application,
            application: appMap.get(r.application) || r.application,
          };
        }
        return r;
      })
      .filter((r: any) => {
        if (!appFilter) return true;
        if (!('application' in r)) return false;
        return r.application === appFilter || r.application_id === appFilter;
      });

    return {
      records: enriched,
      cursor: result.nextCursor,
      has_more: !!result.nextCursor,
    };
  });

  return { publicApp, adminApp };
}

function extractClientIp(request: FastifyRequest): string {
  const forwarded = request.headers['x-forwarded-for'];
  if (typeof forwarded === 'string' && forwarded.length > 0) {
    return forwarded.split(',')[0]!.trim();
  }
  if (Array.isArray(forwarded) && forwarded[0]) {
    return forwarded[0].split(',')[0]!.trim();
  }
  return request.ip;
}

function safeEqualStrings(a: string, b: string): boolean {
  const bufA = Buffer.from(a, 'utf8');
  const bufB = Buffer.from(b, 'utf8');
  if (bufA.length !== bufB.length) {
    return false;
  }
  return crypto.timingSafeEqual(bufA, bufB);
}

function extractAdminToken(request: FastifyRequest): string | null {
  const authHeader = request.headers.authorization;
  if (authHeader && authHeader.startsWith('Bearer ')) {
    return authHeader.slice(7);
  }
  const cookies = (request as any).cookies;
  if (cookies && cookies.zyphon_session) {
    return cookies.zyphon_session;
  }
  return null;
}
