import crypto from "crypto";
import fs from "fs";
import path from "path";
import { Readable } from "stream";
import { env } from "../config/env.js";

const ENCRYPTION_MAGIC = Buffer.from("BROFFENC1");
const IV_LENGTH = 12;
const AUTH_TAG_LENGTH = 16;

function resolveEncryptionKey(rawValue) {
  const raw = String(rawValue || "").trim();
  if (!raw) return null;

  if (/^[0-9a-fA-F]{64}$/.test(raw)) {
    return Buffer.from(raw, "hex");
  }

  try {
    const base64 = Buffer.from(raw, "base64");
    if (base64.length === 32) return base64;
  } catch {
    // noop
  }

  return crypto.createHash("sha256").update(raw, "utf8").digest();
}

function getConfiguredEncryptionKey() {
  return resolveEncryptionKey(env.fileEncryptionKey);
}

function encryptBuffer(buffer) {
  const key = getConfiguredEncryptionKey();

  if (!key) {
    throw new Error("Dateiverschlüsselung ist aktiviert, aber FILE_ENCRYPTION_KEY fehlt oder ist ungültig.");
  }

  const iv = crypto.randomBytes(IV_LENGTH);
  const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
  const encrypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
  const authTag = cipher.getAuthTag();

  return Buffer.concat([ENCRYPTION_MAGIC, iv, authTag, encrypted]);
}

function decryptBuffer(buffer) {
  if (!Buffer.isBuffer(buffer) || buffer.length < ENCRYPTION_MAGIC.length) {
    return buffer;
  }

  if (!buffer.subarray(0, ENCRYPTION_MAGIC.length).equals(ENCRYPTION_MAGIC)) {
    return buffer;
  }

  const key = getConfiguredEncryptionKey();
  if (!key) {
    throw new Error("Die Datei ist verschlüsselt, aber FILE_ENCRYPTION_KEY ist nicht gesetzt.");
  }

  const ivStart = ENCRYPTION_MAGIC.length;
  const ivEnd = ivStart + IV_LENGTH;
  const authTagEnd = ivEnd + AUTH_TAG_LENGTH;

  const iv = buffer.subarray(ivStart, ivEnd);
  const authTag = buffer.subarray(ivEnd, authTagEnd);
  const payload = buffer.subarray(authTagEnd);

  const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
  decipher.setAuthTag(authTag);

  return Buffer.concat([decipher.update(payload), decipher.final()]);
}

export async function saveObject(storageKey, buffer, contentType) {
  if (env.storageProvider === "local") {
    const fullPath = path.resolve(env.uploadDir, storageKey);
    fs.mkdirSync(path.dirname(fullPath), { recursive: true });

    const outputBuffer = env.fileEncryptionEnabled ? encryptBuffer(buffer) : buffer;
    fs.writeFileSync(fullPath, outputBuffer);

    return {
      storage_provider: "local",
      storage_path: fullPath,
      content_type: contentType,
      encrypted: env.fileEncryptionEnabled,
    };
  }

  throw new Error("S3-Provider in dieser Referenz nicht implementiert");
}

export async function getObjectStream(storagePath) {
  const raw = fs.readFileSync(storagePath);
  const output = decryptBuffer(raw);

  return {
    stream: Readable.from(output),
    contentType: undefined,
  };
}


export async function deleteObject(storagePath) {
  if (!storagePath) return;

  if (env.storageProvider === "local") {
    try {
      fs.unlinkSync(storagePath);
    } catch (err) {
      if (err?.code !== "ENOENT") throw err;
    }
    return;
  }

  throw new Error("S3-Provider in dieser Referenz nicht implementiert");
}
