import { getNitroOrigin } from "#site-config/server/composables/getNitroOrigin";
import { useStorage } from "nitropack/runtime";
import { withBase, withoutLeadingSlash } from "ufo";
import { toBase64Image } from "../../../../shared.js";
import { decodeHtml } from "../../../util/encoding.js";
import { logger } from "../../../util/logger.js";
import { getImageDimensions } from "../../utils/image-detector.js";
import { defineTransformer } from "../plugins.js";
const RE_IPV6_BRACKETS = /^\[|\]$/g;
const RE_MAPPED_V4 = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/;
const RE_DIGIT_ONLY = /^\d+$/;
const RE_INT_IP = /^(?:0x[\da-f]+|\d+)$/i;
function isPrivateIPv4(a, b) {
  if (a === 127)
    return true;
  if (a === 10)
    return true;
  if (a === 172 && b >= 16 && b <= 31)
    return true;
  if (a === 192 && b === 168)
    return true;
  if (a === 169 && b === 254)
    return true;
  if (a === 0)
    return true;
  return false;
}
function isBlockedUrl(url) {
  let parsed;
  try {
    parsed = new URL(url);
  } catch {
    return true;
  }
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
    return true;
  const hostname = parsed.hostname.toLowerCase();
  const bare = hostname.replace(RE_IPV6_BRACKETS, "");
  if (bare === "localhost" || bare.endsWith(".localhost"))
    return true;
  const mappedV4 = bare.match(RE_MAPPED_V4);
  const ip = mappedV4 ? mappedV4[1] : bare;
  const parts = ip.split(".");
  if (parts.length === 4 && parts.every((p) => RE_DIGIT_ONLY.test(p))) {
    const octets = parts.map(Number);
    if (octets.some((o) => o > 255))
      return true;
    return isPrivateIPv4(octets[0], octets[1]);
  }
  if (RE_INT_IP.test(ip)) {
    const num = Number(ip);
    if (!Number.isNaN(num) && num >= 0 && num <= 4294967295)
      return isPrivateIPv4(num >> 24 & 255, num >> 16 & 255);
  }
  if (bare === "::1" || bare.startsWith("fc") || bare.startsWith("fd") || bare.startsWith("fe80"))
    return true;
  return false;
}
const RE_URL_LEADING = /^url\(['"]?/;
const RE_URL_TRAILING = /['"]?\)$/;
async function resolveLocalFilePathImage(publicStoragePath, src) {
  const normalizedSrc = withoutLeadingSlash(
    src.replace("_nuxt/@fs/", "").replace("_nuxt/", "").replace("./", "")
  ).replaceAll("/", ":");
  const key = `${publicStoragePath}:${normalizedSrc}`;
  if (await useStorage().hasItem(key))
    return await useStorage().getItemRaw(key);
}
export default defineTransformer([
  // fix <img src="">
  {
    filter: (node) => node.type === "img" && node.props?.src,
    transform: async (node, { e, publicStoragePath, runtimeConfig }) => {
      let src = node.props.src;
      const isRelative = src.startsWith("/");
      let dimensions;
      let imageBuffer;
      if (src.endsWith(".webp")) {
        logger.warn("Using WebP images with Satori is not supported. Please consider switching image format or use the chromium renderer.", src);
      }
      if (isRelative) {
        if (import.meta.prerender || import.meta.dev) {
          const srcWithoutBase = src.replace(runtimeConfig.app.baseURL, "");
          imageBuffer = await resolveLocalFilePathImage(publicStoragePath, srcWithoutBase);
        }
        if (!imageBuffer) {
          imageBuffer = await e.$fetch(src, { responseType: "arrayBuffer" }).catch(() => {
          });
          if (!imageBuffer && !import.meta.prerender) {
            imageBuffer = await e.$fetch(src, {
              baseURL: getNitroOrigin(e),
              responseType: "arrayBuffer"
            }).catch(() => {
            });
          }
        }
        if (imageBuffer) {
          const buffer = imageBuffer instanceof ArrayBuffer ? imageBuffer : imageBuffer.buffer;
          node.props.src = toBase64Image(buffer);
        }
      } else if (!src.startsWith("data:")) {
        src = decodeHtml(src);
        if (!import.meta.dev && isBlockedUrl(src)) {
          logger.warn(`Blocked internal image fetch: ${src}`);
          delete node.props.src;
        } else {
          node.props.src = src;
          imageBuffer = await $fetch(src, {
            responseType: "arrayBuffer"
          }).catch(() => {
          });
          if (imageBuffer) {
            const buffer = imageBuffer instanceof ArrayBuffer ? imageBuffer : imageBuffer.buffer;
            node.props.src = toBase64Image(buffer);
          }
        }
      }
      if (typeof node.props.width === "string")
        node.props.width = Number(node.props.width) || void 0;
      if (typeof node.props.height === "string")
        node.props.height = Number(node.props.height) || void 0;
      if (imageBuffer && (!node.props.width || !node.props.height)) {
        dimensions = getImageDimensions(imageBuffer);
        if (dimensions?.width && dimensions?.height) {
          const naturalAspectRatio = dimensions.width / dimensions.height;
          if (node.props.width && !node.props.height) {
            node.props.height = Math.round(node.props.width / naturalAspectRatio);
          } else if (node.props.height && !node.props.width) {
            node.props.width = Math.round(node.props.height * naturalAspectRatio);
          } else if (!node.props.width && !node.props.height) {
            node.props.width = dimensions.width;
            node.props.height = dimensions.height;
          }
        }
      }
      if (typeof node.props.src === "string" && node.props.src.startsWith("/")) {
        if (imageBuffer) {
          const buffer = imageBuffer instanceof ArrayBuffer ? imageBuffer : imageBuffer.buffer;
          node.props.src = toBase64Image(buffer);
        } else {
          node.props.src = `${withBase(src, `${getNitroOrigin(e)}`)}?${Date.now()}`;
        }
      }
    }
  },
  // fix style="background-image: url('')"
  {
    filter: (node) => node.props?.style?.backgroundImage?.includes("url("),
    transform: async (node, { e, publicStoragePath, runtimeConfig }) => {
      const backgroundImage = node.props.style.backgroundImage;
      const src = backgroundImage.replace(RE_URL_LEADING, "").replace(RE_URL_TRAILING, "");
      if (src.startsWith("data:"))
        return;
      const isRelative = src?.startsWith("/");
      let imageBuffer;
      if (isRelative) {
        if (import.meta.prerender || import.meta.dev) {
          const srcWithoutBase = src.replace(runtimeConfig.app.baseURL, "/");
          imageBuffer = await resolveLocalFilePathImage(publicStoragePath, srcWithoutBase);
        }
        if (!imageBuffer) {
          imageBuffer = await e.$fetch(src, { responseType: "arrayBuffer" }).catch(() => {
          });
          if (!imageBuffer && !import.meta.prerender) {
            imageBuffer = await e.$fetch(src, {
              baseURL: getNitroOrigin(e),
              responseType: "arrayBuffer"
            }).catch(() => {
            });
          }
        }
      } else {
        const decodedSrc = decodeHtml(src);
        if (!import.meta.dev && isBlockedUrl(decodedSrc)) {
          logger.warn(`Blocked internal background-image fetch: ${decodedSrc}`);
          delete node.props.style.backgroundImage;
        } else {
          imageBuffer = await $fetch(decodedSrc, {
            responseType: "arrayBuffer"
          }).catch(() => {
          });
        }
      }
      if (imageBuffer) {
        const buffer = imageBuffer instanceof ArrayBuffer ? imageBuffer : imageBuffer.buffer;
        node.props.style.backgroundImage = `url(${toBase64Image(buffer)})`;
      }
    }
  }
]);
