import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
import { lookup } from 'node:dns/promises';
import { chmod, readFile, writeFile } from 'node:fs/promises';
import net from 'node:net';
import path from 'node:path';

export async function loadOrCreateKey(dataDir) {
  const keyFile = path.join(dataDir, '.secret-key');
  try {
    const key = Buffer.from((await readFile(keyFile, 'utf8')).trim(), 'base64');
    if (key.length !== 32) throw new Error('Invalid local encryption key');
    return key;
  } catch (error) {
    if (error.code !== 'ENOENT') throw error;
    const key = randomBytes(32);
    await writeFile(keyFile, `${key.toString('base64')}\n`, { mode: 0o600 });
    await chmod(keyFile, 0o600);
    return key;
  }
}

export function encryptJson(key, value) {
  const iv = randomBytes(12);
  const cipher = createCipheriv('aes-256-gcm', key, iv);
  const encrypted = Buffer.concat([cipher.update(JSON.stringify(value), 'utf8'), cipher.final()]);
  return { v: 1, iv: iv.toString('base64'), tag: cipher.getAuthTag().toString('base64'), data: encrypted.toString('base64') };
}

export function decryptJson(key, value) {
  if (!value || value.v !== 1) return null;
  const decipher = createDecipheriv('aes-256-gcm', key, Buffer.from(value.iv, 'base64'));
  decipher.setAuthTag(Buffer.from(value.tag, 'base64'));
  return JSON.parse(Buffer.concat([
    decipher.update(Buffer.from(value.data, 'base64')),
    decipher.final(),
  ]).toString('utf8'));
}

function isPrivateIp(address) {
  if (net.isIPv4(address)) {
    const [a, b] = address.split('.').map(Number);
    return a === 10 || a === 127 || a === 0 || (a === 169 && b === 254) ||
      (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168) || (a === 100 && b >= 64 && b <= 127);
  }
  const normal = address.toLowerCase();
  return normal === '::1' || normal === '::' || normal.startsWith('fc') ||
    normal.startsWith('fd') || normal.startsWith('fe8') || normal.startsWith('fe9') ||
    normal.startsWith('fea') || normal.startsWith('feb');
}

export async function assertSafeRemoteUrl(rawUrl) {
  const url = new URL(rawUrl);
  if (!['http:', 'https:'].includes(url.protocol)) throw new Error('Only HTTP(S) image URLs are allowed');
  const addresses = await lookup(url.hostname, { all: true });
  if (!addresses.length || addresses.some(({ address }) => isPrivateIp(address))) {
    throw new Error('Private or local image addresses are not allowed');
  }
  return url;
}

