'use client';

import { useState, useRef, useEffect, useCallback, useMemo, type CSSProperties, type KeyboardEvent, type FocusEvent, type ChangeEvent, type ReactNode } from 'react';
import { HubConnectionState } from '@microsoft/signalr';
import useAuth from '@/hooks/useAuth';
import { useSignalRContext } from '@/contexts/signalrProvider';
import type { ChatMessage, ChatParticipant } from '@/types/chat';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faUsers, faPaperPlane, faBullhorn, faArrowDown, faChevronDown, faBell } from '@fortawesome/free-solid-svg-icons';
import EmojiPicker from '@/app/component/EmojiPicker';
import ChatParticipants from './_component/ChatParticipants';
import ChatSettingsMenu from './_component/ChatSettingsMenu';
import ChatReceiveModal from './_component/ChatReceiveModal';

const ROOM_KEY = 'main';
const MAX_MESSAGES = 200; // 최근 200건 유지·오버플로 드랍 (Backend ChatSettings.MaxMessages 와 동일)
const MAX_SYSTEM_MESSAGES = 50;
const MAX_CONTENT_LENGTH = 500;
const GLOBAL_RATE_LIMIT_SECONDS = 2; // Hub 전역 rate limit (ChatSettings.RateLimitSeconds)
const SCROLL_BOTTOM_THRESHOLD = 50;

// 글씨 크게/작게 — #chat-room 의 --chat-fs 배율(localStorage 영속)
const FONT_SCALE_MIN = 0.85;
const FONT_SCALE_MAX = 1.6;
const FONT_SCALE_STEP = 0.1;
const FONT_SCALE_KEY = 'az-chat-fs';

type SystemMessage = {
	id: number;
	content: string;
	receivedAt: string;
};

type MergedItem =
	| { kind: 'chat'; time: string; data: ChatMessage }
	| { kind: 'system'; time: string; data: SystemMessage };

type ConnectionState = 'connecting'|'connected'|'reconnecting'|'disconnected';

// 메시지 dedupe 키 — ChatMessage 페이로드에 단일 ID 필드가 없어 (memberSID, sentAt, content) 합성 키 사용
// SSR 초기 히스토리 ↔ JoinRoom ReceiveHistory ↔ 실시간 ReceiveMessage 간 중복 제거
const messageKey = (m: ChatMessage) => `${m.memberSID}|${m.sentAt}|${m.content}`;

const formatTime = (dateStr: string) => {
	const date = new Date(dateStr);
	return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
};

type Props = {
	initialMessages?: ChatMessage[];
	notice?: string|null;
	slowModeSeconds?: number;
	// 헤더 우측(.chat-room__meta)에 주입할 컨트롤 — 채팅 도크의 접기 버튼 등 (없으면 미렌더)
	headerSlot?: ReactNode;
	// 팝업 창(/chat-popup)에서 렌더 시 true — 전체 창 레이아웃 + "새창으로 보기"/도크 컨트롤 숨김
	popup?: boolean;
};

export default function MainChat({ initialMessages = [], notice = null, slowModeSeconds = 0, headerSlot, popup = false }: Props)
{
	const { isAuthenticated, loginCheck } = useAuth();
	const { chatConnection, chatConnected } = useSignalRContext();

	const [messages, setMessages] = useState<ChatMessage[]>(initialMessages);
	const [systemMessages, setSystemMessages] = useState<SystemMessage[]>([]);
	const [participantCount, setParticipantCount] = useState(0);
	const [connState, setConnState] = useState<ConnectionState>('connecting');
	const [kicked, setKicked] = useState(false);
	const [inputValue, setInputValue] = useState('');
	const [cooldownLeft, setCooldownLeft] = useState(0);
	const [showJump, setShowJump] = useState(false);

	// 참여자 목록 / 설정 UI
	const [participants, setParticipants] = useState<ChatParticipant[]>([]);
	const [participantsLoading, setParticipantsLoading] = useState(false);
	const [showParticipants, setShowParticipants] = useState(false);
	const [showReceiveModal, setShowReceiveModal] = useState(false);
	const [fontScale, setFontScale] = useState(1);

	const listRef = useRef<HTMLDivElement>(null);
	const inputRef = useRef<HTMLInputElement>(null);
	const autoScrollRef = useRef(true);
	const systemIdRef = useRef(0);
	const cooldownEndRef = useRef(0);
	const activeRef = useRef(true);

	// 글씨 배율 localStorage 복원
	useEffect(() => {
		try {
			const saved = parseFloat(window.localStorage.getItem(FONT_SCALE_KEY) ?? '');
			if (!Number.isNaN(saved) && saved >= FONT_SCALE_MIN && saved <= FONT_SCALE_MAX) {
				setFontScale(saved);
			}
		} catch {}
	}, []);

	const pushSystem = useCallback((content: string) => {
		setSystemMessages((prev) => {
			const next = [...prev, { id: ++systemIdRef.current, content, receivedAt: new Date().toISOString() }];
			return next.length > MAX_SYSTEM_MESSAGES ? next.slice(next.length - MAX_SYSTEM_MESSAGES) : next;
		});
	}, []);

	const appendMessage = useCallback((message: ChatMessage) => {
		setMessages((prev) => {
			const key = messageKey(message);
			if (prev.some((c) => messageKey(c) === key)) {
				return prev;
			}
			const next = [...prev, message];
			return next.length > MAX_MESSAGES ? next.slice(next.length - MAX_MESSAGES) : next;
		});
	}, []);

	// JoinRoom/재연결 시 ReceiveHistory — SSR 초기분과 병합 (dedupe + 시간순 + 상한)
	const mergeHistory = useCallback((history: ChatMessage[]) => {
		setMessages((prev) => {
			const seen = new Set(prev.map(messageKey));
			const added = history.filter((c) => !seen.has(messageKey(c)));
			if (added.length === 0) {
				return prev;
			}
			const next = [...prev, ...added].sort((a, b) => new Date(a.sentAt).getTime() - new Date(b.sentAt).getTime());
			return next.length > MAX_MESSAGES ? next.slice(next.length - MAX_MESSAGES) : next;
		});
	}, []);

	// ── 연결 라이프사이클: provider 의 chatConnection 재사용, JoinRoom 은 마운트 시 · LeaveRoom 은 언마운트 시 ──
	useEffect(() => {
		if (!chatConnection || !chatConnected) {
			return;
		}

		activeRef.current = true;

		const handleHistory = (history: ChatMessage[]) => mergeHistory(history);
		const handleMessage = (message: ChatMessage) => appendMessage(message);
		const handleSystem = (content: string) => pushSystem(content);
		const handleCount = (count: number) => setParticipantCount(count);
		const handleParticipants = (list: ChatParticipant[]) => {
			setParticipants(list);
			setParticipantsLoading(false);
		};
		const handleKick = () => {
			setKicked(true);
			pushSystem('관리자에 의해 채팅 이용이 제한되었습니다.');
		};

		chatConnection.on('ReceiveHistory', handleHistory);
		chatConnection.on('ReceiveMessage', handleMessage);
		chatConnection.on('ReceiveSystemMessage', handleSystem);
		chatConnection.on('ReceiveParticipantCount', handleCount);
		chatConnection.on('ReceiveParticipants', handleParticipants);
		chatConnection.on('Kick', handleKick);

		// 자동 재연결 상태 UX — signalR 은 이 콜백들의 해제 API 가 없어 activeRef 로 stale 호출 가드
		chatConnection.onreconnecting(() => {
			if (activeRef.current) {
				setConnState('reconnecting');
			}
		});
		chatConnection.onreconnected(() => {
			if (!activeRef.current) {
				return;
			}
			setConnState('connected');
			// 재연결 시 그룹 멤버십 소실 → 재참가 (히스토리 재수신은 mergeHistory 가 dedupe)
			chatConnection.invoke('JoinRoom', ROOM_KEY).catch((err) => {
				console.error('채팅 룸 재참가 실패:', err);
			});
		});
		chatConnection.onclose(() => {
			if (activeRef.current) {
				setConnState('disconnected');
			}
		});

		setConnState('connected');
		chatConnection.invoke('JoinRoom', ROOM_KEY).catch((err) => {
			console.error('채팅 룸 참가 실패:', err);
			pushSystem('채팅방 입장에 실패했습니다. 새로고침 후 다시 시도해 주세요.');
		});

		return () => {
			activeRef.current = false;

			chatConnection.off('ReceiveHistory', handleHistory);
			chatConnection.off('ReceiveMessage', handleMessage);
			chatConnection.off('ReceiveSystemMessage', handleSystem);
			chatConnection.off('ReceiveParticipantCount', handleCount);
			chatConnection.off('ReceiveParticipants', handleParticipants);
			chatConnection.off('Kick', handleKick);

			if (chatConnection.state === HubConnectionState.Connected) {
				chatConnection.invoke('LeaveRoom').catch(() => {});
			}
		};
	}, [chatConnection, chatConnected, mergeHistory, appendMessage, pushSystem]);

	// 채팅 + 시스템 메시지 시간순 병합
	const merged = useMemo<MergedItem[]>(() => {
		const items: MergedItem[] = [
			...messages.map((c) => ({ kind: 'chat' as const, time: c.sentAt, data: c })),
			...systemMessages.map((c) => ({ kind: 'system' as const, time: c.receivedAt, data: c }))
		];
		items.sort((a, b) => new Date(a.time).getTime() - new Date(b.time).getTime());
		return items;
	}, [messages, systemMessages]);

	// 자동 스크롤 (하단 고정) — 위로 스크롤 중이면 "새 메시지 ↓" 점프 버튼 노출
	// 도크 접힘(display:none) 상태에서는 clientHeight===0 → 스크롤 조작 생략(펼칠 때 ResizeObserver 가 복원)
	useEffect(() => {
		const el = listRef.current;
		if (!el || merged.length === 0 || el.clientHeight === 0) {
			return;
		}
		if (autoScrollRef.current) {
			el.scrollTop = el.scrollHeight;
		} else {
			setShowJump(true);
		}
	}, [merged.length]);

	// 접힘→펼침(또는 리사이즈)으로 메시지 영역이 다시 표시되면 하단 고정 상태였을 때 하단으로 재정렬
	useEffect(() => {
		const el = listRef.current;
		if (!el || typeof ResizeObserver === 'undefined') {
			return;
		}
		const observer = new ResizeObserver(() => {
			if (el.clientHeight > 0 && autoScrollRef.current) {
				el.scrollTop = el.scrollHeight;
			}
		});
		observer.observe(el);
		return () => observer.disconnect();
	}, []);

	const handleScroll = useCallback(() => {
		const el = listRef.current;
		if (!el) {
			return;
		}
		const atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - SCROLL_BOTTOM_THRESHOLD;
		autoScrollRef.current = atBottom;
		if (atBottom) {
			setShowJump(false);
		}
	}, []);

	const jumpToBottom = useCallback(() => {
		const el = listRef.current;
		if (!el) {
			return;
		}
		el.scrollTop = el.scrollHeight;
		autoScrollRef.current = true;
		setShowJump(false);
	}, []);

	// 전송 쿨다운 (전역 2초 + 룸 SlowMode 중 큰 값) — 남은 초 표시
	const cooling = cooldownLeft > 0;
	useEffect(() => {
		if (!cooling) {
			return;
		}
		const timer = window.setInterval(() => {
			const left = Math.ceil((cooldownEndRef.current - Date.now()) / 1000);
			setCooldownLeft(left > 0 ? left : 0);
		}, 250);
		return () => window.clearInterval(timer);
	}, [cooling]);

	const handleSend = useCallback(async () => {
		const trimmed = inputValue.trim();
		if (!trimmed || trimmed.length > MAX_CONTENT_LENGTH || cooldownLeft > 0 || kicked) {
			return;
		}
		if (!chatConnection || chatConnection.state !== HubConnectionState.Connected) {
			return;
		}

		try {
			await chatConnection.invoke('SendMessage', trimmed);
			setInputValue('');

			const seconds = Math.max(GLOBAL_RATE_LIMIT_SECONDS, slowModeSeconds);
			cooldownEndRef.current = Date.now() + seconds * 1000;
			setCooldownLeft(seconds);
		} catch (error) {
			console.error('메시지 전송 실패:', error);
			pushSystem('메시지 전송에 실패했습니다. 잠시 후 다시 시도해 주세요.');
		}
	}, [inputValue, cooldownLeft, kicked, chatConnection, slowModeSeconds, pushSystem]);

	const handleKeyDown = useCallback((e: KeyboardEvent<HTMLInputElement>) => {
		if (e.key === 'Enter' && !e.nativeEvent.isComposing) {
			e.preventDefault();
			handleSend();
		}
	}, [handleSend]);

	// 비로그인 입력 게이트 — 입력창 focus(클릭·탭 포함) 시 로그인 confirm 후 blur
	const handleInputFocus = useCallback((e: FocusEvent<HTMLInputElement>) => {
		if (isAuthenticated) {
			return;
		}
		const el = e.currentTarget;
		loginCheck();
		el.blur();
	}, [isAuthenticated, loginCheck]);

	const handleInputChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
		if (!isAuthenticated) {
			return;
		}
		setInputValue(e.target.value);
	}, [isAuthenticated]);

	// 이모지 선택 — 비로그인은 로그인 유도
	const handleEmoji = useCallback((emoji: string) => {
		if (!isAuthenticated) {
			loginCheck();
			return;
		}
		setInputValue((prev) => {
			const next = prev + emoji;
			// 길이 초과 시 이모지 전체 거부 — 코드유닛 slice 로 surrogate/ZWJ 시퀀스가 잘리는 것 방지
			return next.length <= MAX_CONTENT_LENGTH ? next : prev;
		});
	}, [isAuthenticated, loginCheck]);

	// 참여자 목록 토글 — 열 때 서버에 목록 요청
	const toggleParticipants = useCallback(() => {
		setShowParticipants((prev) => {
			const next = !prev;
			if (next && chatConnection && chatConnection.state === HubConnectionState.Connected) {
				setParticipantsLoading(true);
				chatConnection.invoke('RequestParticipants').catch(() => setParticipantsLoading(false));
			}
			return next;
		});
	}, [chatConnection]);

	// 새로고침 — 메시지 비우고 히스토리·인원 재요청
	const handleRefresh = useCallback(() => {
		setMessages([]);
		setSystemMessages([]);
		if (chatConnection && chatConnection.state === HubConnectionState.Connected) {
			chatConnection.invoke('RequestHistory').catch(() => {});
			chatConnection.invoke('RequestParticipantCount').catch(() => {});
		}
	}, [chatConnection]);

	// 지우개 — 내 화면만 비우기(서버 삭제 아님)
	const handleClearLocal = useCallback(() => {
		setMessages([]);
		setSystemMessages([]);
	}, []);

	// 글씨 크게/작게
	const changeFontScale = useCallback((delta: number) => {
		setFontScale((prev) => {
			const next = Math.min(FONT_SCALE_MAX, Math.max(FONT_SCALE_MIN, Math.round((prev + delta) * 100) / 100));
			try {
				window.localStorage.setItem(FONT_SCALE_KEY, String(next));
			} catch {}
			return next;
		});
	}, []);

	// 새창으로 보기 — 별도 팝업 라우트(별개 SignalR 연결)
	const handleOpenPopup = useCallback(() => {
		window.open('/chat-popup', 'antoozaChat', 'width=400,height=760,resizable=yes,scrollbars=yes');
	}, []);

	// 수신설정 modal — 로그인 필수
	const openReceiveModal = useCallback(() => {
		if (!isAuthenticated) {
			loginCheck();
			return;
		}
		setShowReceiveModal(true);
	}, [isAuthenticated, loginCheck]);

	const canType = connState === 'connected' && !kicked;
	const canSend = isAuthenticated && canType && inputValue.trim().length > 0 && cooldownLeft === 0;

	return (
		<div
			id='chat-room'
			className={popup ? 'chat-room--popup' : undefined}
			style={{ '--chat-fs': fontScale } as CSSProperties}
		>
			{/* 헤더: 참여자 수(클릭→목록) + 연결 상태 + 수신설정 + 설정 dropdown */}
			<header className='chat-room__head'>
				<button
					type='button'
					className='chat-room__count'
					onClick={toggleParticipants}
					aria-expanded={showParticipants}
					aria-controls='chat-participants-panel'
					title='참여자 목록'
				>
					<FontAwesomeIcon icon={faUsers} />
					<span className='chat-room__count-num'>{participantCount.toLocaleString()}</span>
					<span className='chat-room__count-label'>명 참여 중</span>
					<FontAwesomeIcon icon={faChevronDown} className={`chat-room__count-caret${showParticipants ? ' is-open' : ''}`} />
				</button>

				<div className='chat-room__meta'>
					{connState === 'connecting' && (
						<span className='chat-room__state'>연결 중…</span>
					)}
					{connState === 'reconnecting' && (
						<span className='chat-room__state chat-room__state--warn' role='status'>재연결 중…</span>
					)}
					{connState === 'disconnected' && (
						<span className='chat-room__state chat-room__state--error' role='status'>연결 끊김</span>
					)}

					<button type='button' className='chat-room__icon-btn' onClick={openReceiveModal} title='수신 설정' aria-label='수신 설정'>
						<FontAwesomeIcon icon={faBell} />
					</button>

					<ChatSettingsMenu
						onRefresh={handleRefresh}
						onOpenPopup={handleOpenPopup}
						onClear={handleClearLocal}
						onFontLarger={() => changeFontScale(FONT_SCALE_STEP)}
						onFontSmaller={() => changeFontScale(-FONT_SCALE_STEP)}
						showPopup={!popup}
					/>

					{headerSlot}
				</div>
			</header>

			{/* 상단 공지 */}
			{notice && (
				<div className='chat-room__notice' role='note'>
					<FontAwesomeIcon icon={faBullhorn} />
					<span>{notice}</span>
				</div>
			)}

			{/* 메시지 리스트 (+ 참여자 목록 오버레이) */}
			<div className='chat-room__body'>
				<div className='chat-room__messages' ref={listRef} onScroll={handleScroll} aria-live='off'>
					{merged.length === 0 && (
						<div className='chat-room__empty'>아직 메시지가 없습니다. 첫 메시지를 남겨보세요.</div>
					)}
					{merged.map((item) => {
						if (item.kind === 'system') {
							return (
								<div key={`sys-${item.data.id}`} className='chat-room__system'>
									{item.data.content}
								</div>
							);
						}

						const msg = item.data;
						const badgeUrl = msg.titleIconUrl || msg.gradeImageUrl || msg.memberIcon;
						const badgeAlt = msg.titleName ?? '';
						return (
							<div key={messageKey(msg)} className='chat-room__message'>
								<time className='chat-room__time' dateTime={msg.sentAt}>{formatTime(msg.sentAt)}</time>
								{badgeUrl && (
									// eslint-disable-next-line @next/next/no-img-element
									<img src={badgeUrl} alt={badgeAlt} className='chat-room__badge' aria-hidden={!badgeAlt} />
								)}
								<span
									className='chat-room__name'
									style={msg.titleColor ? ({ '--title-color': msg.titleColor } as CSSProperties) : undefined}
								>
									{msg.memberName || msg.memberSID}
								</span>
								<span className='chat-room__content'>{msg.content}</span>
							</div>
						);
					})}
				</div>

				{showParticipants && (
					<div id='chat-participants-panel' className='chat-room__participants'>
						<ChatParticipants
							participants={participants}
							loading={participantsLoading}
							onClose={() => setShowParticipants(false)}
						/>
					</div>
				)}

				{showJump && (
					<button type='button' className='chat-room__jump' onClick={jumpToBottom}>
						<FontAwesomeIcon icon={faArrowDown} />
						<span>새 메시지</span>
					</button>
				)}
			</div>

			{/* 입력부 — 로그인 사용자만 전송. 비로그인은 입력창 focus 시 로그인 유도 */}
			<div className='chat-room__footer'>
				{kicked ? (
					<div className='chat-room__blocked' role='status'>
						관리자에 의해 채팅 이용이 제한되었습니다.
					</div>
				) : (
					<div className='chat-room__input-row'>
						<input
							ref={inputRef}
							type='text'
							className='chat-room__input'
							placeholder={isAuthenticated ? '메시지를 입력하세요' : '로그인 후 채팅에 참여하세요'}
							value={inputValue}
							onChange={handleInputChange}
							onFocus={handleInputFocus}
							onKeyDown={handleKeyDown}
							maxLength={MAX_CONTENT_LENGTH}
							disabled={isAuthenticated && !canType}
							aria-label='채팅 메시지 입력'
						/>
						<EmojiPicker onEmojiSelect={handleEmoji} />
						<button type='button' className='chat-room__send' title='전송' onClick={handleSend} disabled={!canSend}>
							{cooldownLeft > 0 ? (
								<span className='chat-room__cooldown'>{cooldownLeft}</span>
							) : (
								<FontAwesomeIcon icon={faPaperPlane} />
							)}
						</button>
					</div>
				)}
			</div>

			{showReceiveModal && <ChatReceiveModal onClose={() => setShowReceiveModal(false)} />}
		</div>
	);
}
