import "server-only";

import {
  createCipheriv,
  createDecipheriv,
  createHash,
  randomBytes,
} from "node:crypto";

const ENCRYPTION_PREFIX = "enc:v1";
const ENCRYPTION_ENV_KEY = "EMAIL_CONFIG_ENCRYPTION_KEY";
const MIN_KEY_BYTES = 32;

function decodeConfiguredKey(rawValue: string): Buffer {
  const value = rawValue.trim();

  if (/^[0-9a-fA-F]+$/.test(value) && value.length % 2 === 0) {
    return Buffer.from(value, "hex");
  }

  try {
    const base64Buffer = Buffer.from(value, "base64");
    if (
      base64Buffer.length > 0 &&
      base64Buffer.toString("base64").replace(/=+$/, "") ===
        value.replace(/=+$/, "")
    ) {
      return base64Buffer;
    }
  } catch {
    // Fall back to UTF-8 below.
  }

  return Buffer.from(value, "utf8");
}

function getEncryptionKeyMaterial(): Buffer {
  const configured = process.env[ENCRYPTION_ENV_KEY]?.trim() ?? "";
  if (!configured) {
    throw new Error(`${ENCRYPTION_ENV_KEY} is not configured.`);
  }

  const keyMaterial = decodeConfiguredKey(configured);
  if (keyMaterial.byteLength < MIN_KEY_BYTES) {
    throw new Error(
      `${ENCRYPTION_ENV_KEY} must provide at least ${MIN_KEY_BYTES} bytes of key material.`,
    );
  }

  return createHash("sha256").update(keyMaterial).digest();
}

export function assertSecretsEncryptionReady(): void {
  getEncryptionKeyMaterial();
}

export function encryptSecret(plainText: string): string {
  const normalized = plainText.trim();
  if (!normalized) {
    throw new Error("Secret value is required for encryption.");
  }

  const key = getEncryptionKeyMaterial();
  const iv = randomBytes(12);
  const cipher = createCipheriv("aes-256-gcm", key, iv);
  const encrypted = Buffer.concat([
    cipher.update(normalized, "utf8"),
    cipher.final(),
  ]);
  const tag = cipher.getAuthTag();

  return [
    ENCRYPTION_PREFIX,
    iv.toString("base64"),
    tag.toString("base64"),
    encrypted.toString("base64"),
  ].join(":");
}

export function decryptSecret(cipherText: string): string {
  if (!cipherText.startsWith(`${ENCRYPTION_PREFIX}:`)) {
    throw new Error("Encrypted secret format is invalid.");
  }

  const payload = cipherText.slice(ENCRYPTION_PREFIX.length + 1);
  const [ivPart, tagPart, encryptedPart, ...extraParts] = payload.split(":");
  if (!ivPart || !tagPart || !encryptedPart || extraParts.length > 0) {
    throw new Error("Encrypted secret format is invalid.");
  }

  const key = getEncryptionKeyMaterial();
  const decipher = createDecipheriv(
    "aes-256-gcm",
    key,
    Buffer.from(ivPart, "base64"),
  );
  decipher.setAuthTag(Buffer.from(tagPart, "base64"));

  const plain = Buffer.concat([
    decipher.update(Buffer.from(encryptedPart, "base64")),
    decipher.final(),
  ]);

  return plain.toString("utf8");
}
