import { execFile } from 'node:child_process';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { promisify } from 'node:util';

// src/platform/request-security/resource-resolver.ts
var execFileAsync = promisify(execFile);
var MAX_REFERENCED_RESOURCES = 16;
var MAX_TEXT_LENGTH = 12e4;
var MAX_FILE_BUFFER = 1024 * 1024 * 4;
var MAX_REMOTE_BUFFER = 1024 * 1024 * 5;
var COMMAND_TIMEOUT_MS = 15e3;
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
  ".cjs",
  ".conf",
  ".css",
  ".csv",
  ".d.ts",
  ".env",
  ".html",
  ".htm",
  ".ini",
  ".java",
  ".js",
  ".json",
  ".jsx",
  ".kt",
  ".less",
  ".log",
  ".md",
  ".mjs",
  ".php",
  ".py",
  ".rb",
  ".rs",
  ".scss",
  ".sh",
  ".sql",
  ".svg",
  ".swift",
  ".ts",
  ".tsx",
  ".txt",
  ".xml",
  ".yaml",
  ".yml",
  ".zsh"
]);
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".bmp", ".gif", ".heic", ".jpeg", ".jpg", ".png", ".tif", ".tiff", ".webp"]);
var TEXTUTIL_EXTENSIONS = /* @__PURE__ */ new Set([".doc", ".docx", ".odt", ".rtf", ".rtfd", ".webarchive"]);
var REMOTE_URL_PATTERN = /\bhttps?:\/\/[^\s<>()"'`]+/gi;
var FILE_URL_PATTERN = /\bfile:\/\/[^\s<>()"'`]+/gi;
var LOCAL_PATH_WITH_PREFIX_PATTERN = /(?:^|[\s("'`])((?:~|\.{1,2}\/|\/)[^\s<>()"'`]+)/g;
var RELATIVE_PATH_WITH_SEPARATOR_PATTERN = /(?:^|[\s("'`])((?:[A-Za-z0-9._-]+\/)+[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+)/g;
function trimTrailingPunctuation(value) {
  return value.replace(/[),.;:!?`'"\]}]+$/g, "").trim();
}
function limitTextContent(value) {
  if (!value) {
    return "";
  }
  const normalized = value.replace(/\0/g, "").trim();
  if (normalized.length <= MAX_TEXT_LENGTH) {
    return normalized;
  }
  return normalized.slice(0, MAX_TEXT_LENGTH);
}
function isLikelyTextMimeType(mimeType) {
  return mimeType.startsWith("text/") || mimeType.includes("json") || mimeType.includes("javascript") || mimeType.includes("xml") || mimeType.includes("yaml") || mimeType.includes("svg");
}
function isLikelyImageMimeType(mimeType) {
  return mimeType.startsWith("image/");
}
function collectMatches(text, pattern) {
  const values = [];
  for (const match of text.matchAll(pattern)) {
    const candidate = trimTrailingPunctuation((match[1] || match[0] || "").trim());
    if (!candidate) {
      continue;
    }
    values.push(candidate);
  }
  return values;
}
function resolveLocalResource(value, rootDir) {
  try {
    if (value.startsWith("file://")) {
      return path.normalize(fileURLToPath(value));
    }
  } catch {
    return void 0;
  }
  if (value.startsWith("~")) {
    return path.normalize(path.join(os.homedir(), value.slice(1)));
  }
  if (path.isAbsolute(value)) {
    return path.normalize(value);
  }
  if (!rootDir) {
    return void 0;
  }
  return path.normalize(path.resolve(rootDir, value));
}
function toReferencedResources(values, rootDir) {
  const seen = /* @__PURE__ */ new Set();
  const resources = [];
  for (const value of values) {
    for (const candidate of collectMatches(value, REMOTE_URL_PATTERN)) {
      const normalized = trimTrailingPunctuation(candidate);
      if (!normalized || seen.has(normalized)) {
        continue;
      }
      seen.add(normalized);
      resources.push({
        kind: "remote-url",
        location: normalized
      });
    }
    for (const candidate of [...collectMatches(value, FILE_URL_PATTERN), ...collectMatches(value, LOCAL_PATH_WITH_PREFIX_PATTERN), ...collectMatches(value, RELATIVE_PATH_WITH_SEPARATOR_PATTERN)]) {
      const resolved = resolveLocalResource(candidate, rootDir);
      if (!resolved || seen.has(resolved)) {
        continue;
      }
      seen.add(resolved);
      resources.push({
        kind: "local-file",
        location: resolved
      });
    }
    if (resources.length >= MAX_REFERENCED_RESOURCES) {
      return resources.slice(0, MAX_REFERENCED_RESOURCES);
    }
  }
  return resources;
}
async function detectLocalMimeType(filePath) {
  try {
    const { stdout } = await execFileAsync("file", ["--brief", "--mime-type", filePath], {
      maxBuffer: 64 * 1024,
      timeout: COMMAND_TIMEOUT_MS
    });
    return stdout.trim().toLowerCase();
  } catch {
    return "";
  }
}
async function readTextFile(filePath) {
  try {
    const stat = await fs.stat(filePath);
    if (stat.size > MAX_FILE_BUFFER) {
      const handle = await fs.open(filePath, "r");
      try {
        const buffer2 = Buffer.alloc(MAX_FILE_BUFFER);
        await handle.read(buffer2, 0, MAX_FILE_BUFFER, 0);
        return limitTextContent(buffer2.toString("utf8"));
      } finally {
        await handle.close();
      }
    }
    const buffer = await fs.readFile(filePath);
    return limitTextContent(buffer.toString("utf8"));
  } catch {
    return "";
  }
}
async function readTextViaTextutil(filePath) {
  try {
    const { stdout } = await execFileAsync("textutil", ["-convert", "txt", "-stdout", filePath], {
      maxBuffer: MAX_FILE_BUFFER,
      timeout: COMMAND_TIMEOUT_MS
    });
    return limitTextContent(stdout);
  } catch {
    return "";
  }
}
async function readTextViaMetadata(filePath) {
  try {
    const { stdout } = await execFileAsync("mdls", ["-raw", "-name", "kMDItemTextContent", filePath], {
      maxBuffer: MAX_FILE_BUFFER,
      timeout: COMMAND_TIMEOUT_MS
    });
    const normalized = stdout.trim();
    if (!normalized || normalized === "(null)") {
      return "";
    }
    return limitTextContent(normalized);
  } catch {
    return "";
  }
}
async function readTextViaStrings(filePath) {
  try {
    const { stdout } = await execFileAsync("strings", ["-n", "6", filePath], {
      maxBuffer: MAX_FILE_BUFFER,
      timeout: COMMAND_TIMEOUT_MS
    });
    return limitTextContent(stdout);
  } catch {
    return "";
  }
}
async function readTextViaTesseract(filePath) {
  try {
    const { stdout } = await execFileAsync("tesseract", [filePath, "stdout", "--psm", "6"], {
      maxBuffer: MAX_FILE_BUFFER,
      timeout: COMMAND_TIMEOUT_MS
    });
    return limitTextContent(stdout);
  } catch {
    return "";
  }
}
async function readLocalResourceText(filePath) {
  try {
    const stats = await fs.stat(filePath);
    if (!stats.isFile()) {
      return "";
    }
  } catch {
    return "";
  }
  const extension = path.extname(filePath).toLowerCase();
  const mimeType = await detectLocalMimeType(filePath);
  if (IMAGE_EXTENSIONS.has(extension) || isLikelyImageMimeType(mimeType)) {
    return await readTextViaTesseract(filePath) || await readTextViaMetadata(filePath);
  }
  if (TEXT_EXTENSIONS.has(extension) || isLikelyTextMimeType(mimeType)) {
    return readTextFile(filePath);
  }
  if (TEXTUTIL_EXTENSIONS.has(extension)) {
    return await readTextViaTextutil(filePath) || await readTextViaMetadata(filePath);
  }
  return await readTextViaMetadata(filePath) || await readTextViaStrings(filePath);
}
async function readRemoteBuffer(location) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), COMMAND_TIMEOUT_MS);
  try {
    const response = await fetch(location, {
      redirect: "follow",
      signal: controller.signal
    });
    if (!response.ok) {
      return void 0;
    }
    const contentLength = Number(response.headers.get("content-length") || "0");
    if (contentLength > MAX_REMOTE_BUFFER) {
      return void 0;
    }
    const arrayBuffer = await response.arrayBuffer();
    const buffer = Buffer.from(arrayBuffer);
    if (buffer.byteLength > MAX_REMOTE_BUFFER) {
      return void 0;
    }
    return {
      buffer,
      contentType: (response.headers.get("content-type") || "").toLowerCase()
    };
  } catch {
    return void 0;
  } finally {
    clearTimeout(timer);
  }
}
async function withTemporaryFile(extension, buffer, task) {
  const tempPath = path.join(os.tmpdir(), `ju-hong-e-security-${Date.now()}-${Math.random().toString(16).slice(2)}${extension}`);
  try {
    await fs.writeFile(tempPath, buffer);
    return await task(tempPath);
  } finally {
    await fs.rm(tempPath, { force: true });
  }
}
async function readRemoteResourceText(location) {
  const response = await readRemoteBuffer(location);
  if (!response) {
    return "";
  }
  let extension = "";
  try {
    extension = path.extname(new URL(location).pathname).toLowerCase();
  } catch {
    extension = "";
  }
  if (IMAGE_EXTENSIONS.has(extension) || isLikelyImageMimeType(response.contentType)) {
    return withTemporaryFile(extension || ".img", response.buffer, readTextViaTesseract);
  }
  if (TEXT_EXTENSIONS.has(extension) || isLikelyTextMimeType(response.contentType)) {
    return limitTextContent(response.buffer.toString("utf8"));
  }
  if (TEXTUTIL_EXTENSIONS.has(extension)) {
    return withTemporaryFile(extension, response.buffer, async (filePath) => await readTextViaTextutil(filePath) || await readTextViaMetadata(filePath));
  }
  return "";
}
async function collectReferencedResourceContents(values, rootDir) {
  const resources = toReferencedResources(values, rootDir);
  const contents = [];
  for (const resource of resources) {
    const content = resource.kind === "local-file" ? await readLocalResourceText(resource.location) : await readRemoteResourceText(resource.location);
    if (!content) {
      continue;
    }
    contents.push({
      location: resource.location,
      value: content
    });
  }
  return contents;
}

export { collectReferencedResourceContents };
