import type { DevToolbarRule } from "./types.ts";
import { createIssue } from "./helpers.ts";

export const performanceRules: DevToolbarRule[] = [
  {
    id: "performance/resources",
    category: "performance",
    defaultSeverity: "warning",
    description: "Checks resource, DOM, hydration, and main-thread budgets.",
    run: ({ document, root, performanceEntries }) => {
      const resources = performanceEntries.filter(
        (entry): entry is PerformanceResourceTiming => entry.entryType === "resource",
      );
      const issues = [];
      if (resources.length > 150) {
        issues.push(
          createIssue({
            ruleId: "performance/resource-count",
            category: "performance",
            severity: "warning",
            title: "Page loads many resources",
            message: `Found ${resources.length} resource requests.`,
            recommendation:
              "Remove duplicates, combine tiny assets where useful, and load non-critical resources later.",
          }),
        );
      }
      const total = resources.reduce((sum, entry) => sum + (entry.transferSize || 0), 0);
      if (total > 5_000_000) {
        issues.push(
          createIssue({
            ruleId: "performance/transfer-size",
            category: "performance",
            severity: total > 10_000_000 ? "error" : "warning",
            title: "Page transfer size is large",
            message: `Observed transfer size is approximately ${(total / 1_000_000).toFixed(2)} MB.`,
            recommendation:
              "Compress images, scripts, styles and fonts; review third-party resources.",
          }),
        );
      }

      const javascriptBytes = resources
        .filter((entry) => /(?:\.m?js)(?:\?|$)/i.test(entry.name))
        .reduce((sum, entry) => sum + (entry.transferSize || 0), 0);
      if (javascriptBytes > 150_000) {
        issues.push(
          createIssue({
            ruleId: "performance/javascript-budget",
            category: "javascript",
            severity: javascriptBytes > 300_000 ? "error" : "warning",
            title: "JavaScript budget exceeded",
            message: `JavaScript transfer is approximately ${(javascriptBytes / 1_000).toFixed(1)} KB.`,
            recommendation:
              "Split routes, remove unused client code, and defer optional hydration.",
          }),
        );
      }

      for (const entry of resources.filter((item) => item.duration > 2_000).slice(0, 20)) {
        issues.push(
          createIssue({
            ruleId: "performance/slow-resource",
            category: "network",
            severity: "warning",
            title: "Resource loaded slowly",
            message: `${entry.name} took ${Math.round(entry.duration)} ms.`,
            recommendation: "Inspect server timing, caching and payload size.",
            metadata: { url: entry.name, duration: entry.duration },
          }),
        );
      }

      const domNodes = root.querySelectorAll("*").length;
      if (domNodes > 1_500) {
        issues.push(
          createIssue({
            ruleId: "performance/dom-size",
            category: "performance",
            severity: domNodes > 3_000 ? "error" : "warning",
            title: "DOM is large",
            message: `The page contains ${domNodes} elements.`,
            recommendation:
              "Virtualize long lists and avoid rendering hidden or duplicate structures.",
          }),
        );
      }

      const hydrationRoots = root.querySelectorAll("[data-wrn-client-module], [data-wrn-hydrate]");
      if (hydrationRoots.length > 50) {
        issues.push(
          createIssue({
            ruleId: "performance/hydration-count",
            category: "runtime",
            severity: "warning",
            title: "Many components hydrate",
            message: `${hydrationRoots.length} hydration boundaries were found.`,
            recommendation:
              "Use visible, idle, or interaction hydration and keep static components server-only.",
          }),
        );
      }

      for (const image of root.querySelectorAll<HTMLImageElement>("img")) {
        if (!image.complete || !image.naturalWidth) continue;
        const renderedWidth = Math.max(1, image.getBoundingClientRect().width);
        if (image.naturalWidth > renderedWidth * 2.5) {
          issues.push(
            createIssue({
              ruleId: "performance/oversized-image",
              category: "images",
              severity: "warning",
              title: "Image is larger than rendered size",
              message: `${image.naturalWidth}px image is rendered at about ${Math.round(renderedWidth)}px.`,
              element: image,
              recommendation: "Generate responsive srcset candidates and accurate sizes.",
            }),
          );
        }
      }

      const blocking = document.querySelectorAll(
        'head script:not([async]):not([defer]):not([type="module"]), head link[rel="stylesheet"]:not([media])',
      );
      if (blocking.length > 4) {
        issues.push(
          createIssue({
            ruleId: "performance/render-blocking",
            category: "performance",
            severity: "warning",
            title: "Multiple render-blocking resources",
            message: `${blocking.length} potentially render-blocking resources were found.`,
            recommendation: "Inline only critical CSS and defer non-critical scripts and styles.",
          }),
        );
      }

      const longTasks = performanceEntries.filter(
        (entry) => entry.entryType === "longtask" && entry.duration > 50,
      );
      if (longTasks.length) {
        issues.push(
          createIssue({
            ruleId: "performance/long-tasks",
            category: "javascript",
            severity: "warning",
            title: "Long main-thread tasks detected",
            message: `${longTasks.length} task(s) exceeded 50 ms.`,
            recommendation:
              "Split expensive work, reduce hydration, and move non-UI work off the main thread.",
            metadata: { longestMs: Math.max(...longTasks.map((entry) => entry.duration)) },
          }),
        );
      }

      return issues;
    },
  },
];
