"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var sitemap_exports = {};
__export(sitemap_exports, {
  default: () => sitemap_default
});
module.exports = __toCommonJS(sitemap_exports);
var crypto = __toESM(require("crypto"));
var fs = __toESM(require("fs"));
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
var import_tool = require("./tool");
var import_crawler = require("../../../dom-analyzer/crawler");
var import_blueprint = require("../../../dom-analyzer/blueprint");
var import_serialization = require("../../../dom-analyzer/serialization");
function hashStorageState(path) {
  if (!path)
    return null;
  try {
    const contents = fs.readFileSync(path, "utf-8");
    return crypto.createHash("sha256").update(contents).digest("hex").slice(0, 16);
  } catch {
    const pathHash = crypto.createHash("sha256").update(path).digest("hex").slice(0, 16);
    return `unreadable:${pathHash}`;
  }
}
function cacheKey(entryUrl, storageStateHash, probeButtons) {
  return `${entryUrl}::${storageStateHash ?? "no-auth"}::${probeButtons}`;
}
function humanDuration(ms) {
  if (ms < 6e4)
    return `${Math.round(ms / 1e3)}s`;
  return `${Math.round(ms / 6e4)}m`;
}
const sitemapBuild = (0, import_tool.defineTool)({
  capability: "core",
  schema: {
    name: "browser_sitemap_build",
    title: "Build Sitemap (graph of PageBlueprints)",
    description: [
      "Crawl an application starting from a URL and build a Sitemap \u2014 a graph of PageBlueprints (one per URL)",
      "connected by navigation edges. The Sitemap is cached in the session.",
      "",
      "Call once at the start of a session. Subsequent calls within the TTL (~30 minutes) reuse the cached",
      "Sitemap unless `refresh: true` is passed. Use `browser_sitemap_query` to read already-crawled pages",
      "rather than re-calling this tool.",
      "",
      "Depth defaults to 5, maxPages to 50 (the real bound in practice).",
      "",
      '\u26A0\uFE0F SAFETY: By default, `probeButtons: "immutable-only"` skips destructive-looking buttons',
      "(Delete / Submit / Place Order / etc.) and buttons inside forms to prevent side effects during",
      'crawling. Use `probeButtons: "all"` only against dev / staging environments \u2014 it will click every',
      "unique button, which can submit forms, create records, send notifications, or mutate server state."
    ].join("\n"),
    inputSchema: import_mcpBundle.z.object({
      url: import_mcpBundle.z.string().describe("Entry URL to start crawling from"),
      depth: import_mcpBundle.z.number().optional().default(5).describe("Max crawl depth (default: 5)"),
      maxPages: import_mcpBundle.z.number().optional().default(50).describe("Max pages to visit (default: 50)"),
      sameOriginOnly: import_mcpBundle.z.boolean().optional().default(true).describe("Only follow same-origin links (default: true)"),
      probeButtons: import_mcpBundle.z.enum(["immutable-only", "all", "none"]).optional().default("immutable-only").describe('Button-probing safety mode. Default: immutable-only (safe; skips destructive-verb buttons and buttons inside forms). Use "all" only against dev/staging \u2014 probes every button. "none" disables button probing entirely.'),
      playwrightStoragePath: import_mcpBundle.z.string().optional().describe("Path to a Playwright storageState.json file \u2014 cookies, localStorage, sessionStorage per origin. Use this to crawl apps behind a login."),
      refresh: import_mcpBundle.z.boolean().optional().default(false).describe("Force a full re-crawl, bypassing the cache")
    }),
    type: "readOnly"
  },
  handle: async (context, params, response) => {
    const { url, depth, maxPages, sameOriginOnly, probeButtons, playwrightStoragePath, refresh } = params;
    const normalizedUrl = (0, import_serialization.normalizeUrl)(url);
    const storageStateHash = hashStorageState(playwrightStoragePath);
    const probeMode = probeButtons;
    const key = cacheKey(normalizedUrl, storageStateHash, probeMode);
    const cached = context.sitemapCache.get(key);
    if (cached && !refresh) {
      const age = Date.now() - new Date(cached.sitemap.cachedAt).getTime();
      if (age < import_serialization.CRAWL_TTL_MS) {
        const pageCount2 = Object.keys(cached.sitemap.pages).length;
        response.addTextResult(
          `Reusing cached Sitemap from ${humanDuration(age)} ago.
Entry: ${cached.sitemap.entryUrl}
Pages: ${pageCount2}
Edges: ${cached.sitemap.edges.length}
Storage state: ${storageStateHash ? `auth hash ${storageStateHash}` : "none"}
Probe mode: ${probeMode}

Use browser_sitemap_query to read any page. Pass refresh=true to recrawl.`
        );
        return;
      }
    }
    let sitemap;
    if (depth > 0) {
      sitemap = await (0, import_crawler.crawl)(url, {
        depth,
        maxPages,
        sameOriginOnly,
        probeButtons: probeMode,
        playwrightStoragePath
      });
    } else {
      sitemap = await (0, import_crawler.crawlSinglePage)(url, { playwrightStoragePath });
    }
    context.sitemapCache.set(key, { sitemap, storageStateHash, probeButtonsMode: probeMode });
    const pageCount = Object.keys(sitemap.pages).length;
    const pageList = Object.keys(sitemap.pages).map((u) => {
      const bp = sitemap.pages[u];
      const elCount = bp.sections.reduce((sum, s) => sum + s.elements.length + s.repeatingElements.length, 0);
      return `  - ${u} (${elCount} elements across ${bp.sections.length} sections)`;
    }).join("\n");
    response.addTextResult(
      `Sitemap built for ${sitemap.entryUrl}
Pages: ${pageCount}
Edges: ${sitemap.edges.length}
Storage state: ${storageStateHash ? `auth hash ${storageStateHash}` : "none"}
Probe mode: ${probeMode}

Pages:
${pageList}

Use browser_sitemap_query to read page blueprints, edges, or derived views (mapJson, outline).`
    );
  }
});
const sitemapQuery = (0, import_tool.defineTool)({
  capability: "core",
  schema: {
    name: "browser_sitemap_query",
    title: "Query the cached Sitemap",
    description: [
      "Query the Sitemap cached by browser_sitemap_build. Use this instead of re-calling sitemap_build.",
      "",
      "Modes:",
      "  page     \u2014 returns the full canonical PageBlueprint for the given URL",
      "             (sections with enrichment, logical names, XPaths)",
      "  edges    \u2014 returns navigation edges originating from the given URL",
      "  mapJson  \u2014 returns the derived flat logicalName \u2192 xpath map for the URL",
      "  outline  \u2014 returns the derived textual section-to-element hierarchy for the URL",
      "  overview \u2014 (when url is omitted) returns page list + edge list summary"
    ].join("\n"),
    inputSchema: import_mcpBundle.z.object({
      type: import_mcpBundle.z.enum(["page", "edges", "mapJson", "outline"]).optional().describe("Query mode. Omit with url to get overview."),
      url: import_mcpBundle.z.string().optional().describe("Page URL to query. Omit to get overview across all pages.")
    }),
    type: "readOnly"
  },
  handle: async (context, params, response) => {
    if (context.sitemapCache.size === 0) {
      response.addError("No Sitemap available. Run browser_sitemap_build first.");
      return;
    }
    const latest = [...context.sitemapCache.values()].sort(
      (a, b) => new Date(b.sitemap.cachedAt).getTime() - new Date(a.sitemap.cachedAt).getTime()
    )[0];
    const sitemap = latest.sitemap;
    const { type, url } = params;
    if (!url || !type) {
      const overview = {
        schemaVersion: sitemap.schemaVersion,
        entryUrl: sitemap.entryUrl,
        cachedAt: sitemap.cachedAt,
        pages: Object.keys(sitemap.pages).map((u) => ({
          url: u,
          sections: sitemap.pages[u].sections.length,
          elements: sitemap.pages[u].sections.reduce(
            (s, sec) => s + sec.elements.length + sec.repeatingElements.length,
            0
          ),
          pageHash: sitemap.pages[u].pageHash
        })),
        edges: sitemap.edges
      };
      response.addTextResult(JSON.stringify(overview, null, 2));
      return;
    }
    const normalizedQueryUrl = (0, import_serialization.normalizeUrl)(url);
    const blueprint = sitemap.pages[normalizedQueryUrl];
    if (!blueprint) {
      response.addError(
        `Page not found in Sitemap: ${normalizedQueryUrl}. Available pages: ${Object.keys(sitemap.pages).join(", ")}`
      );
      return;
    }
    if (type === "page") {
      response.addTextResult(JSON.stringify(blueprint, null, 2));
      return;
    }
    if (type === "edges") {
      const edges = sitemap.edges.filter((e) => e.from === normalizedQueryUrl);
      response.addTextResult(JSON.stringify({ url: normalizedQueryUrl, edges }, null, 2));
      return;
    }
    if (type === "mapJson") {
      response.addTextResult(JSON.stringify((0, import_blueprint.buildMap)(blueprint), null, 2));
      return;
    }
    if (type === "outline") {
      response.addTextResult((0, import_blueprint.buildOutline)(blueprint));
      return;
    }
  }
});
var sitemap_default = [sitemapBuild, sitemapQuery];
