import { exec } from 'node:child_process';
import fs from 'node:fs';
import https from 'node:https';
import net from 'node:net';
import { promisify } from 'node:util';
import { Hono } from 'hono';
import { logger } from '../../lib/logger';

const execAsync = promisify(exec);

/**
 * Internal endpoint to get status of infrastructure services.
 * Checks health endpoints of known services running in Docker.
 *
 * Usage:
 *   GET  /api/_internal/services/status              - Get all service statuses
 *   POST /api/_internal/services/restart             - Restart all services
 *   POST /api/_internal/services/restart/:container  - Restart specific service
 *
 * Returns array of service statuses with health information.
 */

interface ServiceStatus {
  name: string;
  container: string;
  status: 'healthy' | 'unhealthy' | 'unknown';
  latencyMs?: number;
  error?: string;
}

interface ServiceConfig {
  name: string;
  container: string;
  healthUrl: string;
  timeout: number;
  acceptCodes?: number[]; // HTTP status codes considered healthy (default: 200-399)
  k8sUnavailable?: boolean; // Service cannot be checked in Kubernetes
}

// Detect runtime environment
const isKubernetes = !!process.env.KUBERNETES_SERVICE_HOST;
const isDocker =
  !isKubernetes && (process.env.NODE_ENV === 'production' || process.env.DOCKER === 'true');

// Local dev port resolution: respects DEV_PORT_OFFSET and explicit per-service overrides
// (mirrors the logic in scripts/docker-up.js)
function localPort(envKey: string, defaultPort: number): number {
  const explicit = process.env[envKey];
  if (explicit) return Number.parseInt(explicit, 10);
  const offset = Number.parseInt(process.env.DEV_PORT_OFFSET || '0', 10);
  return defaultPort + offset;
}

const kongPort = localPort('DEV_KONG_PORT', 8000);
const traefikPort = localPort('DEV_TRAEFIK_PORT', 80);

// Core Supabase services - always present
const CORE_SERVICES: ServiceConfig[] = [
  {
    name: 'PostgreSQL',
    container: 'db',
    // In k8s: checked indirectly via PostgREST through Kong (handled specially in checkServiceHealth)
    healthUrl: isDocker ? 'http://db:5432' : `http://localhost:${localPort('DEV_DB_PORT', 5432)}`,
    timeout: 2000,
  },
  {
    name: 'API Gateway',
    container: 'kong',
    healthUrl: isKubernetes
      ? 'http://kong:8000/'
      : isDocker
        ? 'http://kong:8000/'
        : `http://localhost:${kongPort}/`,
    timeout: 2000,
    acceptCodes: [404], // 404 = running but no default route
  },
  {
    name: 'Authentication',
    container: 'auth',
    healthUrl: isKubernetes
      ? 'http://kong:8000/auth/v1/health'
      : isDocker
        ? 'http://auth:9999/health'
        : `http://localhost:${kongPort}/auth/v1/health`,
    timeout: 2000,
    acceptCodes: isDocker ? undefined : [200, 401], // Via Kong requires API key
  },
  {
    name: 'REST API',
    container: 'rest',
    healthUrl: isKubernetes
      ? 'http://kong:8000/rest/v1/'
      : isDocker
        ? 'http://rest:3000/'
        : `http://localhost:${kongPort}/rest/v1/`,
    timeout: 2000,
    acceptCodes: [200, 401], // 401 = running but requires auth
  },
  {
    name: 'Realtime',
    container: 'realtime',
    healthUrl: isKubernetes
      ? 'http://kong:8000/realtime/v1/'
      : isDocker
        ? 'http://realtime:4000/api/tenants'
        : `http://localhost:${kongPort}/realtime/v1/`,
    timeout: 2000,
    acceptCodes: [200, 401, 403, 426], // May require auth or websocket upgrade
  },
  {
    name: 'File Storage',
    container: 'storage',
    healthUrl: isKubernetes
      ? 'http://kong:8000/storage/v1/status'
      : isDocker
        ? 'http://storage:5000/status'
        : `http://localhost:${kongPort}/storage/v1/status`,
    timeout: 2000,
    acceptCodes: isDocker ? undefined : [200, 401], // Via Kong requires API key
  },
  {
    name: 'Studio',
    container: 'studio',
    healthUrl: isKubernetes
      ? 'http://studio:3000/'
      : isDocker
        ? 'http://studio:3000/'
        : `http://studio.localhost:${traefikPort}/`,
    timeout: 3000,
    acceptCodes: [200, 307], // 307 redirect is fine
  },
  {
    name: 'DB Management',
    container: 'meta',
    healthUrl: isKubernetes
      ? 'http://kong:8000/pg/'
      : isDocker
        ? 'http://meta:8080/health'
        : `http://localhost:${kongPort}/pg/`,
    timeout: 2000,
    acceptCodes: [200, 401], // May require auth via Kong
  },
];

// Optional services - added based on environment configuration
const OPTIONAL_SERVICES: Record<string, ServiceConfig[]> = {
  redis: [
    {
      name: 'Redis',
      container: 'redis',
      healthUrl: 'tcp://redis:6379', // Special marker for TCP check
      timeout: 2000,
    },
  ],
  n8n: [
    {
      name: 'n8n',
      container: 'n8n',
      // In k8s, use the k8s Service DNS name (same as Docker network name)
      healthUrl:
        isKubernetes || isDocker ? 'http://n8n:5678/healthz' : 'http://localhost:5678/healthz',
      timeout: 3000,
    },
  ],
  metabase: [
    {
      name: 'Metabase',
      container: 'metabase',
      healthUrl:
        isKubernetes || isDocker
          ? 'http://metabase:3000/api/health'
          : 'http://localhost:3001/api/health',
      timeout: 5000,
    },
  ],
  observability: [
    {
      name: 'Prometheus',
      container: 'prometheus',
      healthUrl:
        isKubernetes || isDocker
          ? 'http://prometheus:9090/-/healthy'
          : `http://localhost:${localPort('DEV_PROMETHEUS_PORT', 9190)}/-/healthy`,
      timeout: 2000,
    },
    {
      name: 'Grafana',
      container: 'grafana',
      healthUrl:
        isKubernetes || isDocker
          ? 'http://grafana:3000/api/health'
          : `http://localhost:${localPort('DEV_GRAFANA_PORT', 3002)}/api/health`,
      timeout: 3000,
    },
    {
      name: 'Loki',
      container: 'loki',
      healthUrl:
        isKubernetes || isDocker
          ? 'http://loki:3100/ready'
          : `http://localhost:${localPort('DEV_LOKI_PORT', 3100)}/ready`,
      timeout: 2000,
    },
  ],
};

// Cache for running containers (refreshed on each status check)
let runningContainersCache: Set<string> = new Set();
let containersCacheTime = 0;
const CACHE_TTL_MS = 5000; // 5 seconds

/**
 * Detect which containers are currently running via Docker
 */
async function getRunningContainers(): Promise<Set<string>> {
  // Docker is not available in Kubernetes
  if (isKubernetes) return new Set();

  const now = Date.now();
  if (now - containersCacheTime < CACHE_TTL_MS && runningContainersCache.size > 0) {
    return runningContainersCache;
  }

  try {
    const { stdout } = await execAsync('docker ps --format "{{.Names}}" 2>/dev/null', {
      timeout: 5000,
    });
    const containers = new Set(
      stdout
        .split('\n')
        .map((name) => name.trim())
        .filter(Boolean)
        // Extract service name from container name (e.g., "vibecarbon-n8n" -> "n8n")
        .map((name) => name.replace(/^[^-]+-/, ''))
    );
    runningContainersCache = containers;
    containersCacheTime = now;
    return containers;
  } catch {
    // If Docker check fails, return empty set (will only show core services)
    return new Set();
  }
}

/**
 * Get all services to check - core + any running optional services
 */
async function getEnabledServices(): Promise<ServiceConfig[]> {
  const services = [...CORE_SERVICES];
  const runningContainers = await getRunningContainers();

  // Add optional services if their containers are running
  for (const [, configs] of Object.entries(OPTIONAL_SERVICES)) {
    const isRunning = configs.some((config) => runningContainers.has(config.container));
    if (isRunning) {
      services.push(...configs);
    }
  }

  // Also check env vars as fallback (for when Docker detection fails)
  if (process.env.REDIS_ENABLED === 'true' && !services.some((s) => s.container === 'redis')) {
    services.push(...OPTIONAL_SERVICES.redis);
  }
  if (process.env.N8N_ENABLED === 'true' && !services.some((s) => s.container === 'n8n')) {
    services.push(...OPTIONAL_SERVICES.n8n);
  }
  if (
    process.env.METABASE_ENABLED === 'true' &&
    !services.some((s) => s.container === 'metabase')
  ) {
    services.push(...OPTIONAL_SERVICES.metabase);
  }
  if (
    process.env.OBSERVABILITY_ENABLED === 'true' &&
    !services.some((s) => s.container === 'prometheus')
  ) {
    services.push(...OPTIONAL_SERVICES.observability);
  }

  return services;
}

// List of optional service containers that should use Docker health check when not in Docker
const DOCKER_HEALTH_CHECK_CONTAINERS = [
  'redis',
  'n8n',
  'metabase',
  'prometheus',
  'grafana',
  'loki',
];

async function checkServiceHealth(service: ServiceConfig): Promise<ServiceStatus> {
  const start = Date.now();

  // Services that cannot be reached from the app pod in Kubernetes
  if (isKubernetes && service.k8sUnavailable) {
    return {
      name: service.name,
      container: service.container,
      status: 'unknown',
      error: 'Not accessible in Kubernetes (exposed via Traefik only)',
    };
  }

  // For optional services when running on host (not in Docker or Kubernetes), check via Docker container health status
  // This is needed because the API server can't reach Docker internal network hostnames from the host
  if (!isDocker && !isKubernetes && DOCKER_HEALTH_CHECK_CONTAINERS.includes(service.container)) {
    try {
      const { stdout } = await execAsync(
        `docker inspect --format="{{.State.Health.Status}}" $(docker ps -q -f name=-${service.container}) 2>/dev/null || echo "not_found"`,
        { timeout: service.timeout }
      );
      const status = stdout.trim();
      const isHealthy = status === 'healthy';
      const isUnhealthy = status === 'unhealthy' || status === 'not_found' || status === '';
      const isStarting = status === 'starting';

      return {
        name: service.name,
        container: service.container,
        status: isHealthy ? 'healthy' : isStarting ? 'unknown' : 'unhealthy',
        latencyMs: Date.now() - start,
        error: isUnhealthy ? 'Container not healthy' : isStarting ? 'Starting up' : undefined,
      };
    } catch {
      return {
        name: service.name,
        container: service.container,
        status: 'unknown',
        error: 'Cannot check container status',
      };
    }
  }

  // Special case for PostgreSQL - we can't HTTP check it, rely on other services
  if (service.container === 'db') {
    // Check if PostgREST can connect (implies DB is healthy)
    const postgrestUrl = isKubernetes
      ? 'http://kong:8000/rest/v1/'
      : isDocker
        ? 'http://rest:3000/'
        : `http://localhost:${kongPort}/rest/v1/`;
    try {
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), service.timeout);

      const response = await fetch(postgrestUrl, {
        method: 'GET',
        signal: controller.signal,
      });
      clearTimeout(timeoutId);

      // 401 via Kong is fine - means PostgREST (and thus DB) is running
      const isHealthy = response.ok || response.status === 401;

      return {
        name: service.name,
        container: service.container,
        status: isHealthy ? 'healthy' : 'unhealthy',
        latencyMs: Date.now() - start,
      };
    } catch {
      return {
        name: service.name,
        container: service.container,
        status: 'unknown',
        error: 'Cannot verify (checked via PostgREST)',
      };
    }
  }

  // Special case: TCP check for services without an HTTP health endpoint (e.g. Redis)
  if (service.healthUrl.startsWith('tcp://')) {
    const url = new URL(service.healthUrl);
    const host = url.hostname;
    const port = Number.parseInt(url.port, 10);
    return new Promise((resolve) => {
      const socket = new net.Socket();
      const latencyStart = Date.now();
      const done = (ok: boolean, err?: string) => {
        socket.destroy();
        resolve({
          name: service.name,
          container: service.container,
          status: ok ? 'healthy' : 'unhealthy',
          latencyMs: Date.now() - latencyStart,
          error: err,
        });
      };
      socket.setTimeout(service.timeout);
      socket.once('connect', () => done(true));
      socket.once('timeout', () => done(false, 'Timeout'));
      socket.once('error', (e) =>
        done(false, e.message.includes('ECONNREFUSED') ? 'Not running' : e.message)
      );
      socket.connect(port, host);
    });
  }

  try {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), service.timeout);

    const response = await fetch(service.healthUrl, {
      method: 'GET',
      signal: controller.signal,
    });
    clearTimeout(timeoutId);

    const latencyMs = Date.now() - start;

    // Check if status code is acceptable
    const isHealthy = service.acceptCodes
      ? service.acceptCodes.includes(response.status)
      : response.status >= 200 && response.status < 400;

    return {
      name: service.name,
      container: service.container,
      status: isHealthy ? 'healthy' : 'unhealthy',
      latencyMs,
    };
  } catch (error) {
    const latencyMs = Date.now() - start;
    const errorMessage = error instanceof Error ? error.message : 'Unknown error';

    // Check if it's a timeout or connection refused
    const isTimeout = errorMessage.includes('abort');
    const isConnectionRefused =
      errorMessage.includes('ECONNREFUSED') || errorMessage.includes('fetch failed');

    return {
      name: service.name,
      container: service.container,
      status: 'unhealthy',
      latencyMs,
      error: isTimeout ? 'Timeout' : isConnectionRefused ? 'Not running' : errorMessage,
    };
  }
}

import type { HonoVariables } from '../../types';

const servicesStatusRoutes = new Hono<{ Variables: HonoVariables }>();

// Helper to check if user is a super admin
function requireSuperAdmin(c: {
  get: (key: string) => unknown;
}): { error: string; status: 401 | 403 } | null {
  const user = c.get('user') as { app_metadata?: { role?: string } } | null;
  if (!user) return { error: 'Unauthorized', status: 401 };
  if (user.app_metadata?.role !== 'super_admin') return { error: 'Forbidden', status: 403 };
  return null;
}

servicesStatusRoutes.get('/', async (c) => {
  const authError = requireSuperAdmin(c);
  if (authError) return c.json({ error: authError.error }, authError.status);

  logger.debug('Checking services status');

  // Get enabled services (auto-detects running containers)
  const services = await getEnabledServices();

  // Check all services in parallel
  const results = await Promise.all(services.map(checkServiceHealth));

  // Sort by name for consistent ordering
  results.sort((a, b) => a.name.localeCompare(b.name));

  const healthy = results.filter((r) => r.status === 'healthy').length;
  const total = results.length;

  logger.debug({ healthy, total }, 'Services status check complete');

  return c.json({
    summary: {
      healthy,
      unhealthy: total - healthy,
      total,
    },
    services: results,
    timestamp: new Date().toISOString(),
  });
});

// All known container names (core + all optional)
const ALL_KNOWN_CONTAINERS = [
  ...CORE_SERVICES.map((s) => s.container),
  ...Object.values(OPTIONAL_SERVICES).flatMap((configs) => configs.map((c) => c.container)),
];

/**
 * Restart a Docker container using docker compose
 */
async function restartContainer(container: string): Promise<{ success: boolean; error?: string }> {
  // Only allow restarting known containers
  if (!ALL_KNOWN_CONTAINERS.includes(container)) {
    return { success: false, error: `Invalid container: ${container}` };
  }

  try {
    logger.info({ container }, 'Restarting container');
    await execAsync(`docker compose restart ${container}`, {
      timeout: 60000, // 60 second timeout
    });
    logger.info({ container }, 'Container restarted successfully');
    return { success: true };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : 'Unknown error';
    logger.error({ container, error: errorMessage }, 'Failed to restart container');
    return { success: false, error: errorMessage };
  }
}

// POST /restart - Restart all running services
servicesStatusRoutes.post('/restart', async (c) => {
  const authError = requireSuperAdmin(c);
  if (authError) return c.json({ error: authError.error }, authError.status);

  if (isKubernetes) {
    return c.json({ success: false, error: 'Not supported in Kubernetes' }, 501);
  }

  logger.info('Restarting all services');

  const services = await getEnabledServices();
  const containers = services.map((s) => s.container);

  const results = await Promise.all(
    containers.map(async (container) => {
      const result = await restartContainer(container);
      return { container, ...result };
    })
  );

  const successful = results.filter((r) => r.success).length;
  const failed = results.filter((r) => !r.success);

  logger.info({ successful, failed: failed.length }, 'Restart all complete');

  return c.json({
    success: failed.length === 0,
    summary: {
      successful,
      failed: failed.length,
      total: containers.length,
    },
    results,
  });
});

// POST /restart/:container - Restart a specific service
servicesStatusRoutes.post('/restart/:container', async (c) => {
  const authError = requireSuperAdmin(c);
  if (authError) return c.json({ error: authError.error }, authError.status);

  if (isKubernetes) {
    return c.json({ success: false, error: 'Not supported in Kubernetes' }, 501);
  }

  const container = c.req.param('container');

  if (!ALL_KNOWN_CONTAINERS.includes(container)) {
    return c.json({ success: false, error: `Invalid container: ${container}` }, 400);
  }

  const result = await restartContainer(container);

  if (!result.success) {
    return c.json(result, 500);
  }

  return c.json(result);
});

// ============================================================================
// KUBERNETES LOG HELPERS
// ============================================================================

// Load k8s service account credentials once at startup
const k8sCredentials = (() => {
  if (!isKubernetes) return null;
  try {
    return {
      token: fs.readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/token', 'utf8').trim(),
      ca: fs.readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/ca.crt'),
      namespace: fs
        .readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/namespace', 'utf8')
        .trim(),
    };
  } catch {
    return null;
  }
})();

/** Read the pod's default gateway from /proc/net/route (little-endian hex) */
function getPodDefaultGateway(): string | null {
  try {
    const routes = fs.readFileSync('/proc/net/route', 'utf8');
    for (const line of routes.split('\n').slice(1)) {
      const parts = line.split('\t');
      if (parts[1] === '00000000' && parts[2] && parts[2] !== '00000000') {
        const h = parts[2];
        return [
          parseInt(h.slice(6, 8), 16),
          parseInt(h.slice(4, 6), 16),
          parseInt(h.slice(2, 4), 16),
          parseInt(h.slice(0, 2), 16),
        ].join('.');
      }
    }
  } catch {
    /* ignore */
  }
  return null;
}

function testTcpConnection(host: string, port: number): Promise<boolean> {
  return new Promise((resolve) => {
    const sock = new net.Socket();
    sock.setTimeout(2000);
    sock.connect(port, host, () => {
      sock.destroy();
      resolve(true);
    });
    sock.on('error', () => {
      sock.destroy();
      resolve(false);
    });
    sock.on('timeout', () => {
      sock.destroy();
      resolve(false);
    });
  });
}

// Lazily detected API server endpoint (ClusterIP or pod gateway fallback)
let k8sApiEndpoint: { host: string; port: number } | null | undefined;

async function getK8sApiEndpoint(): Promise<{ host: string; port: number } | null> {
  if (k8sApiEndpoint !== undefined) return k8sApiEndpoint;

  const clusterIpHost = process.env.KUBERNETES_SERVICE_HOST ?? '';
  const clusterIpPort =
    Number(process.env.KUBERNETES_SERVICE_PORT_HTTPS || process.env.KUBERNETES_SERVICE_PORT) || 443;

  // Try ClusterIP first (works on worker nodes via kube-proxy DNAT)
  if (clusterIpHost && (await testTcpConnection(clusterIpHost, clusterIpPort))) {
    k8sApiEndpoint = { host: clusterIpHost, port: clusterIpPort };
    return k8sApiEndpoint;
  }

  // Fall back to pod gateway on port 6443 (k3s master: kube-proxy doesn't install
  // KUBE-SERVICES rules for the kubernetes ClusterIP on the master node itself)
  const gateway = getPodDefaultGateway();
  if (gateway && (await testTcpConnection(gateway, 6443))) {
    logger.info({ gateway }, 'K8s API: using pod gateway fallback (k3s master node)');
    k8sApiEndpoint = { host: gateway, port: 6443 };
    return k8sApiEndpoint;
  }

  k8sApiEndpoint = null;
  return null;
}

/** Human-readable names for k8s app labels */
const K8S_LABEL_NAMES: Record<string, string> = {
  'vibecarbon-app': 'App',
  'vibecarbon-kong': 'API Gateway',
  'vibecarbon-auth': 'Authentication',
  'vibecarbon-rest': 'REST API',
  'vibecarbon-realtime': 'Realtime',
  'vibecarbon-storage': 'File Storage',
  'vibecarbon-studio': 'Studio',
  'vibecarbon-meta': 'DB Management',
  'vibecarbon-postgres': 'PostgreSQL',
  n8n: 'n8n',
  metabase: 'Metabase',
  prometheus: 'Prometheus',
  grafana: 'Grafana',
  loki: 'Loki',
};

function sinceToSeconds(since: string): number {
  const match = since.match(/^(\d+)([smhd])$/);
  if (!match) return 600;
  const value = Number(match[1]);
  const multipliers: Record<string, number> = { s: 1, m: 60, h: 3600, d: 86400 };
  return value * (multipliers[match[2]] ?? 60);
}

async function k8sRequest(path: string, acceptText = false): Promise<string> {
  const creds = k8sCredentials;
  if (!creds) throw new Error('K8s credentials not available');

  const endpoint = await getK8sApiEndpoint();
  if (!endpoint) throw new Error('K8s API server not reachable');

  return new Promise((resolve, reject) => {
    const req = https.request(
      {
        hostname: endpoint.host,
        port: endpoint.port,
        path,
        method: 'GET',
        headers: {
          Authorization: `Bearer ${creds.token}`,
          Accept: acceptText ? 'text/plain' : 'application/json',
        },
        ca: creds.ca,
        // When falling back to gateway:6443, the TLS cert won't match the gateway IP
        // (it's issued for the ClusterIP/master hostname), so we must disable verification
        rejectUnauthorized: endpoint.host === (process.env.KUBERNETES_SERVICE_HOST ?? ''),
      },
      (res) => {
        let data = '';
        res.on('data', (chunk: Buffer) => {
          data += chunk.toString();
        });
        res.on('end', () => resolve(data));
      }
    );
    req.setTimeout(30000, () => req.destroy(new Error('K8s API timeout')));
    req.on('error', reject);
    req.end();
  });
}

interface K8sPod {
  metadata: { name: string; labels?: Record<string, string> };
  status?: { phase?: string };
}

async function listK8sPods(labelSelector?: string): Promise<K8sPod[]> {
  const ns = k8sCredentials?.namespace;
  if (!ns) return [];
  const qs = labelSelector ? `?labelSelector=${encodeURIComponent(labelSelector)}` : '';
  const body = await k8sRequest(`/api/v1/namespaces/${ns}/pods${qs}`);
  const parsed = JSON.parse(body) as { items?: K8sPod[] };
  return parsed.items ?? [];
}

async function getK8sPodLogs(
  podName: string,
  tail: number,
  sinceSeconds: number
): Promise<string[]> {
  const ns = k8sCredentials?.namespace;
  if (!ns) return [];
  const qs = new URLSearchParams({
    tailLines: String(tail),
    sinceSeconds: String(sinceSeconds),
    timestamps: 'true',
  });
  const text = await k8sRequest(`/api/v1/namespaces/${ns}/pods/${podName}/log?${qs}`, true);
  return text.split('\n').filter((l) => l.trim());
}

// ============================================================================
// DOCKER LOGS
// ============================================================================

/**
 * Fetch logs from Docker containers
 * Query params:
 *   - tail: number of lines (default 200)
 *   - since: time filter (e.g., "5m", "1h", "30s")
 *   - container: specific container (optional, defaults to all)
 */
servicesStatusRoutes.get('/logs', async (c) => {
  const authError = requireSuperAdmin(c);
  if (authError) return c.json({ error: authError.error }, authError.status);

  const tail = Math.min(Number(c.req.query('tail')) || 200, 1000); // Max 1000 lines
  const since = c.req.query('since') || '10m';
  const container = c.req.query('container');

  // Validate since format (should be like "5m", "1h", "30s")
  if (!/^\d+[smhd]$/.test(since)) {
    return c.json(
      { success: false, error: 'Invalid since format. Use format like "5m", "1h", "30s"' },
      400
    );
  }

  // --- Kubernetes: fetch logs via K8s API ---
  if (isKubernetes) {
    // Validate container param (must be a safe label value)
    if (container && !/^[a-z0-9-]+$/.test(container)) {
      return c.json({ success: false, error: 'Invalid container', logs: [] }, 400);
    }

    if (!k8sCredentials) {
      return c.json(
        {
          success: false,
          error: 'K8s service account credentials not available. Check RBAC configuration.',
          logs: [],
        },
        503
      );
    }

    try {
      const sinceSeconds = sinceToSeconds(since);
      const labelSelector = container ? `app=${container}` : undefined;
      const pods = await listK8sPods(labelSelector);
      const runningPods = pods.filter((p) => p.status?.phase === 'Running');

      if (runningPods.length === 0) {
        return c.json({
          success: true,
          logs: [],
          meta: {
            tail,
            since,
            container: container || 'all',
            lineCount: 0,
            timestamp: new Date().toISOString(),
          },
        });
      }

      const allLogs = await Promise.all(
        runningPods.map(async (pod) => {
          const appLabel = pod.metadata.labels?.app ?? pod.metadata.name;
          const displayName = appLabel.replace(/^vibecarbon-/, '');
          try {
            const lines = await getK8sPodLogs(pod.metadata.name, tail, sinceSeconds);
            return lines.map((line) => `${displayName} | ${line}`);
          } catch {
            return [];
          }
        })
      );

      const logs = allLogs.flat().sort();

      logger.debug({ tail, since, container, podCount: runningPods.length }, 'Fetched K8s logs');

      return c.json({
        success: true,
        logs,
        meta: {
          tail,
          since,
          container: container || 'all',
          lineCount: logs.length,
          timestamp: new Date().toISOString(),
        },
      });
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      logger.error({ error: errorMessage }, 'Failed to fetch K8s logs');
      return c.json({ success: false, error: errorMessage, logs: [] }, 500);
    }
  }

  // --- Docker: fetch logs via docker compose ---
  // Validate container if specified
  if (container && !ALL_KNOWN_CONTAINERS.includes(container)) {
    return c.json({ success: false, error: `Invalid container: ${container}` }, 400);
  }

  try {
    const containerArg = container || '';
    const cmd = `docker compose logs --tail ${tail} --since ${since} --timestamps ${containerArg} 2>&1`;

    logger.debug({ cmd, tail, since, container }, 'Fetching Docker logs');

    const { stdout } = await execAsync(cmd, {
      timeout: 30000, // 30 second timeout
      maxBuffer: 10 * 1024 * 1024, // 10MB buffer
    });

    // Parse and structure the logs
    const lines = stdout.split('\n').filter((line) => line.trim());

    return c.json({
      success: true,
      logs: lines,
      meta: {
        tail,
        since,
        container: container || 'all',
        lineCount: lines.length,
        timestamp: new Date().toISOString(),
      },
    });
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : 'Unknown error';
    logger.error({ error: errorMessage }, 'Failed to fetch Docker logs');

    // Check if Docker is not available
    if (errorMessage.includes('docker') && errorMessage.includes('not found')) {
      return c.json(
        {
          success: false,
          error: 'Docker is not available',
          logs: [],
        },
        503
      );
    }

    return c.json(
      {
        success: false,
        error: errorMessage,
        logs: [],
      },
      500
    );
  }
});

// GET /logs/containers - List available containers for log filtering
servicesStatusRoutes.get('/logs/containers', async (c) => {
  const authError = requireSuperAdmin(c);
  if (authError) return c.json({ error: authError.error }, authError.status);

  if (isKubernetes) {
    try {
      const pods = await listK8sPods();
      const seen = new Set<string>();
      const containers: Array<{ name: string; container: string }> = [];
      for (const pod of pods) {
        if (pod.status?.phase !== 'Running') continue;
        const appLabel = pod.metadata.labels?.app;
        if (appLabel && !seen.has(appLabel)) {
          seen.add(appLabel);
          containers.push({
            name: K8S_LABEL_NAMES[appLabel] ?? appLabel,
            container: appLabel,
          });
        }
      }
      containers.sort((a, b) => a.name.localeCompare(b.name));
      return c.json({ containers });
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      logger.error({ error: errorMessage }, 'Failed to list K8s pods');
      return c.json({ containers: [] });
    }
  }

  const services = await getEnabledServices();
  return c.json({
    containers: services.map((s) => ({
      name: s.name,
      container: s.container,
    })),
  });
});

export { servicesStatusRoutes };
