import { prisma } from '../config/database.js';
import { encryptAesGcm, decryptAesGcm, deriveAesKey } from '@horux/core';
import { env } from '../config/env.js';
import { randomBytes } from 'crypto';

function getEncryptionKey(): Buffer {
  const secret = env.CONNECTOR_ENCRYPTION_KEY || env.FIEL_ENCRYPTION_KEY;
  return deriveAesKey(secret);
}

export async function provisionConnector(tenantId: string): Promise<{
  tunnelHostname: string;
  horuxToken: string;
  dockerRunCommand: string;
}> {
  const tenant = await prisma.tenant.findUnique({
    where: { id: tenantId },
    select: { nombre: true, rfc: true, dbMode: true },
  });
  if (!tenant) throw new Error('Tenant no encontrado');

  const slug = tenant.rfc.toLowerCase().replace(/[^a-z0-9]/g, '');
  const tunnelDomain = env.CLOUDFLARE_TUNNEL_DOMAIN || 'tunnel.horux.mx';
  const hostname = `${slug}.${tunnelDomain}`;

  // Generate a secure token for the connector
  const horuxToken = randomBytes(32).toString('hex');

  // Encrypt the token for storage — format: iv(16) + ciphertext + tag(16), base64
  const key = getEncryptionKey();
  const { encrypted, iv, tag } = encryptAesGcm(Buffer.from(horuxToken, 'utf-8'), key);
  const tokenEncoded = Buffer.concat([iv, encrypted, tag]).toString('base64');

  // TODO: Call Cloudflare API to create tunnel when CLOUDFLARE_API_TOKEN is configured
  // For now, store the config and let the user manually configure cloudflared
  if (env.CLOUDFLARE_API_TOKEN) {
    console.log(`[Connector] Would create Cloudflare tunnel for ${hostname} — API integration pending`);
  }

  await prisma.tenant.update({
    where: { id: tenantId },
    data: {
      // El conector es una feature de respaldo; el tenant siempre permanece MANAGED
      connectorTokenEnc: tokenEncoded,
      connectorTunnelHostname: hostname,
    },
  });

  const dockerRunCommand = [
    'docker run -d --name horux-connector',
    `  -e HORUX_TOKEN="${horuxToken}"`,
    `  -e HORUX_API_URL="${env.CORS_ORIGIN || 'https://horuxfin.com'}"`,
    '  -e POSTGRES_HOST="localhost"',
    '  -e POSTGRES_PORT="5432"',
    '  horux/connector:latest',
  ].join(' \\\n');

  return { tunnelHostname: hostname, horuxToken, dockerRunCommand };
}

export async function recordHeartbeat(tenantId: string, data: {
  version: string;
  uptimeSeconds: number;
  postgresPingMs: number;
  pgVersion?: string;
  lastMigration?: string;
  status?: string;
  errorMsg?: string;
}): Promise<void> {
  await Promise.all([
    prisma.connectorHeartbeat.create({
      data: {
        tenantId,
        latencyMs: data.postgresPingMs,
        version: data.version,
        pgVersion: data.pgVersion,
        status: data.status || 'OK',
        errorMsg: data.errorMsg,
      },
    }),
    prisma.tenant.update({
      where: { id: tenantId },
      data: {
        connectorLastSeen: new Date(),
        connectorVersion: data.version,
      },
    }),
  ]);
}

export async function verifyConnectorToken(token: string): Promise<string | null> {
  // Find tenant by trying to decrypt stored tokens.
  // This is O(N) — for production, use a hashed token lookup table.
  const tenants = await prisma.tenant.findMany({
    where: { connectorTokenEnc: { not: null } },
    select: { id: true, connectorTokenEnc: true },
  });

  const key = getEncryptionKey();
  // Stored format: iv(16 bytes) + ciphertext + tag(16 bytes), base64-encoded
  const IV_LENGTH = 16;
  const TAG_LENGTH = 16;

  for (const t of tenants) {
    if (!t.connectorTokenEnc) continue;
    try {
      const blob = Buffer.from(t.connectorTokenEnc, 'base64');
      const iv = blob.subarray(0, IV_LENGTH);
      const tag = blob.subarray(blob.length - TAG_LENGTH);
      const ciphertext = blob.subarray(IV_LENGTH, blob.length - TAG_LENGTH);
      const decrypted = decryptAesGcm(ciphertext, iv, tag, key);
      if (decrypted.toString('utf-8') === token) {
        return t.id;
      }
    } catch {
      continue;
    }
  }

  return null;
}

export async function getConnectorStatus(tenantId: string): Promise<{
  configured: boolean;
  tunnelHostname?: string;
  lastSeen?: string;
  version?: string;
  status: 'connected' | 'degraded' | 'disconnected' | 'not_configured';
}> {
  const tenant = await prisma.tenant.findUnique({
    where: { id: tenantId },
    select: { dbMode: true, connectorTunnelHostname: true, connectorLastSeen: true, connectorVersion: true },
  });

  if (!tenant || !tenant.connectorTunnelHostname) {
    return { configured: false, status: 'not_configured' };
  }

  const lastSeen = tenant.connectorLastSeen;
  const now = new Date();
  let status: 'connected' | 'degraded' | 'disconnected' = 'disconnected';

  if (lastSeen) {
    const diffMs = now.getTime() - lastSeen.getTime();
    if (diffMs < 60_000) status = 'connected';
    else if (diffMs < 300_000) status = 'degraded';
  }

  return {
    configured: true,
    tunnelHostname: tenant.connectorTunnelHostname ?? undefined,
    lastSeen: lastSeen?.toISOString(),
    version: tenant.connectorVersion ?? undefined,
    status,
  };
}
