<script>
  /**
   * Resolves item references (composite "html\x00xpath" strings) to full item data using htmlGroups.
   * This is needed because items are now stored as references (html keys) to reduce JSON size.
   * @param {Array} items - Array of item references (strings) or full item objects
   * @param {Object} ruleInCategory - Rule object containing htmlGroups
   * @param {Object} page - Page object containing url and pageTitle
   * @returns {Array} - Array of resolved full item objects
   */
  function resolveItemReferences(items, ruleInCategory, page) {
    if (!items || items.length === 0) return [];
    
    // Check if items are references (strings) or full objects
    // With lighter scanItems, items are strings (HTML keys)
    const isReference = typeof items[0] === 'string';
    
    if (!isReference) {
      // Items are already full objects - just add page info
      return items.map(item => ({
        ...item,
        pageUrl: page.url,
        pageTitle: page.pageTitle || page.metadata
      }));
    }
    
    // Items are references (strings) - resolve using htmlGroups
    const htmlGroups = ruleInCategory.htmlGroups || {};
    
    return items.map(compositeKey => {
      const groupData = htmlGroups[compositeKey];
      if (groupData) {
        return {
          html: groupData.html,
          xpath: groupData.xpath,
          message: groupData.message,
          screenshotPath: groupData.screenshotPath,
          displayNeedsReview: groupData.displayNeedsReview,
          pageUrl: page.url,
          pageTitle: page.pageTitle || page.metadata
        };
      }
      // Fallback: try to parse composite key
      const nullByteIndex = compositeKey.indexOf('\x00');
      const html = nullByteIndex !== -1 ? compositeKey.slice(0, nullByteIndex) : compositeKey;
      const xpath = nullByteIndex !== -1 ? compositeKey.slice(nullByteIndex + 1) : '';
      return {
        html,
        xpath,
        message: '',
        screenshotPath: '',
        pageUrl: page.url,
        pageTitle: page.pageTitle || page.metadata
      };
    });
  }

  function buildExpandedRuleCategoryContent(category, ruleInCategory) {
    const contentContainer = document.getElementById('expandedRuleCategoryContent');
    const isCustomFlow = <%- isCustomFlow -%>;

    if (category === 'passed') {
      contentContainer.innerHTML = `You may find the list of passed HTML elements in <a href='./passed_items.json' target='_blank'>passed_items.json.txt</a>.`;
      return;
    }

    setupGroupByToggle(category, ruleInCategory, isCustomFlow);

    renderGroupedByPage(category, ruleInCategory, isCustomFlow);
  }

  function setupGroupByToggle(category, ruleInCategory, isCustomFlow) {
    const groupByPage = document.getElementById('groupByPage');
    const groupByHtmlElement = document.getElementById('groupByHtmlElement');

    const newGroupByPage = groupByPage.cloneNode(true);
    const newGroupByHtmlElement = groupByHtmlElement.cloneNode(true);
    groupByPage.parentNode.replaceChild(newGroupByPage, groupByPage);
    groupByHtmlElement.parentNode.replaceChild(newGroupByHtmlElement, groupByHtmlElement);

    const updateDropdownTitle = (isHtmlGrouping) => {
      const itemsToUse = typeof filteredItems !== 'undefined' ? filteredItems : scanItems;
        const dropdownTitle = document.getElementById('expandedRuleDropdownTitle');
        if (dropdownTitle) {
          const selectedCategory = itemsToUse[category]?.rules?.find(r => r.rule === ruleInCategory.rule);
          if (selectedCategory) {
            // Use pre-computed htmlGroups for count if available, otherwise use pages
            const count = isHtmlGrouping && selectedCategory.htmlGroups
              ? Object.keys(selectedCategory.htmlGroups).length
              : (selectedCategory.pagesAffectedCount || selectedCategory.pagesAffected.length);
            if (isHtmlGrouping) {
              dropdownTitle.innerText = `HTML elements affected by this issue (${count})`;
            } else {
              dropdownTitle.innerText = `Pages affected by this issue (${count})`;
            }
          }
        }
      };


    newGroupByPage.addEventListener('change', () => {
      if (newGroupByPage.checked) {
        renderGroupedByPage(category, ruleInCategory, isCustomFlow);
        updateDropdownTitle(false);
      }
    });

    newGroupByHtmlElement.addEventListener('change', async () => {
      if (newGroupByHtmlElement.checked) {
        updateDropdownTitle(true);
        // htmlGroups are pre-computed, no loading needed
        renderGroupedByHtmlElement(category, ruleInCategory, isCustomFlow);
      }
    });
  }

  async function loadAllPageItems(ruleInCategory) {
    // If using lighter scanItems, items might be references (strings).
    // We don't necessarily need to "load" them from backend if they are already in the rule's htmlGroups.
    // However, if pagination logic existed here for fetching items, it would need adjustment.
    // Assuming current structure has all references loaded in the JSON.
    const loadPromises = ruleInCategory.pagesAffected.map(async (page) => {
      if (!page.items && page.itemsCount > 0) {
        page.items = []; // Should ideally be populated or fetched if not present
      }
    });

    await Promise.all(loadPromises);
  }

  function renderGroupedByPage(category, ruleInCategory, isCustomFlow) {
    const contentContainer = document.getElementById('expandedRuleCategoryContent');
    const accordionsList = createElementFromString(`<ul class="unbulleted-list"></ul>`);

    ruleInCategory.pagesAffected.forEach((page, index) => {
      const accordion = createPageAccordion(page, index, category, ruleInCategory, isCustomFlow);
      accordionsList.appendChild(accordion);
    });

    contentContainer.replaceChildren(accordionsList);
    hljs.highlightAll();
  }

  function renderGroupedByHtmlElement(category, ruleInCategory, isCustomFlow) {
    const contentContainer = document.getElementById('expandedRuleCategoryContent');

    // Use pre-computed htmlGroups if available, otherwise compute in real-time (backwards compatibility)
    // htmlGroups keys are composite "html\x00xpath" strings for precise matching
    let groupedByHtml = {};

    if (ruleInCategory.htmlGroups) {
      // Use pre-computed groups from JSON
      // Each entry has a unique html+xpath combination; group by html for display
      Object.entries(ruleInCategory.htmlGroups).forEach(([compositeKey, groupData]) => {
        // Use compositeKey as the grouping key to preserve xpath uniqueness
        groupedByHtml[compositeKey] = groupData.pageUrls.map(pageUrl => ({
          html: groupData.html,
          xpath: groupData.xpath,
          message: groupData.message,
          screenshotPath: groupData.screenshotPath,
          displayNeedsReview: groupData.displayNeedsReview,
          pageUrl: pageUrl,
        }));
      });
    } else {
      // Fallback: compute in real-time (for backwards compatibility or full objects)
      ruleInCategory.pagesAffected.forEach((page) => {
        if (!page.items || page.items.length === 0) return;

        const itemsToProcess = resolveItemReferences(page.items, ruleInCategory, page);

        itemsToProcess.forEach((item) => {
          // Use composite key for grouping
          const compositeKey = `${item.html || 'No HTML element'}\x00${item.xpath || ''}`;
          if (!groupedByHtml[compositeKey]) {
            groupedByHtml[compositeKey] = [];
          }
          groupedByHtml[compositeKey].push({
            ...item,
            pageUrl: page.url,
            pageTitle: page.pageTitle || page.metadata,
          });
        });
      });
    }

    if (Object.keys(groupedByHtml).length === 0) {
      contentContainer.innerHTML = `
        <div class="p-4 text-center">
          <p class="mb-0">No items available for grouping by HTML element.</p>
          <p class="text-muted small">Items may need to be loaded first by expanding pages.</p>
        </div>
      `;
      return;
    }

    const accordionsList = createElementFromString(`<ul class="unbulleted-list"></ul>`);
    let htmlIndex = 0;

    const sortedGroups = Object.entries(groupedByHtml).sort(([, itemsA], [, itemsB]) => {
      return itemsB.length - itemsA.length; // DESC
    });

    sortedGroups.forEach(([compositeKey, items]) => {
      // Extract html portion for display (strip the xpath part after \x00)
      const nullByteIndex = compositeKey.indexOf('\x00');
      const htmlElement = nullByteIndex !== -1 ? compositeKey.slice(0, nullByteIndex) : compositeKey;

      const accordion = createHtmlElementAccordion(
        htmlElement,
        items,
        htmlIndex,
        category,
        ruleInCategory,
        isCustomFlow
      );
      accordionsList.appendChild(accordion);
      htmlIndex++;
    });

    contentContainer.replaceChildren(accordionsList);
    hljs.highlightAll();
  }

  function createHtmlElementAccordion(htmlElement, items, index, category, ruleInCategory, isCustomFlow) {
    const accordionId = `${ruleInCategory.rule}-${category}-html-${index}`;
    const itemsCount = items.length;

    const htmlPreview = htmlElement === 'No HTML element' ? htmlElement : htmlElement;
    const escapedPreview = htmlEscapeString(htmlPreview);

    const accordion = createElementFromString(`
      <li>
        <div class="accordion mt-2 ${category}">
          <div class="accordion-item rule-modal-item">
            <div class="accordion-header" id="${accordionId}-title">
              <button
                aria-label="HTML element ${index + 1}, ${itemsCount} ${itemsCount === 1 ? 'occurrence' : 'occurrences'}"
                class="accordion-button collapsed"
                type="button"
              >
                <span class="sr-only visually-hidden">${itemsCount} ${itemsCount === 1 ? 'occurrence' : 'occurrences'}</span>
                <svg
                  class="accordion-arrow"
                  width="24"
                  height="24"
                  viewBox="0 0 24 24"
                  fill="none"
                  xmlns="http://www.w3.org/2000/svg"
                >
                  <path
                    d="M7.41 8.58984L12 13.1698L16.59 8.58984L18 9.99984L12 15.9998L6 9.99984L7.41 8.58984Z"
                    fill="#5735DF"
                  />
                </svg>
                <div class="me-3 flex-1">${escapedPreview}</div>
                <div class="ms-auto rule-occurrence-count counter">${itemsCount} occ.</div>
              </button>
            </div>
            <div id="${accordionId}-content" class="accordion-collapse collapse" aria-labelledby="${accordionId}-title">
              <div class="accordion-body p-3">
              </div>
            </div>
          </div>
        </div>
      </li>
    `);

    setupHtmlElementAccordionHandlers(accordion, accordionId, category, ruleInCategory, items, index);

    return accordion;
  }

  function setupHtmlElementAccordionHandlers(accordion, accordionId, category, ruleInCategory, items, index) {
    const button = accordion.querySelector('button');

    button.addEventListener('click', function loadContent(event) {
      const accordionBody = accordion.querySelector(".accordion-body");

      if (!accordionBody.querySelector(".item-pagination-controls")) {
        buildHtmlElementItemCards(accordionId, category, ruleInCategory, items, index);
        this.setAttribute('data-bs-target', '#' + accordionId + "-content");
        this.removeEventListener('click', loadContent);
        this.click();
      }
    });

    button.addEventListener('click', function initCollapse(event) {
      this.setAttribute('data-bs-toggle', 'collapse');
      this.setAttribute('data-bs-target', '#' + accordionId + "-content");
      this.setAttribute('aria-expanded', 'false');
      this.setAttribute('aria-controls', accordionId + "-content");

      new bootstrap.Collapse(this, { toggle: false });

      this.removeEventListener('click', initCollapse);
      setTimeout(() => {
        this.click();
      }, 0);
    });
  }

  function buildHtmlElementItemCards(accordionId, category, ruleInCategory, items, index) {
    const accordion = document.getElementById(`${accordionId}-title`).parentElement.parentElement.parentElement;
    const accordionBody = accordion.getElementsByClassName('accordion-body')[0];
    const totalItems = items.length;
    let currentItemIndex = 0;

    const paginationControls = createPaginationControls(totalItems);

    const itemCardContainer = createElementFromString('<div></div>');

    async function renderItem(itemIndex) {
      const item = items[itemIndex];
      const accordionAIId = `${ruleInCategory.rule}-${category}-accordion-AI-html-${index}-${itemIndex}`;
      const buttonAIId = `${ruleInCategory.rule}-${category}-button-AI-html-${index}-${itemIndex}`;
      const errorAIId = `${ruleInCategory.rule}-${category}-error-AI-html-${index}-${itemIndex}`;

      const isPurpleAiRule = oobeeAiRules.includes(ruleInCategory.rule);
      let oobeeAiQueryLabel;
      if (isPurpleAiRule) {
        oobeeAiQueryLabel = await checkPurpleAiQueryLabel(ruleInCategory.rule, item.html);
      }

      const itemCard = createHtmlGroupedItemCard(item, isPurpleAiRule, oobeeAiQueryLabel, {
        accordionDivToAppendAI: accordionAIId,
        buttonDivForAiFeedback: buttonAIId,
        aiErrorDiv: errorAIId,
        ruleInCategory,
      }, item.html);

      itemCardContainer.replaceChildren(itemCard);

      setupCopyButton(itemCard, item.xpath);

      hljs.configure({ ignoreUnescapedHTML: true });
      hljs.highlightAll();
    }

    function updatePaginationState() {
      const prevBtn = paginationControls.querySelector('.pagination-prev');
      const nextBtn = paginationControls.querySelector('.pagination-next');
      const pageInput = paginationControls.querySelector('.pagination-page-input');

      prevBtn.disabled = currentItemIndex === 0;
      nextBtn.disabled = currentItemIndex === totalItems - 1;
      pageInput.value = currentItemIndex + 1;
    }

    setupPaginationHandlers(paginationControls, totalItems, {
      onPageChange: newIndex => {
        currentItemIndex = newIndex;
        renderItem(currentItemIndex);
        updatePaginationState();
      },
    });

    accordionBody.appendChild(paginationControls);
    accordionBody.appendChild(itemCardContainer);

    renderItem(0);
  }

  function createHtmlGroupedItemCard(item, isPurpleAiRule, oobeeAiQueryLabel, aiConfig, htmlElement) {
    return createElementFromString(`
      <div>
        <div class="d-flex gap-3 flex-column">
          <h3>Where to find this issue</h3>
          ${item.screenshotPath ? createScreenshotSection(item.screenshotPath) : ''}
          ${item.xpath ? createXpathSection(item.xpath) : ''}

          <div class="d-flex justify-content-between g-one modal-view">
            <div class="fw-semibold">URL</div>
            <div class="page-item-card-section-content">
              <a href="${item.pageUrl}" target="_blank">${item.pageUrl}</a>
              <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                  <g clip-path="url(#clip0_174_1993)">
                      <path d="M12.6667 12.6667H3.33333V3.33333H8V2H3.33333C2.59333 2 2 2.6 2 3.33333V12.6667C2 13.4 2.59333 14 3.33333 14H12.6667C13.4 14 14 13.4 14 12.6667V8H12.6667V12.6667ZM9.33333 2V3.33333H11.7267L5.17333 9.88667L6.11333 10.8267L12.6667 4.27333V6.66667H14V2H9.33333Z" fill="#5735DF"/>
                  </g>
                  <defs>
                      <clipPath id="clip0_174_1993">
                          <rect width="16" height="16" fill="white"/>
                      </clipPath>
                  </defs>
              </svg>
            </div>
          </div>

        <div class="d-flex justify-content-between g-one modal-view">
            <div class="fw-semibold mb-2">HTML element</div>
            <pre class="page-item-card-section-content">
                <code class="language-html">${htmlEscapeString(htmlElement)}</code>
            </pre>
        </div>

          ${
            (() => {
              const showDetails = item.displayNeedsReview || ['color-contrast', 'color-contrast-enhanced', 'oobee-grading-text-contents', 'target-size', 'valid-lang', 'aria-valid-attr-value', 'aria-required-children', 'aria-prohibited-attr', 'aria-allowed-attr'].includes(aiConfig.ruleInCategory.rule);
              return showDetails && item.message
                ? `<div class="d-flex justify-content-between g-one modal-view">
            <div class="fw-semibold page-item-card-section-title">Details</div>
            <div class="page-item-card-section-content">
              ${generateItemMessageElement(item.displayNeedsReview || true, item.message)}
            </div>
          </div>`
                : '';
            })()
          }
          ${isPurpleAiRule ? createAiSuggestionSection(item, oobeeAiQueryLabel, aiConfig) : ''}
        </div>
      </div>
    `);
  }

  function createPageAccordion(page, index, category, ruleInCategory, isCustomFlow) {
    const accordionId = `${ruleInCategory.rule}-${category}-page-${index}`;

    // When htmlGroups is present, the accordian is expandable
    const normalMode = ruleInCategory.htmlGroups;

    // Calculation for items count: use items.length if array, else itemsCount property
    const pageItemsCount = (page.items && page.items.length) || page.itemsCount || 0;

    const accordion = createElementFromString(`
      <li class="${normalMode ? '' : 'no-chevron'}">
        <div class="accordion mt-2 ${category}">
          <div class="accordion-item rule-modal-item">
            <div class="accordion-header" id="${accordionId}-title">
              ${normalMode ? '<button' : '<div'}
                aria-label="Page ${index + 1}: ${page.pageTitle}, ${pageItemsCount} ${pageItemsCount === 1 ? 'occurrence' : 'occurrences'}"
                class="accordion-button collapsed"
                ${normalMode ? 'type="button"' : ''}
              >
                <span class="sr-only visually-hidden">${pageItemsCount} ${pageItemsCount === 1 ? 'occurrence' : 'occurrences'}</span>
                <svg
                  class="accordion-arrow"
                  width="24"
                  height="24"
                  viewBox="0 0 24 24"
                  fill="none"
                  xmlns="http://www.w3.org/2000/svg"
                >
                  <path
                    d="M7.41 8.58984L12 13.1698L16.59 8.58984L18 9.99984L12 15.9998L6 9.99984L7.41 8.58984Z"
                    fill="#5735DF"
                  />
                </svg>
                <div class="me-3 flex-1">${page.metadata ? page.metadata : page.pageTitle}</div>
                <div class="ms-auto rule-occurrence-count counter">${pageItemsCount} occ.</div>
              ${normalMode ? '</button>' : '</div>'}
            </div>
            <div id="${accordionId}-content" class="accordion-collapse collapse" aria-labelledby="${accordionId}-title">
              <div class="accordion-body p-3">
                ${isCustomFlow
                  ? createCustomFlowContent(page, ruleInCategory)
                  :
                  `<div class="d-flex align-items-center gap-1 accordion-link" style="    word-wrap: break-word; word-break: break-word;">
                    <a href="${page.url}" target="_blank">${page.url}
                      <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" style="margin-left: 0.25rem;">
                        <g clip-path="url(#clip0_174_1993)">
                            <path d="M12.6667 12.6667H3.33333V3.33333H8V2H3.33333C2.59333 2 2 2.6 2 3.33333V12.6667C2 13.4 2.59333 14 3.33333 14H12.6667C13.4 14 14 13.4 14 12.6667V8H12.6667V12.6667ZM9.33333 2V3.33333H11.7267L5.17333 9.88667L6.11333 10.8267L12.6667 4.27333V6.66667H14V2H9.33333Z" fill="#5735DF"/>
                        </g>
                        <defs>
                            <clipPath id="clip0_174_1993">
                                <rect width="16" height="16" fill="white"/>
                            </clipPath>
                        </defs>
                    </svg>
                    </a>
                  </div>`
                }
              </div>
            </div>
          </div>
        </div>
      </li>
    `);

    if (normalMode) {
      setupPageAccordionHandlers(accordion, accordionId, category, ruleInCategory, page, index);
    }

    if (isCustomFlow) {
      setupCustomFlowScreenshot(accordion, page);
    }

    return accordion;
  }

  function getCustomFlowPageScreenshot(page, ruleInCategory) {
    if (page && page.pageImagePath) return page.pageImagePath;

    // Use same source-of-truth as scanAboutModal.
    const scannedPages =
      typeof scanData !== 'undefined' &&
      scanData &&
      Array.isArray(scanData.pagesScanned)
        ? scanData.pagesScanned
        : [];

    if (page) {
      const matchByUrl = scannedPages.find((p) => p && p.url === page.url && p.pageImagePath);
      if (matchByUrl) return matchByUrl.pageImagePath;

      const matchByTitle = scannedPages.find(
        (p) => p && p.pageTitle === page.pageTitle && p.pageImagePath,
      );
      if (matchByTitle) return matchByTitle.pageImagePath;
    }

    return '';
  }

  function createCustomFlowContent(page, ruleInCategory) {
    const pageScreenshot = getCustomFlowPageScreenshot(page, ruleInCategory);

    return `
      <div class="custom-flow-screenshot-container">
          <div class="custom-flow-thumb">
            ${
              pageScreenshot
                ? `<img
                    src="${pageScreenshot}"
                    alt="Screenshot of ${page.url}"
                    class="custom-flow-screenshot"
                    onerror="this.onerror = null; this.remove();"
                  >`
                : ''
            }
          </div>
          <div class="display-url-container">
            <div><a href="${page.url}" target="_blank">${page.url}</a></div>
          </div>
        </div>
    `;
  }

  function setupPageAccordionHandlers(accordion, accordionId, category, ruleInCategory, page, index) {
    const button = accordion.querySelector('button');

    // Handler for the first click to load content and init collapse
    const initAndLoad = function(event) {
      const accordionBody = accordion.querySelector(".accordion-body");

      // Build content if missing
      if (!accordionBody.querySelector(".item-pagination-controls") &&
          !accordionBody.querySelector(".unbulleted-list")) {
        buildItemCardsWithPagination(accordionId, category, ruleInCategory, page, index);
      }

      // Initialize Bootstrap Collapse
      // This sets up the data attributes and creates the instance
      this.setAttribute('data-bs-toggle', 'collapse');
      this.setAttribute('data-bs-target', '#' + accordionId + "-content");
      this.setAttribute('aria-expanded', 'true'); 
      this.setAttribute('aria-controls', accordionId + "-content");

      // Create and toggle the collapse instance manually for this first interaction
      const collapseElement = document.getElementById(accordionId + "-content");
      const bsCollapse = new bootstrap.Collapse(collapseElement, { toggle: false });
      bsCollapse.toggle();

      // Remove this listener so subsequent clicks are handled by Bootstrap's data-api
      button.removeEventListener('click', initAndLoad);
    };

    button.addEventListener('click', initAndLoad);
  }

  function setupCustomFlowScreenshot(accordion, page) {
    const customScreenshotElem = accordion.getElementsByClassName('custom-flow-screenshot')[0];
    if (!customScreenshotElem) return;

    customScreenshotElem.onerror = function(event) {
      this.onerror = null;
      this.remove();
    };

    customScreenshotElem.onclick = function(event) {
      event.preventDefault();
      openLightbox(this.src, page.pageTitle, page.url);
    };
  }
</script>
