/** Tiny helper around the platform_settings JSON store. */
import { db } from '@server/db/drizzle';
import { sql } from 'drizzle-orm';
import crypto from 'crypto';

import { childLogger } from '@server/logger';
const log = childLogger('svc.email.platform-settings');

export async function getPlatformSetting<T = unknown>(key: string, fallback: T): Promise<T> {
  try {
    const { rows } = await db.execute(sql`SELECT value FROM platform_settings WHERE key = ${key} LIMIT 1`);
    if (!rows[0]) return fallback;
    const v = (rows[0] as { value: unknown }).value;
    return (v as T) ?? fallback;
  } catch {
    return fallback;
  }
}

export async function setPlatformSetting(key: string, value: unknown, updatedBy?: string | null): Promise<void> {
  await db.execute(sql`
    INSERT INTO platform_settings (key, value, updated_by, updated_at)
    VALUES (${key}, ${JSON.stringify(value)}::jsonb, ${updatedBy ?? null}, now())
    ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_by = EXCLUDED.updated_by, updated_at = now()
  `);
}

// ---------- SMTP password obfuscation ----------
// Symmetric AES-256-GCM using a key derived from a stable secret.
// We avoid plaintext-at-rest while keeping migrations simple. The secret comes
// from SMTP_ENC_KEY (preferred) or falls back to SESSION_SECRET so existing
// deployments keep working without extra config.

function getEncKey(): Buffer {
  const raw = process.env.SMTP_ENC_KEY || process.env.SESSION_SECRET;
  if (!raw) {
    if (process.env.NODE_ENV === 'production') {
      throw new Error('SMTP_ENC_KEY (or SESSION_SECRET) must be set in production for SMTP password encryption');
    }
    // dev only: stable per-process key so encrypt/decrypt round-trip works without env config
    log.warn('No SMTP_ENC_KEY/SESSION_SECRET set — using dev-only fallback key. Set one of these env vars in production.');
    return crypto.createHash('sha256').update('restroagent-dev-only-smtp-enc').digest();
  }
  return crypto.createHash('sha256').update(raw).digest();
}

export function encryptSecret(plain: string): string {
  if (!plain) return '';
  const iv = crypto.randomBytes(12);
  const cipher = crypto.createCipheriv('aes-256-gcm', getEncKey(), iv);
  const enc = Buffer.concat([cipher.update(plain, 'utf8'), cipher.final()]);
  const tag = cipher.getAuthTag();
  return `enc1:${iv.toString('base64')}:${tag.toString('base64')}:${enc.toString('base64')}`;
}

export function decryptSecret(value: string | null | undefined): string {
  if (!value) return '';
  if (!value.startsWith('enc1:')) return value; // legacy plaintext
  try {
    const [, ivB64, tagB64, encB64] = value.split(':');
    const iv = Buffer.from(ivB64, 'base64');
    const tag = Buffer.from(tagB64, 'base64');
    const enc = Buffer.from(encB64, 'base64');
    if (tag.length !== 16) {
      throw new Error('Invalid auth tag length — expected 16 bytes');
    }
    const decipher = crypto.createDecipheriv('aes-256-gcm', getEncKey(), iv, { authTagLength: 16 });
    decipher.setAuthTag(tag);
    return Buffer.concat([decipher.update(enc), decipher.final()]).toString('utf8');
  } catch {
    return '';
  }
}

export interface PlatformSmtpConfig {
  host: string;
  port: number;
  user: string;
  pass: string;
  from: string;
  secure?: boolean;
}

export async function getPlatformSmtp(): Promise<PlatformSmtpConfig | null> {
  const cfg = await getPlatformSetting<Partial<PlatformSmtpConfig & { passEnc?: string }>>('smtp_config', {});
  if (!cfg || !cfg.host || !cfg.user) return null;
  const pass = cfg.passEnc ? decryptSecret(cfg.passEnc) : (cfg.pass || '');
  if (!pass) return null;
  return {
    host: cfg.host,
    port: cfg.port ?? 587,
    user: cfg.user,
    pass,
    from: cfg.from || cfg.user,
    secure: cfg.secure,
  };
}

export async function setPlatformSmtp(cfg: PlatformSmtpConfig & { pass?: string }, updatedBy?: string | null): Promise<void> {
  const stored = {
    host: cfg.host,
    port: cfg.port,
    user: cfg.user,
    from: cfg.from,
    secure: cfg.secure ?? cfg.port === 465,
    passEnc: cfg.pass ? encryptSecret(cfg.pass) : undefined,
  };
  // Preserve existing password if blank submitted
  if (!cfg.pass) {
    const existing = await getPlatformSetting<{ passEnc?: string }>('smtp_config', {});
    stored.passEnc = existing?.passEnc;
  }
  await setPlatformSetting('smtp_config', stored, updatedBy);
}
