---
export interface Props {
  src: string;
  alt: string;
  caption?: string;
  className?: string;
  width?: string | number;
  height?: string | number;
  aspectRatio?: string;
  lazy?: boolean;
}

const { 
  src, 
  alt, 
  caption, 
  className = '',
  width,
  height,
  aspectRatio,
  lazy = true 
} = Astro.props;

// 生成唯一ID
const imageId = `image-${Math.random().toString(36).substring(2, 11)}`;
---

<div class={`image-viewer ${className}`}>
  <div 
    class="image-container" 
    style={aspectRatio ? `aspect-ratio: ${aspectRatio}` : ''}
    onclick={`openImageModal('${imageId}')`}
  >
    <img 
      id={imageId}
      src={src}
      alt={alt}
      width={width}
      height={height}
      loading={lazy ? 'lazy' : 'eager'}
  class="viewer-image"
  data-image-id={imageId}
      data-full-src={src}
      data-caption={caption ?? ''}
      style="cursor: pointer;"
    />
  </div>
  {caption && (
    <div class="image-caption">{caption}</div>
  )}
</div>

<!-- 全屏模态框由全局脚本统一创建 -->

<script is:inline>
(function() {
  if (window.imageViewerInitialized) return;
  window.imageViewerInitialized = true;

  const MODAL_ID = 'global-image-viewer-modal';
  const MAX_SCALE = 6;
  const MIN_SCALE = 1;

  const state = {
    modal: null,
    overlay: null,
    closeBtn: null,
    prevBtn: null,
    nextBtn: null,
    counter: null,
    modalImage: null,
    modalCaption: null,
    images: [],
    currentIndex: -1,
    scale: 1,
    offsetX: 0,
    offsetY: 0,
    isPanning: false,
    startX: 0,
    startY: 0,
    pointers: new Map(),
    pinchStartDistance: 0,
    pinchStartScale: 1,
  };

  function createModal() {
    const modal = document.createElement('div');
    modal.id = MODAL_ID;
    modal.className = 'image-modal';
    modal.innerHTML = `
      <div class="modal-overlay"></div>
      <button class="nav-button nav-prev" type="button" aria-label="上一张" hidden>‹</button>
      <button class="nav-button nav-next" type="button" aria-label="下一张" hidden>›</button>
      <div class="modal-content">
        <button class="close-btn" aria-label="关闭">×</button>
        <img class="modal-image" alt="" draggable="false" />
        <div class="modal-caption" hidden></div>
        <div class="image-counter" hidden></div>
      </div>
    `;
    document.body.appendChild(modal);
    return modal;
  }

  function assignModalElements(modal) {
    state.modal = modal;
    state.overlay = modal.querySelector('.modal-overlay');
    state.closeBtn = modal.querySelector('.close-btn');
    state.prevBtn = modal.querySelector('.nav-prev');
    state.nextBtn = modal.querySelector('.nav-next');
    state.counter = modal.querySelector('.image-counter');
    state.modalImage = modal.querySelector('.modal-image');
    state.modalCaption = modal.querySelector('.modal-caption');
  }

  assignModalElements(document.getElementById(MODAL_ID) ?? createModal());

  function collectImages() {
    const candidates = Array.from(document.querySelectorAll('.viewer-image'));
    const seen = new Set();
    state.images = candidates.filter((node) => {
      if (!(node instanceof HTMLImageElement)) return false;
      if (!node.id) {
        node.id = `viewer-${crypto.randomUUID?.() ?? Math.random().toString(36).slice(2)}`;
      }
      if (seen.has(node.id)) return false;
      seen.add(node.id);
      if (!node.dataset.imageId) {
        node.dataset.imageId = node.id;
      }
      if (!node.dataset.fullSrc) {
        node.dataset.fullSrc = node.currentSrc || node.src;
      }
      return true;
    });
  }

  function clamp(value, min, max) {
    return Math.min(Math.max(value, min), max);
  }

  function clampOffsets() {
    if (!state.modalImage) return;
    const maxX = (state.scale - 1) * (state.modalImage.clientWidth / 2);
    const maxY = (state.scale - 1) * (state.modalImage.clientHeight / 2);
    if (maxX <= 0 || Number.isNaN(maxX)) {
      state.offsetX = 0;
    } else {
      state.offsetX = clamp(state.offsetX, -maxX, maxX);
    }
    if (maxY <= 0 || Number.isNaN(maxY)) {
      state.offsetY = 0;
    } else {
      state.offsetY = clamp(state.offsetY, -maxY, maxY);
    }
  }

  function updateTransform() {
    if (!state.modalImage) return;
    state.modalImage.style.transform = `translate(${state.offsetX}px, ${state.offsetY}px) scale(${state.scale})`;
  }

  function updateCursor(mode) {
    if (!state.modalImage) return;
    if (mode === 'grabbing') {
      state.modalImage.style.cursor = 'grabbing';
    } else if (state.scale > 1) {
      state.modalImage.style.cursor = 'grab';
    } else {
      state.modalImage.style.cursor = 'zoom-in';
    }
  }

  function resetTransform() {
    state.scale = 1;
    state.offsetX = 0;
    state.offsetY = 0;
    state.isPanning = false;
    state.startX = 0;
    state.startY = 0;
    state.pointers.clear();
    state.pinchStartDistance = 0;
    state.pinchStartScale = 1;
    updateTransform();
    updateCursor();
  }

  function toggleNavVisibility() {
    const hasMultiple = state.images.length > 1;
    if (state.prevBtn) {
      state.prevBtn.hidden = !hasMultiple;
    }
    if (state.nextBtn) {
      state.nextBtn.hidden = !hasMultiple;
    }
    if (state.counter) {
      if (hasMultiple) {
        state.counter.removeAttribute('hidden');
      } else {
        state.counter.setAttribute('hidden', '');
      }
    }
  }

  function showImage(index) {
    if (!state.images.length) return;
    const normalizedIndex = ((index % state.images.length) + state.images.length) % state.images.length;
    const target = state.images[normalizedIndex];
    if (!target || !(target instanceof HTMLImageElement)) return;

    state.currentIndex = normalizedIndex;

    const fullSrc = target.dataset.fullSrc || target.currentSrc || target.src;
    if (state.modalImage) {
      state.modalImage.src = fullSrc;
      state.modalImage.alt = target.alt || '';
    }

    if (state.modalCaption) {
      const captionText = target.dataset.caption || target.getAttribute('title') || '';
      if (captionText) {
        state.modalCaption.textContent = captionText;
        state.modalCaption.removeAttribute('hidden');
      } else {
        state.modalCaption.textContent = '';
        state.modalCaption.setAttribute('hidden', '');
      }
    }

    if (state.counter) {
      if (state.images.length > 1) {
        state.counter.textContent = `${normalizedIndex + 1} / ${state.images.length}`;
      } else {
        state.counter.textContent = '';
      }
    }

    toggleNavVisibility();
    resetTransform();
  }

  function closeModal() {
    if (!state.modal) return;
    state.modal.classList.remove('active');
    document.body.style.overflow = '';
    resetTransform();
  }

  function navigate(delta) {
    if (!state.modal?.classList.contains('active')) return;
    if (state.images.length <= 1) return;
    showImage(state.currentIndex + delta);
  }

  function onWheel(event) {
    if (!state.modal?.classList.contains('active')) return;
    event.preventDefault();
    const factor = event.deltaY < 0 ? 1.1 : 0.9;
    state.scale = clamp(state.scale * factor, MIN_SCALE, MAX_SCALE);
    if (state.scale === 1) {
      state.offsetX = 0;
      state.offsetY = 0;
    }
    clampOffsets();
    updateTransform();
    updateCursor();
  }

  function onPointerDown(event) {
    if (!state.modal?.classList.contains('active') || !state.modalImage) return;
    event.preventDefault();
    state.modalImage.setPointerCapture?.(event.pointerId);
    state.pointers.set(event.pointerId, { x: event.clientX, y: event.clientY });
    if (state.pointers.size === 1) {
      state.isPanning = state.scale > 1;
      state.startX = event.clientX - state.offsetX;
      state.startY = event.clientY - state.offsetY;
      if (state.isPanning) {
        updateCursor('grabbing');
      }
    } else if (state.pointers.size === 2) {
      const points = Array.from(state.pointers.values());
      state.pinchStartDistance = Math.hypot(points[0].x - points[1].x, points[0].y - points[1].y);
      state.pinchStartScale = state.scale;
      state.isPanning = false;
    }
  }

  function onPointerMove(event) {
    if (!state.modal?.classList.contains('active') || !state.pointers.has(event.pointerId)) return;
    state.pointers.set(event.pointerId, { x: event.clientX, y: event.clientY });

    if (state.pointers.size === 1 && state.isPanning && state.scale > 1) {
      event.preventDefault();
      state.offsetX = event.clientX - state.startX;
      state.offsetY = event.clientY - state.startY;
      clampOffsets();
      updateTransform();
    } else if (state.pointers.size >= 2) {
      const points = Array.from(state.pointers.values()).slice(0, 2);
      const distance = Math.hypot(points[0].x - points[1].x, points[0].y - points[1].y);
      if (state.pinchStartDistance > 0) {
        event.preventDefault();
        const nextScale = state.pinchStartScale * (distance / state.pinchStartDistance);
        state.scale = clamp(nextScale, MIN_SCALE, MAX_SCALE);
        if (state.scale === 1) {
          state.offsetX = 0;
          state.offsetY = 0;
        }
        clampOffsets();
        updateTransform();
        updateCursor();
      }
    }
  }

  function onPointerEnd(event) {
    if (!state.pointers.has(event.pointerId)) return;
    event.preventDefault();
    state.modalImage?.releasePointerCapture?.(event.pointerId);
    state.pointers.delete(event.pointerId);
    if (state.pointers.size === 0) {
      state.isPanning = false;
      updateCursor();
    } else if (state.pointers.size === 1) {
      const remaining = Array.from(state.pointers.values())[0];
      state.startX = remaining.x - state.offsetX;
      state.startY = remaining.y - state.offsetY;
    }
  }

  function onDoubleClick() {
    if (!state.modal?.classList.contains('active')) return;
    if (state.scale > 1.2) {
      state.scale = 1;
      state.offsetX = 0;
      state.offsetY = 0;
    } else {
      state.scale = 2;
    }
    clampOffsets();
    updateTransform();
    updateCursor();
  }

  state.overlay?.addEventListener('click', closeModal);
  state.closeBtn?.addEventListener('click', closeModal);
  state.prevBtn?.addEventListener('click', () => navigate(-1));
  state.nextBtn?.addEventListener('click', () => navigate(1));

  state.modalImage?.addEventListener('wheel', onWheel, { passive: false });
  state.modalImage?.addEventListener('pointerdown', onPointerDown);
  state.modalImage?.addEventListener('pointermove', onPointerMove);
  state.modalImage?.addEventListener('pointerup', onPointerEnd);
  state.modalImage?.addEventListener('pointercancel', onPointerEnd);
  state.modalImage?.addEventListener('dblclick', onDoubleClick);

  document.addEventListener('keydown', (event) => {
    if (!state.modal?.classList.contains('active')) return;
    if (event.key === 'Escape') {
      event.preventDefault();
      closeModal();
    } else if (event.key === 'ArrowLeft') {
      event.preventDefault();
      navigate(-1);
    } else if (event.key === 'ArrowRight') {
      event.preventDefault();
      navigate(1);
    } else if (event.key === 'Home') {
      event.preventDefault();
      if (state.images.length) showImage(0);
    } else if (event.key === 'End') {
      event.preventDefault();
      if (state.images.length) showImage(state.images.length - 1);
    }
  });

  const observer = new MutationObserver(() => {
    collectImages();
    toggleNavVisibility();
  });
  observer.observe(document.body, { childList: true, subtree: true });

  collectImages();
  toggleNavVisibility();

  window.openImageModal = function(imageId) {
    collectImages();
    if (!state.images.length) return;
    const targetIndex = state.images.findIndex((img) => img.dataset.imageId === imageId || img.id === imageId);
    if (targetIndex === -1) return;
    showImage(targetIndex);
    state.modal?.classList.add('active');
    document.body.style.overflow = 'hidden';
  };

  window.closeImageModal = closeModal;

  console.log('ImageViewer global modal with gallery & gestures ready');
})();
</script>

<style>
  .image-viewer {
    margin: 1.5rem 0;
    border-radius: 1rem;
    overflow: hidden;
    box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
    transition: all 0.3s ease;
  }
  
  .image-viewer:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
  }
  
  .image-container {
    position: relative;
    overflow: hidden;
    cursor: pointer;
    background: rgba(255, 255, 255, 0.1);
    backdrop-filter: blur(10px);
  }
  
  .viewer-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.3s ease;
    display: block;
  }
  
  .image-container:hover .viewer-image {
    transform: scale(1.05);
  }
  
  .image-caption {
    padding: 1rem;
    text-align: center;
    background: rgba(255, 255, 255, 0.1);
    backdrop-filter: blur(15px);
    border-top: 1px solid rgba(255, 255, 255, 0.1);
    color: #374151;
    font-size: 0.9rem;
    font-style: italic;
  }
  
  /* 模态框样式 */
  :global(.image-modal) {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease, visibility 0.3s ease;
  }
  
  :global(.image-modal.active) {
    opacity: 1;
    visibility: visible;
  }
  
  :global(.image-modal .modal-overlay) {
    position: absolute;
    inset: 0;
    background: rgba(11, 18, 33, 0.85);
    backdrop-filter: blur(30px);
  }
  
  :global(.image-modal .modal-content) {
    position: relative;
    width: 100vw;
    height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: clamp(1.5rem, 4vw, 3rem);
    box-sizing: border-box;
    transform: scale(0.95);
    transition: transform 0.3s ease;
  }
  
  :global(.image-modal.active .modal-content) {
    transform: scale(1);
  }
  
  :global(.image-modal .close-btn) {
    position: absolute;
    top: clamp(1rem, 3vw, 2.5rem);
    right: clamp(1rem, 3vw, 2.5rem);
    width: 3rem;
    height: 3rem;
    background: rgba(255, 255, 255, 0.18);
    color: #f9fafb;
    border: none;
    border-radius: 999px;
    font-size: 2rem;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s ease;
    backdrop-filter: blur(10px);
    box-shadow: 0 12px 30px rgba(15, 23, 42, 0.35);
    z-index: 1;
  }
  
  :global(.image-modal .close-btn:hover) {
    background: rgba(255, 255, 255, 0.3);
    transform: scale(1.05);
  }
  
  :global(.image-modal .modal-image) {
    width: 100%;
    height: auto;
    max-width: min(1200px, 95vw);
    max-height: calc(100vh - clamp(6rem, 12vh, 10rem));
    object-fit: contain;
    border-radius: 1rem;
    box-shadow: 0 25px 60px rgba(15, 23, 42, 0.45);
    touch-action: none;
    user-select: none;
    cursor: zoom-in;
    transition: transform 0.12s ease-out;
  }

  :global(.image-modal .nav-button) {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: clamp(3rem, 4vw, 4.5rem);
    height: clamp(3rem, 4vw, 4.5rem);
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.18);
    color: #f8fafc;
    font-size: clamp(1.8rem, 3vw, 2.4rem);
    cursor: pointer;
    backdrop-filter: blur(12px);
    box-shadow: 0 20px 40px rgba(15, 23, 42, 0.45);
    transition: transform 0.2s ease, background 0.2s ease;
    z-index: 1;
  }

  :global(.image-modal .nav-button:hover) {
    background: rgba(255, 255, 255, 0.3);
    transform: translateY(-50%) scale(1.05);
  }

  :global(.image-modal .nav-button:active) {
    transform: translateY(-50%) scale(0.95);
  }

  :global(.image-modal .nav-prev) {
    left: clamp(0.75rem, 2vw, 2.5rem);
  }

  :global(.image-modal .nav-next) {
    right: clamp(0.75rem, 2vw, 2.5rem);
  }

  :global(.image-modal .image-counter) {
    position: absolute;
    bottom: clamp(1.5rem, 3vh, 3rem);
    left: 50%;
    transform: translateX(-50%);
    background: rgba(15, 23, 42, 0.55);
    color: #e2e8f0;
    padding: 0.6rem 1.6rem;
    border-radius: 999px;
    font-size: 0.95rem;
    box-shadow: 0 12px 30px rgba(15, 23, 42, 0.35);
    letter-spacing: 0.08em;
  }
  
  :global(.image-modal .modal-caption) {
    margin-top: clamp(1rem, 2.5vh, 2rem);
    padding: clamp(0.75rem, 2vw, 1.5rem) clamp(1rem, 3vw, 2rem);
    border-radius: 999px;
    background: rgba(15, 23, 42, 0.55);
    color: #e2e8f0;
    font-size: clamp(0.9rem, 2vw, 1.05rem);
    font-style: italic;
    box-shadow: 0 12px 30px rgba(15, 23, 42, 0.35);
    text-align: center;
  }
  
  /* 响应式设计 */
  @media (max-width: 768px) {
    :global(.image-modal) {
      padding: 1rem;
    }
    
    :global(.image-modal .modal-image) {
      max-width: 95vw;
      max-height: 75vh;
    }
    
    :global(.image-modal .nav-button) {
      width: 2.75rem;
      height: 2.75rem;
      font-size: 1.75rem;
    }
  }
  
  /* 加载动画 */
  .viewer-image {
    animation: fadeIn 0.5s ease-out;
  }
  
  @keyframes fadeIn {
    from {
      opacity: 0;
      transform: scale(0.95);
    }
    to {
      opacity: 1;
      transform: scale(1);
    }
  }
</style>