<script setup>
import PagedJsWrapper from './components/PagedJsWrapper.vue';
import EditorPanel from './components/editor/EditorPanel.vue';
import ElementPopup from './components/ElementPopup.vue';
import PagePopup from './components/PagePopup.vue';
import PreviewLoader from './components/PreviewLoader.vue';
import { onMounted, onUnmounted, ref, watch, computed, provide } from 'vue';
import { useStylesheetStore } from './stores/stylesheet';
import Coloris from '@melloware/coloris';

const stylesheetStore = useStylesheetStore();
const previewFrame1 = ref(null);
const previewFrame2 = ref(null);
const elementPopup = ref(null);
const pagePopup = ref(null);
const activeTab = ref('');

provide('activeTab', activeTab);

// Page interaction state
const hoveredPage = ref(null);
const selectedPages = ref([]); // Pages with active border (when popup is open)
const EDGE_THRESHOLD = 30; // px from edge to trigger hover
const PAGE_HIGHLIGHT_COLOR = '#ff8a50';

let savedScrollPercentage = 0;
const currentFrameIndex = ref(1); // 1 or 2, which iframe is currently visible
const isTransitioning = ref(false);

const activeFrame = computed(() => {
  return currentFrameIndex.value === 1
    ? previewFrame1.value
    : previewFrame2.value;
});

// Check if mouse position is near the edges of a page element
const isNearPageEdge = (pageElement, mouseX, mouseY) => {
  const rect = pageElement.getBoundingClientRect();

  const nearLeft = mouseX >= rect.left && mouseX <= rect.left + EDGE_THRESHOLD;
  const nearRight = mouseX >= rect.right - EDGE_THRESHOLD && mouseX <= rect.right;
  const nearTop = mouseY >= rect.top && mouseY <= rect.top + EDGE_THRESHOLD;
  const nearBottom = mouseY >= rect.bottom - EDGE_THRESHOLD && mouseY <= rect.bottom;

  const inHorizontalRange = mouseY >= rect.top && mouseY <= rect.bottom;
  const inVerticalRange = mouseX >= rect.left && mouseX <= rect.right;

  return (
    (nearLeft && inHorizontalRange) ||
    (nearRight && inHorizontalRange) ||
    (nearTop && inVerticalRange) ||
    (nearBottom && inVerticalRange)
  );
};

// Get all pages using the same template as the given page
const getPagesWithSameTemplate = (page, doc) => {
  const pageType = page.getAttribute('data-page-type') || 'default';
  const allPages = doc.querySelectorAll('.pagedjs_page');
  return Array.from(allPages).filter(
    (p) => (p.getAttribute('data-page-type') || 'default') === pageType
  );
};

// Handle mouse movement in iframe
const handleIframeMouseMove = (event) => {
  const pages = event.target.ownerDocument.querySelectorAll('.pagedjs_page');
  let foundPage = null;

  for (const page of pages) {
    if (isNearPageEdge(page, event.clientX, event.clientY)) {
      foundPage = page;
      break;
    }
  }

  // Update hover state
  if (foundPage !== hoveredPage.value) {
    // Remove highlight from previous page (only if not in selectedPages)
    if (hoveredPage.value && !selectedPages.value.includes(hoveredPage.value)) {
      hoveredPage.value.style.outline = '';
    }

    // Add highlight to new page (only if not already selected)
    if (foundPage && !selectedPages.value.includes(foundPage)) {
      foundPage.style.outline = `2px solid ${PAGE_HIGHLIGHT_COLOR}50`;
    }

    hoveredPage.value = foundPage;
  }
};

// Clear selection highlight from all selected pages
const clearSelectedPages = () => {
  selectedPages.value.forEach((page) => {
    page.style.outline = '';
  });
  selectedPages.value = [];
};

// Content elements that can trigger ElementPopup
const CONTENT_ELEMENTS = [
  'P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6',
  'IMG', 'FIGURE', 'FIGCAPTION',
  'UL', 'OL', 'LI',
  'BLOCKQUOTE', 'PRE', 'CODE',
  'TABLE', 'THEAD', 'TBODY', 'TR', 'TH', 'TD',
  'A', 'SPAN', 'STRONG', 'EM', 'B', 'I', 'U',
  'ARTICLE', 'SECTION', 'ASIDE', 'HEADER', 'FOOTER', 'NAV'
];

// Check if element is a content element (or find closest content parent)
const getContentElement = (element) => {
  let current = element;
  while (current && current.tagName !== 'BODY') {
    if (CONTENT_ELEMENTS.includes(current.tagName)) {
      return current;
    }
    current = current.parentElement;
  }
  return null;
};

// Handle click in iframe
const handleIframeClick = (event) => {
  const element = event.target;

  // Check if clicking near a page edge
  if (hoveredPage.value) {
    event.stopPropagation();

    // Clear previous selection
    clearSelectedPages();

    // Get all pages with same template and highlight them
    const doc = event.target.ownerDocument;
    const sameTemplatePages = getPagesWithSameTemplate(hoveredPage.value, doc);
    sameTemplatePages.forEach((page) => {
      page.style.outline = `2px solid ${PAGE_HIGHLIGHT_COLOR}`;
    });
    selectedPages.value = sameTemplatePages;

    pagePopup.value.open(hoveredPage.value, event, sameTemplatePages.length);
    elementPopup.value.close();
    return;
  }

  // Only show popup for elements inside the page template
  const isInsidePage = element.closest('.pagedjs_page');
  if (!isInsidePage) {
    clearSelectedPages();
    elementPopup.value.close();
    pagePopup.value.close();
    return;
  }

  // Only show ElementPopup for content elements, not divs
  const contentElement = getContentElement(element);
  if (!contentElement) {
    clearSelectedPages();
    elementPopup.value.close();
    pagePopup.value.close();
    return;
  }

  clearSelectedPages();
  elementPopup.value.handleIframeClick(event, contentElement);
  pagePopup.value.close();
};

// Expose clearSelectedPages for PagePopup to call when closing
const handlePagePopupClose = () => {
  clearSelectedPages();
};

const renderPreview = async (shouldReloadFromFile = false) => {
  if (isTransitioning.value) return;
  isTransitioning.value = true;

  // Determine which iframe is currently visible and which to render to
  const visibleFrame =
    currentFrameIndex.value === 1 ? previewFrame1.value : previewFrame2.value;
  const hiddenFrame =
    currentFrameIndex.value === 1 ? previewFrame2.value : previewFrame1.value;

  if (!hiddenFrame) {
    isTransitioning.value = false;
    return;
  }

  // Save scroll position from visible frame
  if (
    visibleFrame &&
    visibleFrame.contentWindow &&
    visibleFrame.contentDocument
  ) {
    const scrollTop = visibleFrame.contentWindow.scrollY || 0;
    const scrollHeight =
      visibleFrame.contentDocument.documentElement.scrollHeight;
    const clientHeight = visibleFrame.contentWindow.innerHeight;
    const maxScroll = scrollHeight - clientHeight;

    savedScrollPercentage = maxScroll > 0 ? scrollTop / maxScroll : 0;
  }

  if (shouldReloadFromFile || !stylesheetStore.content) {
    await stylesheetStore.loadStylesheet();
  }

  // Render to the hidden frame
  hiddenFrame.srcdoc = `
    <!DOCTYPE html>
    <html>
    <head>
      <link rel="stylesheet" href="/assets/css/pagedjs-interface.css">
      <style id="dynamic-styles">${stylesheetStore.content}</style>
      <script src="https://unpkg.com/pagedjs/dist/paged.polyfill.js"><\/script>
    </head>
    <body>${document.getElementById('content-source').innerHTML}</body>
    </html>
  `;

  hiddenFrame.onload = () => {
    // Add event listeners for page and element interactions
    hiddenFrame.contentDocument.addEventListener('mousemove', handleIframeMouseMove);
    hiddenFrame.contentDocument.addEventListener('click', handleIframeClick);

    // Close Coloris when clicking in the iframe
    hiddenFrame.contentDocument.addEventListener('click', () => {
      Coloris.close();
    });

    // Wait for PagedJS to finish rendering
    setTimeout(() => {
      // Restore scroll position
      const scrollHeight =
        hiddenFrame.contentDocument.documentElement.scrollHeight;
      const clientHeight = hiddenFrame.contentWindow.innerHeight;
      const maxScroll = scrollHeight - clientHeight;
      const targetScroll = savedScrollPercentage * maxScroll;

      hiddenFrame.contentWindow.scrollTo(0, targetScroll);

      // Start crossfade transition
      setTimeout(() => {
        // Make hidden frame visible (it's already behind)
        hiddenFrame.style.opacity = '1';
        hiddenFrame.style.zIndex = '1';

        // Fade out visible frame
        if (visibleFrame) {
          visibleFrame.style.opacity = '0';
        }

        // After fade completes, swap the frames
        setTimeout(() => {
          if (visibleFrame) {
            visibleFrame.style.zIndex = '0';
          }

          // Swap current frame
          currentFrameIndex.value = currentFrameIndex.value === 1 ? 2 : 1;
          isTransitioning.value = false;
        }, 200); // Match CSS transition duration
      }, 50); // Small delay to ensure scroll is set
    }, 200); // Wait for PagedJS
  };
};

watch(
  () => stylesheetStore.content,
  () => {
    renderPreview();
  }
);

// Handle Cmd+P / Ctrl+P to print the iframe content
const handlePrint = async (event) => {
  if ((event.metaKey || event.ctrlKey) && event.key === 'p') {
    event.preventDefault();
    const frame = activeFrame.value;
    if (frame && frame.contentDocument) {
      const doc = frame.contentDocument;

      // Collect all styles (inline and from stylesheets)
      let allStyles = '';

      // Get inline <style> tags
      doc.querySelectorAll('style').forEach((style) => {
        allStyles += style.outerHTML;
      });

      // Get computed styles from linked stylesheets
      for (const sheet of doc.styleSheets) {
        try {
          for (const rule of sheet.cssRules) {
            allStyles += rule.cssText + '\n';
          }
        } catch (e) {
          // Cross-origin stylesheet, try to fetch it
          if (sheet.href) {
            try {
              const response = await fetch(sheet.href);
              const css = await response.text();
              allStyles += css;
            } catch (fetchError) {
              console.warn('Could not fetch stylesheet:', sheet.href);
            }
          }
        }
      }

      // Get the body content (already rendered by PagedJS)
      const bodyContent = doc.body.innerHTML;

      // Build the print document
      const printContent = `
        <!DOCTYPE html>
        <html>
        <head>
          <meta charset="UTF-8">
          <style>${allStyles}</style>
        </head>
        <body>${bodyContent}</body>
        </html>
      `;

      // Open print window
      const printWindow = window.open('', '_blank');
      printWindow.document.write(printContent);
      printWindow.document.close();

      // Wait for rendering then print
      setTimeout(() => {
        printWindow.print();
        printWindow.close();
      }, 100);
    }
  }
};

onMounted(() => {
  renderPreview(true);
  window.addEventListener('keydown', handlePrint);
});

onUnmounted(() => {
  window.removeEventListener('keydown', handlePrint);
});
</script>

<template>
  <div id="content-source" style="display: none">
    <PagedJsWrapper />
  </div>

  <EditorPanel />

  <iframe
    ref="previewFrame1"
    class="preview-frame"
    :class="{ shifted: activeTab.length > 0 }"
  ></iframe>
  <iframe
    ref="previewFrame2"
    class="preview-frame"
    :class="{ shifted: activeTab.length > 0 }"
  ></iframe>

  <PreviewLoader :isLoading="isTransitioning" :shifted="activeTab.length > 0" />

  <ElementPopup ref="elementPopup" :iframeRef="activeFrame" />
  <PagePopup ref="pagePopup" :iframeRef="activeFrame" @close="handlePagePopupClose" />
</template>

<style>
.preview-frame {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  border: none;
  margin-left: 0;
  transform: scale(1) translateY(0);
  height: 100vh;
  transition: all 0.2s ease-in-out var(--curve);
}

.preview-frame.shifted {
  margin-left: 17.55rem;
  transform: scale(0.65) translateY(-40vh);
  height: 155vh;
}

.preview-frame:nth-of-type(1) {
  z-index: 1;
  opacity: 1;
}

.preview-frame:nth-of-type(2) {
  z-index: 0;
  opacity: 0;
}

/* Hide UI elements when printing */
@media print {
  #editor-panel,
  #element-popup,
  #page-popup,
  .preview-loader {
    display: none !important;
  }

  .preview-frame {
    position: static !important;
    margin-left: 0 !important;
    transform: none !important;
    width: 100% !important;
    height: auto !important;
  }

  .preview-frame:not(:first-of-type) {
    display: none !important;
  }
}
</style>
