'use client';

import { useState, useEffect, useRef, useCallback, useMemo, memo } from 'react';
import { useParams, useRouter } from 'next/navigation';
import Link from 'next/link';
import { storyApi, seriesApi } from '../../../lib/api';
import { Story } from '../../../types/api';
import LoadingSpinner from '../../../components/ui/LoadingSpinner';
import Button from '../../../components/ui/Button';
import StoryRating from '../../../components/stories/StoryRating';
import TagDisplay from '../../../components/tags/TagDisplay';
import TableOfContents from '../../../components/stories/TableOfContents';
import { sanitizeHtml, preloadSanitizationConfig } from '../../../lib/sanitization';
import { debug } from '../../../lib/debug';

// Memoized content component that only re-renders when content changes
const StoryContent = memo(({
  content,
  contentRef
}: {
  content: string;
  contentRef: React.RefObject<HTMLDivElement>;
}) => {
  const renderTime = Date.now();
  debug.log('🔄 StoryContent component rendering at', renderTime, 'with content length:', content.length, 'hash:', content.slice(0, 50) + '...');

  // Add observer to track image loading events
  useEffect(() => {
    if (!contentRef.current) return;

    const images = contentRef.current.querySelectorAll('img');
    debug.log('📸 Found', images.length, 'images in content');

    const handleImageLoad = (e: Event) => {
      const img = e.target as HTMLImageElement;
      debug.log('🖼️ Image loaded:', img.src);
    };

    const handleImageError = (e: Event) => {
      const img = e.target as HTMLImageElement;
      debug.log('❌ Image error:', img.src);
    };

    images.forEach(img => {
      img.addEventListener('load', handleImageLoad);
      img.addEventListener('error', handleImageError);
      debug.log('👀 Monitoring image:', img.src);
    });

    return () => {
      images.forEach(img => {
        img.removeEventListener('load', handleImageLoad);
        img.removeEventListener('error', handleImageError);
      });
    };
  }, [content]);

  return (
    <div
      ref={contentRef}
      className="reading-content"
      dangerouslySetInnerHTML={{ __html: content }}
      style={{
        // Prevent layout shifts that might cause image reloads
        minHeight: '100vh',
        contain: 'layout style'
      }}
    />
  );
});

StoryContent.displayName = 'StoryContent';

export default function StoryReadingPage() {
  const params = useParams();
  const router = useRouter();
  const [story, setStory] = useState<Story | null>(null);
  const [seriesStories, setSeriesStories] = useState<Story[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [readingProgress, setReadingProgress] = useState(0);
  const [readingPercentage, setReadingPercentage] = useState(0);
  const [sanitizedContent, setSanitizedContent] = useState<string>('');
  const [hasScrolledToPosition, setHasScrolledToPosition] = useState(false);
  const [showToc, setShowToc] = useState(false);
  const [hasHeadings, setHasHeadings] = useState(false);
  const [showEndOfStoryPopup, setShowEndOfStoryPopup] = useState(false);
  const [hasReachedEnd, setHasReachedEnd] = useState(false);
  const [resettingPosition, setResettingPosition] = useState(false);
  const contentRef = useRef<HTMLDivElement>(null);
  const saveTimeoutRef = useRef<NodeJS.Timeout | null>(null);

  const storyId = params.id as string;

  // Convert scroll position to approximate character position in the content
  const getCharacterPositionFromScroll = useCallback((): number => {
    if (!contentRef.current || !story) return 0;

    const content = contentRef.current;
    const scrolled = window.scrollY;
    const contentTop = content.offsetTop;
    const contentHeight = content.scrollHeight;
    const windowHeight = window.innerHeight;

    // Calculate how far through the content we are (0-1)
    const scrollRatio = Math.min(1, Math.max(0,
      (scrolled - contentTop + windowHeight * 0.3) / contentHeight
    ));

    // Convert to character position in the HTML content (ALWAYS use contentHtml for consistency)
    const textLength = story.contentHtml?.length || 0;
    return Math.floor(scrollRatio * textLength);
  }, [story]);

  // Calculate reading percentage from character position
  const calculateReadingPercentage = useCallback((currentPosition: number): number => {
    if (!story) return 0;

    // ALWAYS use contentHtml for consistency with position calculation
    const totalLength = story.contentHtml?.length || 0;
    if (totalLength === 0) return 0;

    return Math.round((currentPosition / totalLength) * 100);
  }, [story]);

  // Convert character position back to scroll position for auto-scroll
  const scrollToCharacterPosition = useCallback((position: number) => {
    if (!contentRef.current || !story || hasScrolledToPosition) return;

    // ALWAYS use contentHtml for consistency with position calculation
    const textLength = story.contentHtml?.length || 0;
    if (textLength === 0 || position === 0) return;

    const ratio = position / textLength;
    const content = contentRef.current;
    const contentTop = content.offsetTop;
    const contentHeight = content.scrollHeight;
    const windowHeight = window.innerHeight;

    // Calculate target scroll position
    const targetScroll = contentTop + (ratio * contentHeight) - (windowHeight * 0.3);

    // Use smooth scrolling only for shorter stories (< 15 000 words).
    // For longer stories the saved position can be 50 000+ px from the top;
    // browsers animate smooth-scroll at ~1 000–1 500 px/s, so the animation
    // would take tens of seconds and often stalls or cancels before finishing.
    const scrollBehavior = (story.wordCount ?? 0) < 15000 ? 'smooth' : 'instant';
    window.scrollTo({
      top: Math.max(0, targetScroll),
      behavior: scrollBehavior
    });

    setHasScrolledToPosition(true);
  }, [story, hasScrolledToPosition]);

  // Debounced function to save reading position
  const saveReadingPosition = useCallback(async (position: number) => {
    if (!story || position === story.readingPosition) {
      debug.log('Skipping save - no story or position unchanged:', { story: !!story, position, current: story?.readingPosition });
      return;
    }
    
    debug.log('Saving reading position:', position, 'for story:', story.id);
    try {
      const updatedStory = await storyApi.updateReadingProgress(story.id, position);
      debug.log('Reading position saved successfully, updated story:', updatedStory.readingPosition);
      setStory(prev => prev ? { ...prev, readingPosition: position, lastReadAt: updatedStory.lastReadAt } : null);
    } catch (error) {
      console.error('Failed to save reading position:', error);
    }
  }, [story]);

  // Debounced version of saveReadingPosition
  const debouncedSavePosition = useCallback((position: number) => {
    if (saveTimeoutRef.current) {
      clearTimeout(saveTimeoutRef.current);
    }
    
    saveTimeoutRef.current = setTimeout(() => {
      saveReadingPosition(position);
    }, 2000); // Save after 2 seconds of no scrolling
  }, [saveReadingPosition]);

  useEffect(() => {
    const loadStory = async () => {
      try {
        setLoading(true);
        
        // Preload sanitization config and load story in parallel
        const [storyData] = await Promise.all([
          storyApi.getStory(storyId),
          preloadSanitizationConfig()
        ]);
        
        setStory(storyData);

        // Sanitize story content and add IDs to headings
        const sanitized = await sanitizeHtml(storyData.contentHtml || '');

        // Add IDs to headings for TOC functionality using regex instead of DOMParser
        // This avoids potential browser-specific sanitization that might strip src attributes
        let processedContent = sanitized;
        const headingMatches = processedContent.match(/<h[1-6][^>]*>/gi);
        let headingCount = 0;

        if (headingMatches) {
          processedContent = processedContent.replace(/<h([1-6])([^>]*)>/gi, (match, level, attrs) => {
            const headingId = `heading-${headingCount++}`;

            // Check if id attribute already exists
            if (attrs.includes('id=')) {
              // Replace existing id
              return match.replace(/id=['"][^'"]*['"]/, `id="${headingId}"`);
            } else {
              // Add id attribute
              return `<h${level}${attrs} id="${headingId}">`;
            }
          });
        }

        setSanitizedContent(processedContent);
        setHasHeadings(headingCount > 0);

        // Load series stories if part of a series
        if (storyData.seriesId) {
          const seriesData = await seriesApi.getSeriesStories(storyData.seriesId);
          setSeriesStories(seriesData);
        }
      } catch (err: any) {
        console.error('Failed to load story:', err);
        setError(err.response?.data?.message || 'Failed to load story');
      } finally {
        setLoading(false);
      }
    };

    if (storyId) {
      loadStory();
    }
  }, [storyId]);

  // Auto-scroll to saved reading position or URL hash when story content is loaded
  useEffect(() => {
    if (story && sanitizedContent && !hasScrolledToPosition) {
      let cancelled = false;

      const performScroll = () => {
        if (cancelled) return;

        debug.log('Initializing reading position tracking, saved position:', story.readingPosition);

        // Check if there's a hash in the URL (for TOC navigation)
        const hash = window.location.hash.substring(1);
        if (hash && hash.startsWith('heading-')) {
          debug.log('Auto-scrolling to heading from URL hash:', hash);
          const element = document.getElementById(hash);
          if (element) {
            element.scrollIntoView({
              behavior: 'smooth',
              block: 'start'
            });
            setHasScrolledToPosition(true);
            return;
          }
        }

        // Otherwise, use saved reading position
        if (story.readingPosition && story.readingPosition > 0) {
          debug.log('Auto-scrolling to saved position:', story.readingPosition);
          const initialPercentage = calculateReadingPercentage(story.readingPosition);
          setReadingPercentage(initialPercentage);
          scrollToCharacterPosition(story.readingPosition);
        } else {
          // Even if there's no saved position, mark as ready for tracking
          debug.log('No saved position, starting fresh tracking');
          setReadingPercentage(0);
          setHasScrolledToPosition(true);
        }
      };

      // Wait for images to load before scrolling so scrollHeight is accurate
      const waitForImagesAndScroll = () => {
        if (!contentRef.current) {
          // Content not in DOM yet (e.g. series API still loading) — retry
          // until the ref is available rather than jumping straight to performScroll,
          // which would silently no-op and never set hasScrolledToPosition.
          if (!cancelled) setTimeout(waitForImagesAndScroll, 100);
          return;
        }

        const images = contentRef.current.querySelectorAll('img');
        const unloadedImages = Array.from(images).filter(img => !img.complete);

        if (unloadedImages.length === 0) {
          // No pending images, scroll after a brief delay for layout
          debug.log('No pending images, scrolling immediately');
          setTimeout(performScroll, 100);
          return;
        }

        debug.log(`Waiting for ${unloadedImages.length} images to load before scrolling`);
        let loadedCount = 0;

        // Fallback timeout in case images take too long
        const fallbackTimeout = setTimeout(() => {
          debug.log('Image load timeout reached, scrolling anyway');
          performScroll();
        }, 5000);

        const onImageReady = () => {
          loadedCount++;
          if (loadedCount >= unloadedImages.length) {
            clearTimeout(fallbackTimeout);
            // Small delay after last image loads for layout to settle
            setTimeout(performScroll, 50);
          }
        };

        unloadedImages.forEach(img => {
          img.addEventListener('load', onImageReady, { once: true });
          img.addEventListener('error', onImageReady, { once: true });
        });
      };

      // Small delay to ensure content is in the DOM
      const timeout = setTimeout(waitForImagesAndScroll, 100);

      return () => {
        cancelled = true;
        clearTimeout(timeout);
      };
    }
  }, [story, sanitizedContent, scrollToCharacterPosition, calculateReadingPercentage, hasScrolledToPosition]);

  // Track reading progress and save position
  useEffect(() => {
    let ticking = false;
    let scrollEventCount = 0;

    const handleScroll = () => {
      scrollEventCount++;
      if (scrollEventCount % 10 === 0) {
        debug.log('📜 Scroll event #', scrollEventCount, 'at', Date.now());
      }

      if (!ticking) {
        requestAnimationFrame(() => {
          const article = document.querySelector('[data-reading-content]') as HTMLElement;
          if (article) {
            const scrolled = window.scrollY;
            const articleTop = article.offsetTop;
            const articleHeight = article.scrollHeight;
            const windowHeight = window.innerHeight;

            const progress = Math.min(100, Math.max(0,
              ((scrolled - articleTop + windowHeight) / articleHeight) * 100
            ));

            setReadingProgress(progress);

            // Multi-method end-of-story detection
            const documentHeight = document.documentElement.scrollHeight;
            const windowBottom = scrolled + windowHeight;
            const distanceFromBottom = documentHeight - windowBottom;

            // Method 1: Distance from bottom (most reliable)
            const nearBottom = distanceFromBottom <= 200;

            // Method 2: High progress but only as secondary check
            const highProgress = progress >= 98;

            // Method 3: Check if story content itself is fully visible
            const storyContentElement = contentRef.current;
            let storyContentFullyVisible = false;
            if (storyContentElement) {
              const contentRect = storyContentElement.getBoundingClientRect();
              const contentBottom = scrolled + contentRect.bottom;
              const documentContentHeight = Math.max(documentHeight - 300, contentBottom); // Account for footer padding
              storyContentFullyVisible = windowBottom >= documentContentHeight;
            }

            // Trigger end detection if user is near bottom AND (has high progress OR story content is fully visible)
            if (nearBottom && (highProgress || storyContentFullyVisible) && !hasReachedEnd && hasScrolledToPosition) {
              debug.log('End of story detected:', { nearBottom, highProgress, storyContentFullyVisible, distanceFromBottom, progress });
              setHasReachedEnd(true);
              setShowEndOfStoryPopup(true);
            }

            // Save reading position and update percentage (debounced)
            if (hasScrolledToPosition) { // Only save after initial auto-scroll
              const characterPosition = getCharacterPositionFromScroll();
              const percentage = calculateReadingPercentage(characterPosition);
              debug.log('Scroll detected, character position:', characterPosition, 'percentage:', percentage);
              setReadingPercentage(percentage);
              debouncedSavePosition(characterPosition);
            } else {
              debug.log('Scroll detected but not ready for tracking yet');
            }
          }
          ticking = false;
        });
        ticking = true;
      }
    };

    window.addEventListener('scroll', handleScroll);
    return () => {
      window.removeEventListener('scroll', handleScroll);
      // Clean up timeout on unmount
      if (saveTimeoutRef.current) {
        clearTimeout(saveTimeoutRef.current);
      }
    };
  }, [story, hasScrolledToPosition, getCharacterPositionFromScroll, calculateReadingPercentage, debouncedSavePosition, hasReachedEnd]);

  const handleRatingUpdate = async (newRating: number) => {
    if (!story) return;

    try {
      await storyApi.updateRating(story.id, newRating);
      setStory(prev => prev ? { ...prev, rating: newRating } : null);
    } catch (error) {
      console.error('Failed to update rating:', error);
    }
  };

  const handleResetReadingPosition = async () => {
    if (!story) return;

    try {
      setResettingPosition(true);
      await storyApi.updateReadingProgress(story.id, 0);
      setStory(prev => prev ? { ...prev, readingPosition: 0 } : null);
      setShowEndOfStoryPopup(false);
      setHasReachedEnd(false);

      // DON'T scroll immediately - let user stay at current position
      // The reset will take effect when they next open the story
    } catch (error) {
      console.error('Failed to reset reading position:', error);
    } finally {
      setResettingPosition(false);
    }
  };


  const findNextStory = (): Story | null => {
    if (!story?.seriesId || seriesStories.length <= 1) return null;
    
    const currentIndex = seriesStories.findIndex(s => s.id === story.id);
    return currentIndex < seriesStories.length - 1 ? seriesStories[currentIndex + 1] : null;
  };

  const findPreviousStory = (): Story | null => {
    if (!story?.seriesId || seriesStories.length <= 1) return null;
    
    const currentIndex = seriesStories.findIndex(s => s.id === story.id);
    return currentIndex > 0 ? seriesStories[currentIndex - 1] : null;
  };

  const nextStory = findNextStory();
  const previousStory = findPreviousStory();

  // Memoize the sanitized content to prevent re-processing on scroll
  const memoizedContent = useMemo(() => {
    return sanitizedContent;
  }, [sanitizedContent]);

  if (loading) {
    return (
      <div className="min-h-screen theme-bg flex items-center justify-center">
        <LoadingSpinner size="lg" />
      </div>
    );
  }

  if (error || !story) {
    return (
      <div className="min-h-screen theme-bg flex items-center justify-center">
        <div className="text-center">
          <h1 className="text-2xl font-bold theme-header mb-4">
            {error || 'Story not found'}
          </h1>
          <Button href="/library">
            Return to Library
          </Button>
        </div>
      </div>
    );
  }

  return (
    <div className="min-h-screen theme-bg">
      {/* Progress Bar */}
      <div className="fixed top-0 left-0 right-0 h-1 bg-gray-200 dark:bg-gray-700 z-50">
        <div 
          className="h-full theme-accent-bg transition-all duration-200 ease-out"
          style={{ width: `${readingProgress}%` }}
        />
      </div>

      {/* Header */}
      <header className="sticky top-1 z-40 theme-card theme-shadow">
        <div className="max-w-4xl mx-auto px-4 py-3 flex items-center justify-between">
          <div className="flex items-center gap-4">
            <Link href="/library" className="theme-text hover:theme-accent">
              ← Library
            </Link>
            <Link href={`/stories/${story.id}/detail`} className="theme-text hover:theme-accent">
              📄 Details
            </Link>
          </div>
          
          <div className="flex items-center gap-4">
            {/* Reading percentage indicator */}
            <div className="text-sm theme-text font-mono bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">
              {readingPercentage}%
            </div>

            {hasHeadings && (
              <button
                onClick={() => setShowToc(!showToc)}
                className="text-sm theme-text hover:theme-accent transition-colors"
                title="Table of Contents"
              >
                📋 TOC
              </button>
            )}

            <StoryRating
              rating={story.rating || 0}
              onRatingChange={handleRatingUpdate}
            />

            <Link href={`/stories/${story.id}/edit`}>
              <Button size="sm" variant="ghost">
                Edit
              </Button>
            </Link>
          </div>
        </div>
      </header>

      {/* Table of Contents Modal */}
      {showToc && (
        <>
          {/* Backdrop */}
          <div 
            className="fixed inset-0 bg-black bg-opacity-50 z-50"
            onClick={() => setShowToc(false)}
          />
          
          {/* TOC Modal */}
          <div className="fixed top-20 right-4 left-4 md:left-auto md:w-80 max-h-96 z-50">
            <TableOfContents 
              htmlContent={sanitizedContent}
              collapsible={false}
              onItemClick={(item) => {
                const element = document.getElementById(item.id);
                if (element) {
                  element.scrollIntoView({ 
                    behavior: 'smooth',
                    block: 'start'
                  });
                  setShowToc(false); // Close TOC after navigation
                }
              }}
            />
          </div>
        </>
      )}

      {/* End of Story Popup */}
      {showEndOfStoryPopup && (
        <>
          {/* Backdrop */}
          <div
            className="fixed inset-0 bg-black bg-opacity-50 z-50"
            onClick={() => setShowEndOfStoryPopup(false)}
          />

          {/* Popup Modal */}
          <div className="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-50 max-w-md w-full mx-4">
            <div className="theme-card theme-shadow rounded-lg p-6">
              <div className="text-center">
                <h3 className="text-lg font-semibold theme-header mb-3">
                  🎉 Story Complete!
                </h3>
                <p className="theme-text mb-6">
                  You've reached the end of "{story?.title}". Would you like to reset your reading position so the story starts from the beginning next time you open it?
                </p>

                <div className="flex gap-3 justify-center">
                  <Button
                    variant="ghost"
                    onClick={() => setShowEndOfStoryPopup(false)}
                  >
                    Keep Current Position
                  </Button>
                  <Button
                    variant="primary"
                    onClick={handleResetReadingPosition}
                    loading={resettingPosition}
                  >
                    Reset for Next Time
                  </Button>
                </div>
              </div>
            </div>
          </div>
        </>
      )}

      {/* Story Content */}
      <main className="max-w-4xl mx-auto px-4 py-8">
        <article data-reading-content>

          {/* Title and Metadata */}
          <header className="mb-8 text-center">
            <h1 className="text-4xl font-bold theme-header mb-4">
              {story.title}
            </h1>
            
            <div className="space-y-2">
              <Link 
                href={`/authors/${story.authorId}`}
                className="text-xl theme-accent hover:underline"
              >
                by {story.authorName}
              </Link>
              
              <div className="flex justify-center items-center gap-4 text-sm theme-text">
                <span>{story.wordCount.toLocaleString()} words</span>
                <span>•</span>
                <span>{new Date(story.createdAt).toLocaleDateString()}</span>
                {story.seriesName && (
                  <>
                    <span>•</span>
                    <span>{story.seriesName} #{story.volume}</span>
                  </>
                )}
              </div>
            </div>

            {/* Tags */}
            {story.tags && story.tags.length > 0 && (
              <div className="flex flex-wrap justify-center gap-2 mt-4">
                {story.tags.map((tag) => (
                  <TagDisplay
                    key={tag.id}
                    tag={tag}
                    size="md"
                    clickable={false}
                  />
                ))}
              </div>
            )}

            {/* Source URL */}
            {story.sourceUrl && (
              <div className="mt-4">
                <a
                  href={story.sourceUrl}
                  target="_blank"
                  rel="noopener noreferrer"
                  className="text-sm theme-accent hover:underline"
                >
                  Original Source ↗
                </a>
              </div>
            )}
          </header>

          {/* Story Content */}
          <StoryContent
            key={`story-content-${story?.id || 'loading'}`}
            content={memoizedContent}
            contentRef={contentRef}
          />
        </article>

        {/* Series Navigation */}
        {(previousStory || nextStory) && (
          <nav className="mt-12 pt-8 border-t theme-border">
            <h3 className="text-lg font-semibold theme-header mb-4 text-center">
              Continue Reading in {story.seriesName}
            </h3>
            
            <div className="flex justify-between items-center">
              {previousStory ? (
                <Link
                  href={`/stories/${previousStory.id}`}
                  className="flex-1 max-w-md p-4 theme-card theme-shadow rounded-lg hover:shadow-lg transition-shadow"
                >
                  <div className="text-sm theme-text mb-1">← Previous</div>
                  <div className="font-semibold theme-header">{previousStory.title}</div>
                  <div className="text-sm theme-text">Part {previousStory.volume}</div>
                </Link>
              ) : (
                <div className="flex-1 max-w-md"></div>
              )}

              {nextStory ? (
                <Link
                  href={`/stories/${nextStory.id}`}
                  className="flex-1 max-w-md p-4 theme-card theme-shadow rounded-lg hover:shadow-lg transition-shadow text-right"
                >
                  <div className="text-sm theme-text mb-1">Next →</div>
                  <div className="font-semibold theme-header">{nextStory.title}</div>
                  <div className="text-sm theme-text">Part {nextStory.volume}</div>
                </Link>
              ) : (
                <div className="flex-1 max-w-md"></div>
              )}
            </div>
          </nav>
        )}

        {/* Back to Library */}
        <div className="text-center mt-12">
          <Button href="/library" variant="ghost">
            ← Return to Library
          </Button>
        </div>
      </main>
    </div>
  );
}