import * as fs from 'fs';
import * as path from 'path';
import * as sharp from 'sharp';
import { v4 as uuidv4 } from 'uuid';

/** Privata IP-ranges som ska blockeras (SSRF-skydd) */
const BLOCKED_HOSTNAMES = /^(localhost|127\.|10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|0\.0\.0\.0|::1|fc00:|fe80:)/i;

/**
 * Laddar ner en bild från en extern URL, optimerar den med sharp och
 * sparar till destDir. Returnerar relativ URL för användning i DB (/images/{uuid}.jpg).
 *
 * SSRF-skydd:
 * - Kräver https://
 * - Blockar privata IP-adresser och localhost
 * - Timeout 10s, max 5 MB
 * - Validerar att content-type är image/*
 */
export async function downloadAndOptimizeImage(
  sourceUrl: string,
  destDir: string,
): Promise<string> {
  const raw = sourceUrl.trim();
  const protocolNormalized = raw.startsWith('//') ? `https:${raw}` : raw;

  // SSRF: blockera privata hostnames
  let parsedUrl: URL;
  try {
    parsedUrl = new URL(protocolNormalized);
  } catch {
    throw new Error('Ogiltig bild-URL');
  }

  // Protokollvalidering
  if (parsedUrl.protocol !== 'https:') {
    throw new Error('Bild-URL måste använda https://');
  }

  const hostname = parsedUrl.hostname;

  if (BLOCKED_HOSTNAMES.test(hostname)) {
    throw new Error('Bild-URL pekar på ett blockerat nätverk');
  }

  // Ladda ner bilden
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 10_000);
  let response: Response;
  try {
    response = await fetch(parsedUrl.toString(), {
      signal: controller.signal,
      headers: { 'User-Agent': 'Mozilla/5.0 (compatible; RecipeApp/1.0)' },
    });
  } finally {
    clearTimeout(timeout);
  }

  if (!response.ok) {
    throw new Error(`HTTP ${response.status} vid nedladdning av bild`);
  }

  // Validera content-type
  const contentType = response.headers.get('content-type') ?? '';
  if (!contentType.startsWith('image/')) {
    throw new Error(`Ogiltig content-type: ${contentType}`);
  }

  const arrayBuffer = await response.arrayBuffer();
  if (arrayBuffer.byteLength > 5 * 1024 * 1024) {
    throw new Error('Bilden är för stor (max 5 MB)');
  }

  const imageBuffer = Buffer.from(arrayBuffer);

  // Skapa destDir om det inte finns
  if (!fs.existsSync(destDir)) {
    fs.mkdirSync(destDir, { recursive: true });
  }

  const filename = `${uuidv4()}.jpg`;
  const outputPath = path.join(destDir, filename);

  // Optimera med sharp
  await (sharp as unknown as (input: Buffer) => sharp.Sharp)(imageBuffer)
    .resize(1200, 800, {
      fit: 'inside',
      withoutEnlargement: true,
    })
    .jpeg({ quality: 80 })
    .toFile(outputPath);

  // Returnera relativ sökväg för DB-lagring
  return `/images/${filename}`;
}
