import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
import { dirname, isAbsolute, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { CONFIG_PATH } from './config-store.mjs';
import { SECRET_DEFINITIONS, SECRET_NAMES, maskSecret, validateSecretName, validateSecretValue } from '../app/core/security/secret-contract.mjs';

const appRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const dataRoot = resolve(String(process.env.VENDOO_DATA_DIR || join(appRoot, 'data')));
const configuredStore = String(process.env.VENDOO_SECRETS_PATH || '').trim();
const configuredKey = String(process.env.VENDOO_MASTER_KEY_FILE || '').trim();
const defaultStorePath = join(dataRoot, 'config', 'secrets.enc.json');
const defaultMasterKeyPath = join(dataRoot, 'config', 'master.key');
export const SECRETS_PATH = resolve(configuredStore ? (isAbsolute(configuredStore) ? configuredStore : join(appRoot, configuredStore)) : defaultStorePath);
export const MASTER_KEY_PATH = resolve(configuredKey ? (isAbsolute(configuredKey) ? configuredKey : join(appRoot, configuredKey)) : defaultMasterKeyPath);
const ALGORITHM = 'aes-256-gcm';
let cache = null;
let masterKey = null;

function secureWrite(path, content) {
  mkdirSync(dirname(path), { recursive: true });
  const temp = `${path}.tmp-${process.pid}-${Date.now()}`;
  writeFileSync(temp, content, { encoding: 'utf8', mode: 0o600 });
  try { chmodSync(temp, 0o600); } catch {}
  renameSync(temp, path);
  try { chmodSync(path, 0o600); } catch {}
}

function parseMasterKey(raw) {
  const text = String(raw || '').trim();
  if (!text) return null;
  const buffer = /^[a-f0-9]{64}$/i.test(text) ? Buffer.from(text, 'hex') : Buffer.from(text, 'base64');
  if (buffer.length !== 32) throw new Error('VENDOO_MASTER_KEY muss 32 Bytes als Base64 oder 64 Hex-Zeichen enthalten.');
  return buffer;
}

function loadMasterKey() {
  if (masterKey) return masterKey;
  const fromEnv = parseMasterKey(process.env.VENDOO_MASTER_KEY);
  if (fromEnv) return (masterKey = fromEnv);
  if (existsSync(MASTER_KEY_PATH)) {
    masterKey = parseMasterKey(readFileSync(MASTER_KEY_PATH, 'utf8'));
    if (!masterKey) throw new Error('Master-Key-Datei ist leer.');
    return masterKey;
  }
  masterKey = randomBytes(32);
  secureWrite(MASTER_KEY_PATH, masterKey.toString('base64'));
  return masterKey;
}

function emptyStore() {
  return { version: 1, algorithm: ALGORITHM, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), entries: {} };
}

function readStore() {
  if (cache) return cache;
  if (!existsSync(SECRETS_PATH)) return (cache = emptyStore());
  const parsed = JSON.parse(readFileSync(SECRETS_PATH, 'utf8'));
  if (parsed.version !== 1 || parsed.algorithm !== ALGORITHM || !parsed.entries || typeof parsed.entries !== 'object') {
    throw new Error('Secret-Speicher besitzt ein unbekanntes Format.');
  }
  return (cache = parsed);
}

function persist(store) {
  store.updatedAt = new Date().toISOString();
  secureWrite(SECRETS_PATH, `${JSON.stringify(store, null, 2)}\n`);
  cache = store;
}

function encrypt(value) {
  const iv = randomBytes(12);
  const cipher = createCipheriv(ALGORITHM, loadMasterKey(), iv);
  const ciphertext = Buffer.concat([cipher.update(value, 'utf8'), cipher.final()]);
  return { iv: iv.toString('base64'), tag: cipher.getAuthTag().toString('base64'), ciphertext: ciphertext.toString('base64') };
}

function decrypt(entry) {
  const decipher = createDecipheriv(ALGORITHM, loadMasterKey(), Buffer.from(entry.iv, 'base64'));
  decipher.setAuthTag(Buffer.from(entry.tag, 'base64'));
  return Buffer.concat([decipher.update(Buffer.from(entry.ciphertext, 'base64')), decipher.final()]).toString('utf8');
}

export function setSecret(name, value, { source = 'ui' } = {}) {
  const key = validateSecretName(name);
  const clean = validateSecretValue(value);
  const store = readStore();
  if (!clean) {
    delete store.entries[key];
    delete process.env[key];
  } else {
    store.entries[key] = { ...encrypt(clean), updatedAt: new Date().toISOString(), source: String(source || 'ui').slice(0, 40) };
    process.env[key] = clean;
  }
  persist(store);
  return getSecretMetadata(key);
}

export function getSecret(name) {
  const key = validateSecretName(name);
  const entry = readStore().entries[key];
  if (entry) return decrypt(entry);
  return process.env[key] || '';
}

export function hasSecret(name) {
  try { return Boolean(getSecret(name)); } catch { return false; }
}

export function deleteSecret(name) { return setSecret(name, '', { source: 'deleted' }); }

export function getSecretMetadata(name) {
  const key = validateSecretName(name);
  const entry = readStore().entries[key];
  const value = entry ? decrypt(entry) : (process.env[key] || '');
  return {
    name: key,
    label: SECRET_DEFINITIONS[key].label,
    category: SECRET_DEFINITIONS[key].category,
    editable: SECRET_DEFINITIONS[key].editable,
    configured: Boolean(value),
    masked: maskSecret(value),
    provider: entry ? 'encrypted-file' : (value ? 'environment' : 'none'),
    updated_at: entry?.updatedAt || null,
  };
}

export function listSecretMetadata() { return SECRET_NAMES.map(getSecretMetadata); }

export function hydrateSecretsToEnvironment() {
  const store = readStore();
  for (const name of SECRET_NAMES) {
    if (store.entries[name]) process.env[name] = decrypt(store.entries[name]);
  }
  return listSecretMetadata();
}

function parseEnv(content) {
  const result = new Map();
  for (const line of String(content || '').split(/\r?\n/)) {
    const match = line.match(/^([A-Z][A-Z0-9_]*)=(.*)$/);
    if (match) result.set(match[1], match[2]);
  }
  return result;
}

export function migrateRuntimeConfigSecrets() {
  if (!existsSync(CONFIG_PATH)) return { migrated: [], backup: null };
  const original = readFileSync(CONFIG_PATH, 'utf8');
  const values = parseEnv(original);
  const migrated = [];
  let next = original;
  for (const name of SECRET_NAMES) {
    const value = values.get(name);
    if (!value) continue;
    setSecret(name, value, { source: 'runtime-config-migration' });
    next = next.replace(new RegExp(`^${name}=.*(?:\\r?\\n|$)`, 'm'), '');
    migrated.push(name);
  }
  if (!migrated.length) return { migrated, backup: null };
  const backup = `${CONFIG_PATH}.pre-secrets-1.37.0.bak`;
  if (!existsSync(backup)) copyFileSync(CONFIG_PATH, backup);
  secureWrite(CONFIG_PATH, next.replace(/^\s+/, ''));
  return { migrated, backup };
}

export function secretStoreStatus() {
  const metadata = listSecretMetadata();
  return {
    provider: 'encrypted-file', algorithm: ALGORITHM,
    store_path: SECRETS_PATH, master_key_path: MASTER_KEY_PATH,
    configured: metadata.filter(item => item.configured).length,
    encrypted: metadata.filter(item => item.provider === 'encrypted-file').length,
    environment: metadata.filter(item => item.provider === 'environment').length,
    secrets: metadata,
  };
}
