'use client';

import { useCallback, useEffect, useRef, useState } from 'react';
import { fetchApi } from '@/lib/utils/client';
import { FeedCard as FeedCardType, FeedTimelineResponse } from '@/types/feed/card';
import FeedCard from './FeedCard';
import '../style.scss';

type Props = {
	initialCards: FeedCardType[];
	initialTotal: number;
	endpoint: string;
	perPage?: number;
	emptyMessage?: string;
};

export default function FeedList({ initialCards, initialTotal, endpoint, perPage = 20, emptyMessage }: Props) {
	const [cards, setCards] = useState<FeedCardType[]>(initialCards);
	const [page, setPage] = useState(2);
	const [hasMore, setHasMore] = useState(initialCards.length < initialTotal);
	const [loading, setLoading] = useState(false);
	const sentinelRef = useRef<HTMLDivElement|null>(null);
	const [focusIndex, setFocusIndex] = useState<number|null>(null);

	const loadMore = useCallback(async () => {
		if (loading || !hasMore) {
			return;
		}

		setLoading(true);

		try {
			const sep = endpoint.includes('?') ? '&' : '?';
			const res = await fetchApi<FeedTimelineResponse>(`${endpoint}${sep}page=${page}&perPage=${perPage}`, {
				method: 'GET',
				silent: true
			});

			const list = res.data?.list ?? [];
			const total = res.data?.total ?? 0;

			setCards((prev) => {
				const seen = new Set(prev.map((c) => c.postID));
				const dedup = list.filter((c) => !seen.has(c.postID));
				const next = [...prev, ...dedup];
				if (next.length >= total || list.length === 0) {
					setHasMore(false);
				}
				return next;
			});

			setPage((p) => p + 1);
		} catch (err) {
			console.error('Feed load failed:', err);
			setHasMore(false);
		} finally {
			setLoading(false);
		}
	}, [loading, hasMore, endpoint, page, perPage]);

	useEffect(() => {
		const node = sentinelRef.current;
		if (!node || !hasMore) {
			return;
		}

		const observer = new IntersectionObserver(
			(entries) => {
				if (entries[0].isIntersecting) {
					loadMore();
				}
			},
			{ rootMargin: '200px' }
		);

		observer.observe(node);
		return () => observer.disconnect();
	}, [hasMore, loadMore]);

	const scrollToCard = useCallback((index: number) => {
		const el = document.querySelector<HTMLElement>(`[data-post-id="${cards[index]?.postID}"]`);
		if (el) {
			el.scrollIntoView({ behavior: 'smooth', block: 'center' });
		}
	}, [cards]);

	// 새 글 게시 후 즉시 prepend (FeedComposer 가 dispatch)
	useEffect(() => {
		const handlePosted = async () => {
			try {
				const sep = endpoint.includes('?') ? '&' : '?';
				const res = await fetchApi<FeedTimelineResponse>(`${endpoint}${sep}page=1&perPage=${perPage}`, {
					method: 'GET',
					silent: true
				});

				const list = res.data?.list ?? [];
				if (list.length === 0) {
					return;
				}

				setCards((prev) => {
					const seen = new Set(prev.map((c) => c.postID));
					const fresh = list.filter((c) => !seen.has(c.postID));
					if (fresh.length === 0) {
						return prev;
					}
					return [...fresh, ...prev];
				});

				if (typeof window !== 'undefined') {
					window.scrollTo({ top: 0, behavior: 'smooth' });
				}
			} catch (err) {
				console.error('Feed refresh after post failed:', err);
			}
		};

		window.addEventListener('feed:posted', handlePosted);
		return () => window.removeEventListener('feed:posted', handlePosted);
	}, [endpoint, perPage]);

	// 키보드 네비게이션 (J/K)
	useEffect(() => {
		const handleKeyDown = (e: KeyboardEvent) => {
			if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) {
				return;
			}

			if (e.key === 'j' || e.key === 'J') {
				setFocusIndex((idx) => {
					const next = idx === null ? 0 : Math.min(idx + 1, cards.length - 1);
					scrollToCard(next);
					return next;
				});
			} else if (e.key === 'k' || e.key === 'K') {
				setFocusIndex((idx) => {
					const next = idx === null ? 0 : Math.max(idx - 1, 0);
					scrollToCard(next);
					return next;
				});
			}
		};

		window.addEventListener('keydown', handleKeyDown);
		return () => window.removeEventListener('keydown', handleKeyDown);
	}, [cards.length, scrollToCard]);

	if (cards.length === 0) {
		return <p className="feed__empty">{emptyMessage ?? '게시물이 없습니다.'}</p>;
	}

	return (
		<div className="feed__list" role="feed">
			{cards.map((card, index) => (
				<article
					key={card.postID}
					className={`feed__list-item${focusIndex === index ? ' feed__list-item--focused' : ''}`}
				>
					<FeedCard card={card} />
				</article>
			))}

			{hasMore && (
				<div ref={sentinelRef} className="feed__sentinel">
					{loading ? '준비 중...' : ''}
				</div>
			)}
		</div>
	);
}