'use client';

/**
 * useDraftAutoSave
 * 편집 중 자동 임시저장 커스텀 훅
 *
 * - localStorage: debounce 1초
 * - Firestore: debounce 3초 + 최대 30초 강제 저장 (setInterval)
 * - contentHash(SHA-256 앞 16자)로 중복 저장 방지
 * - Page Visibility API: 탭 비활성 시 타이머 정지, 복귀 시 즉시 저장
 * - beforeunload: 미저장 변경 시 이탈 방지
 * - 오프라인 감지: navigator.onLine false 시 Firestore 저장 스킵
 */

import { useState, useEffect, useRef, useCallback } from 'react';
import { db } from '@/lib/firebase';
import { COLLECTIONS } from '@/types/firestore';
import {
    doc,
    setDoc,
    getDoc,
    deleteDoc,
    serverTimestamp,
    Timestamp,
} from 'firebase/firestore';

// ── 타입 정의 ────────────────────────────────────────────────────────────────

export interface UseDraftAutoSaveOptions {
    docId: string;
    userId: string | undefined;
    isEditing: boolean;
    editContent: string;
    editTitle: string;
    editCategory?: string;
}

export interface UseDraftAutoSaveReturn {
    draftStatus: 'idle' | 'saving' | 'saved' | 'error';
    lastSavedAt: Date | null;
    hasDraft: boolean;
    draftData: { title: string; content: string; category?: string; savedAt: Date } | null;
    checkForDraft: () => Promise<void>;
    clearDraft: () => Promise<void>;
    restoreDraft: () => { title: string; content: string; category?: string } | null;
}

// ── localStorage 직렬화 구조 ────────────────────────────────────────────────

interface LocalDraftPayload {
    title: string;
    content: string;
    category?: string;
    contentHash: string;
    savedAt: string; // ISO 문자열
}

// ── 상수 ────────────────────────────────────────────────────────────────────

const LS_DEBOUNCE_MS = 1_000;   // localStorage 저장 debounce
const FS_DEBOUNCE_MS = 3_000;   // Firestore 저장 debounce
const FS_FORCE_INTERVAL_MS = 30_000; // Firestore 강제 저장 주기
const DRAFT_TTL_MS = 24 * 60 * 60 * 1_000; // 24시간 (ms)

// ── contentHash ──────────────────────────────────────────────────────────────

export async function computeContentHash(content: string): Promise<string> {
    const encoder = new TextEncoder();
    const data = encoder.encode(content);
    const hashBuffer = await crypto.subtle.digest('SHA-256', data);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    return hashArray.map(b => b.toString(16).padStart(2, '0')).join('').slice(0, 16);
}

// ── 훅 본체 ──────────────────────────────────────────────────────────────────

export function useDraftAutoSave({
    docId,
    userId,
    isEditing,
    editContent,
    editTitle,
    editCategory,
}: UseDraftAutoSaveOptions): UseDraftAutoSaveReturn {

    const [draftStatus, setDraftStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle');
    const [lastSavedAt, setLastSavedAt] = useState<Date | null>(null);
    const [hasDraft, setHasDraft] = useState(false);
    const [draftData, setDraftData] = useState<{
        title: string;
        content: string;
        category?: string;
        savedAt: Date;
    } | null>(null);

    // ref: 타이머 핸들
    const lsDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
    const fsDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
    const fsIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);

    // ref: 마지막으로 Firestore에 저장한 contentHash (중복 방지)
    const lastFsHashRef = useRef<string | null>(null);
    // ref: 마지막으로 localStorage에 저장한 contentHash
    const lastLsHashRef = useRef<string | null>(null);
    // ref: 편집 중 미저장 여부
    const hasPendingChangesRef = useRef(false);
    // ref: Page Visibility로 타이머가 멈춘 상태인지
    const isHiddenRef = useRef(false);

    // ── 키 헬퍼 ────────────────────────────────────────────────────────────

    const lsKey = `insuwiki_autosave_${docId}_${userId}`;
    const fsDocId = `${userId}--${docId}`;

    // ── localStorage 저장 ────────────────────────────────────────────────────

    const saveToLocalStorage = useCallback(async (
        title: string,
        content: string,
        category: string | undefined,
    ) => {
        if (!userId) return;

        const hash = await computeContentHash(content);
        if (hash === lastLsHashRef.current) return; // 변경 없음, 스킵

        const payload: LocalDraftPayload = {
            title,
            content,
            category,
            contentHash: hash,
            savedAt: new Date().toISOString(),
        };

        try {
            localStorage.setItem(lsKey, JSON.stringify(payload));
            lastLsHashRef.current = hash;
        } catch (err) {
            console.error('[useDraftAutoSave] localStorage 저장 실패:', err);
        }
    }, [userId, lsKey]);

    // ── Firestore 저장 ───────────────────────────────────────────────────────

    const saveToFirestore = useCallback(async (
        title: string,
        content: string,
        category: string | undefined,
    ) => {
        if (!userId) return;
        if (!navigator.onLine) return; // 오프라인 스킵

        const hash = await computeContentHash(content);
        if (hash === lastFsHashRef.current) return; // 동일 내용, 스킵

        setDraftStatus('saving');
        try {
            const now = new Date();
            const savedAtTs = Timestamp.fromDate(now);
            const expiresAtTs = Timestamp.fromDate(new Date(now.getTime() + DRAFT_TTL_MS));

            await setDoc(
                doc(db, COLLECTIONS.AUTO_SAVE_DRAFTS, fsDocId),
                {
                    documentId: docId,
                    userId,
                    title,
                    content,
                    contentHash: hash,
                    category: category ?? null,
                    savedAt: savedAtTs,
                    expiresAt: expiresAtTs,
                    schemaVersion: 1,
                },
            );

            lastFsHashRef.current = hash;
            setLastSavedAt(now);
            setDraftStatus('saved');
        } catch (err) {
            console.error('[useDraftAutoSave] Firestore 저장 실패:', err);
            setDraftStatus('error');
        }
    }, [userId, docId, fsDocId]);

    // ── debounce 저장 트리거 ─────────────────────────────────────────────────

    const triggerSave = useCallback((
        title: string,
        content: string,
        category: string | undefined,
    ) => {
        hasPendingChangesRef.current = true;

        // localStorage debounce 1초
        if (lsDebounceRef.current) clearTimeout(lsDebounceRef.current);
        lsDebounceRef.current = setTimeout(() => {
            saveToLocalStorage(title, content, category);
        }, LS_DEBOUNCE_MS);

        // Firestore debounce 3초
        if (!isHiddenRef.current) {
            if (fsDebounceRef.current) clearTimeout(fsDebounceRef.current);
            fsDebounceRef.current = setTimeout(() => {
                saveToFirestore(title, content, category);
            }, FS_DEBOUNCE_MS);
        }
    }, [saveToLocalStorage, saveToFirestore]);

    // ── Firestore 강제 저장 interval ────────────────────────────────────────

    const startFsInterval = useCallback(() => {
        if (fsIntervalRef.current) clearInterval(fsIntervalRef.current);
        fsIntervalRef.current = setInterval(() => {
            if (!isHiddenRef.current && isEditing) {
                saveToFirestore(editTitle, editContent, editCategory);
            }
        }, FS_FORCE_INTERVAL_MS);
    }, [isEditing, editTitle, editContent, editCategory, saveToFirestore]);

    const stopFsInterval = useCallback(() => {
        if (fsIntervalRef.current) {
            clearInterval(fsIntervalRef.current);
            fsIntervalRef.current = null;
        }
    }, []);

    // ── isEditing 변화 감지: 편집 시작/종료 ────────────────────────────────

    useEffect(() => {
        if (isEditing) {
            // 편집 시작: interval 시작
            startFsInterval();
        } else {
            // 편집 종료: 모든 타이머 정리
            stopFsInterval();
            if (lsDebounceRef.current) clearTimeout(lsDebounceRef.current);
            if (fsDebounceRef.current) clearTimeout(fsDebounceRef.current);
            hasPendingChangesRef.current = false;
        }
        // interval은 isEditing/content/title이 바뀔 때마다 재등록해야 최신 ref를 캡처
        // 그러나 startFsInterval 자체가 useCallback deps로 묶여 있음.
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [isEditing]);

    // ── 편집 내용 변화 감지: 저장 트리거 ────────────────────────────────────

    useEffect(() => {
        if (!isEditing || !userId) return;
        triggerSave(editTitle, editContent, editCategory);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [editContent, editTitle, editCategory, isEditing, userId]);

    // ── interval을 내용 변화 시 재시작 (최신 클로저 유지) ───────────────────

    useEffect(() => {
        if (!isEditing) return;
        startFsInterval();
        return stopFsInterval;
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [editContent, editTitle, editCategory, isEditing]);

    // ── Page Visibility API ──────────────────────────────────────────────────

    useEffect(() => {
        const handleVisibilityChange = () => {
            if (document.hidden) {
                // 탭 비활성: 타이머 일시 정지
                isHiddenRef.current = true;
                stopFsInterval();
                if (fsDebounceRef.current) {
                    clearTimeout(fsDebounceRef.current);
                    fsDebounceRef.current = null;
                }
            } else {
                // 탭 복귀: 즉시 1회 Firestore 저장 + 타이머 재시작
                isHiddenRef.current = false;
                if (isEditing && userId) {
                    saveToFirestore(editTitle, editContent, editCategory);
                    startFsInterval();
                }
            }
        };

        document.addEventListener('visibilitychange', handleVisibilityChange);
        return () => {
            document.removeEventListener('visibilitychange', handleVisibilityChange);
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [isEditing, editTitle, editContent, editCategory, userId]);

    // ── beforeunload 이탈 방지 ───────────────────────────────────────────────

    useEffect(() => {
        const handleBeforeUnload = (e: BeforeUnloadEvent) => {
            if (isEditing && hasPendingChangesRef.current) {
                e.preventDefault();
                e.returnValue = '';
            }
        };

        if (isEditing) {
            window.addEventListener('beforeunload', handleBeforeUnload);
        }
        return () => {
            window.removeEventListener('beforeunload', handleBeforeUnload);
        };
    }, [isEditing]);

    // ── 컴포넌트 unmount 시 cleanup ──────────────────────────────────────────

    useEffect(() => {
        return () => {
            if (lsDebounceRef.current) clearTimeout(lsDebounceRef.current);
            if (fsDebounceRef.current) clearTimeout(fsDebounceRef.current);
            stopFsInterval();
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    // ── checkForDraft ────────────────────────────────────────────────────────

    /**
     * 편집 화면 진입 시 호출.
     * localStorage + Firestore 양쪽을 확인하고 더 최신 데이터를 draftData에 저장.
     * 현재 문서 내용과 contentHash가 동일하면 null(불필요한 복구 방지).
     */
    const checkForDraft = useCallback(async () => {
        if (!userId) return;

        // 현재 문서 내용의 hash (비교용)
        const currentHash = await computeContentHash(editContent);

        let lsDraft: { title: string; content: string; category?: string; savedAt: Date; contentHash: string } | null = null;
        let fsDraft: { title: string; content: string; category?: string; savedAt: Date; contentHash: string } | null = null;

        // 1. localStorage 확인
        try {
            const raw = localStorage.getItem(lsKey);
            if (raw) {
                const parsed: LocalDraftPayload = JSON.parse(raw);
                lsDraft = {
                    title: parsed.title,
                    content: parsed.content,
                    category: parsed.category,
                    savedAt: new Date(parsed.savedAt),
                    contentHash: parsed.contentHash,
                };
            }
        } catch (err) {
            console.warn('[useDraftAutoSave] localStorage 파싱 실패:', err);
        }

        // 2. Firestore 확인
        try {
            const snap = await getDoc(doc(db, COLLECTIONS.AUTO_SAVE_DRAFTS, fsDocId));
            if (snap.exists()) {
                const data = snap.data();
                const savedAtDate = (data.savedAt as Timestamp).toDate();
                fsDraft = {
                    title: data.title,
                    content: data.content,
                    category: data.category ?? undefined,
                    savedAt: savedAtDate,
                    contentHash: data.contentHash,
                };
            }
        } catch (err) {
            console.warn('[useDraftAutoSave] Firestore 임시저장본 확인 실패:', err);
        }

        // 3. 더 최신 타임스탬프 선택
        let candidate: typeof lsDraft = null;
        if (lsDraft && fsDraft) {
            candidate = lsDraft.savedAt >= fsDraft.savedAt ? lsDraft : fsDraft;
        } else {
            candidate = lsDraft ?? fsDraft;
        }

        if (!candidate) {
            setHasDraft(false);
            setDraftData(null);
            return;
        }

        // 4. contentHash가 현재 문서와 동일하면 복구 불필요
        if (candidate.contentHash === currentHash) {
            setHasDraft(false);
            setDraftData(null);
            return;
        }

        setHasDraft(true);
        setDraftData({
            title: candidate.title,
            content: candidate.content,
            category: candidate.category,
            savedAt: candidate.savedAt,
        });
    }, [userId, lsKey, fsDocId, editContent]);

    // ── clearDraft ───────────────────────────────────────────────────────────

    const clearDraft = useCallback(async () => {
        if (!userId) return;

        // localStorage 삭제
        try {
            localStorage.removeItem(lsKey);
            lastLsHashRef.current = null;
        } catch (err) {
            console.warn('[useDraftAutoSave] localStorage 삭제 실패:', err);
        }

        // Firestore 삭제
        try {
            await deleteDoc(doc(db, COLLECTIONS.AUTO_SAVE_DRAFTS, fsDocId));
            lastFsHashRef.current = null;
        } catch (err) {
            console.warn('[useDraftAutoSave] Firestore 임시저장본 삭제 실패:', err);
        }

        setHasDraft(false);
        setDraftData(null);
        setDraftStatus('idle');
    }, [userId, lsKey, fsDocId]);

    // ── restoreDraft ─────────────────────────────────────────────────────────

    const restoreDraft = useCallback((): { title: string; content: string; category?: string } | null => {
        if (!draftData) return null;
        return {
            title: draftData.title,
            content: draftData.content,
            category: draftData.category,
        };
    }, [draftData]);

    return {
        draftStatus,
        lastSavedAt,
        hasDraft,
        draftData,
        checkForDraft,
        clearDraft,
        restoreDraft,
    };
}
