import { describe, it, expect, vi, beforeEach } from 'vitest';

// ── firebase 모킹 (useDraftAutoSave가 firebase를 import하므로) ────────────────

vi.mock('@/lib/firebase', () => ({
    db: {},
}));

vi.mock('firebase/firestore', () => ({
    doc: vi.fn(),
    setDoc: vi.fn(),
    getDoc: vi.fn(),
    deleteDoc: vi.fn(),
    serverTimestamp: vi.fn(),
    Timestamp: {
        fromDate: vi.fn((d: Date) => ({ toDate: () => d })),
    },
}));

vi.mock('@/types/firestore', () => ({
    COLLECTIONS: {
        AUTO_SAVE_DRAFTS: 'autoSaveDrafts',
    },
}));

// React hook은 사용하지 않으므로 react 모킹
vi.mock('react', () => ({
    useState: vi.fn((init: unknown) => [init, vi.fn()]),
    useEffect: vi.fn(),
    useRef: vi.fn((init: unknown) => ({ current: init })),
    useCallback: vi.fn((fn: unknown) => fn),
}));

// ── computeContentHash import ─────────────────────────────────────────────────

import { computeContentHash } from '@/hooks/useDraftAutoSave';

// ── formatRelativeTime import ─────────────────────────────────────────────────
// DraftRecoveryModal은 'use client' 지시어를 포함하지만
// Node 환경에서는 해당 지시어가 무시되므로 직접 import 가능

import { formatRelativeTime } from '@/components/DraftRecoveryModal';

// ─────────────────────────────────────────────────────────────────────────────

describe('useDraftAutoSave - 순수 함수', () => {

    describe('computeContentHash', () => {

        it('빈 문자열에 대해 일관된 해시 반환', async () => {
            const hash1 = await computeContentHash('');
            const hash2 = await computeContentHash('');
            expect(hash1).toBe(hash2);
        });

        it('같은 내용은 같은 해시 반환', async () => {
            const content = '안녕하세요, InsuWiki 자동 임시저장 테스트입니다.';
            const hash1 = await computeContentHash(content);
            const hash2 = await computeContentHash(content);
            expect(hash1).toBe(hash2);
        });

        it('다른 내용은 다른 해시 반환', async () => {
            const hash1 = await computeContentHash('내용 A');
            const hash2 = await computeContentHash('내용 B');
            expect(hash1).not.toBe(hash2);
        });

        it('해시 길이가 16자', async () => {
            const hash = await computeContentHash('테스트 내용');
            expect(hash).toHaveLength(16);
        });

        it('해시는 16진수 문자만 포함', async () => {
            const hash = await computeContentHash('hex string test');
            expect(hash).toMatch(/^[0-9a-f]{16}$/);
        });

        it('공백 한 칸 차이도 다른 해시를 반환', async () => {
            const hash1 = await computeContentHash('hello world');
            const hash2 = await computeContentHash('hello  world');
            expect(hash1).not.toBe(hash2);
        });

        it('빈 문자열의 해시도 16자', async () => {
            const hash = await computeContentHash('');
            expect(hash).toHaveLength(16);
        });
    });

    describe('localStorage 키 형식', () => {

        it('올바른 키 형식 생성', () => {
            const docId = 'doc123';
            const userId = 'user456';
            const key = `insuwiki_autosave_${docId}_${userId}`;
            expect(key).toBe('insuwiki_autosave_doc123_user456');
        });

        it('키가 insuwiki_autosave_ 접두사로 시작', () => {
            const docId = 'myDoc';
            const userId = 'myUser';
            const key = `insuwiki_autosave_${docId}_${userId}`;
            expect(key.startsWith('insuwiki_autosave_')).toBe(true);
        });

        it('docId와 userId가 언더스코어로 구분', () => {
            const docId = 'doc-abc';
            const userId = 'user-xyz';
            const key = `insuwiki_autosave_${docId}_${userId}`;
            // 접두사 제거 후 분리
            const suffix = key.replace('insuwiki_autosave_', '');
            // suffix = "doc-abc_user-xyz"
            // 마지막 언더스코어를 기준으로 분리
            const lastUnderscore = suffix.lastIndexOf('_');
            const parsedDocId = suffix.slice(0, lastUnderscore);
            const parsedUserId = suffix.slice(lastUnderscore + 1);
            expect(parsedDocId).toBe(docId);
            expect(parsedUserId).toBe(userId);
        });
    });

    describe('Firestore 문서 ID 형식', () => {

        it('더블 하이픈 구분자 사용', () => {
            const userId = 'user456';
            const docId = 'doc123';
            const fsDocId = `${userId}--${docId}`;
            expect(fsDocId).toBe('user456--doc123');
            expect(fsDocId.split('--')).toHaveLength(2);
        });

        it('분리 시 userId와 docId가 올바르게 복원', () => {
            const userId = 'user-abc';
            const docId = 'doc-xyz';
            const fsDocId = `${userId}--${docId}`;
            const [parsedUserId, parsedDocId] = fsDocId.split('--');
            expect(parsedUserId).toBe(userId);
            expect(parsedDocId).toBe(docId);
        });

        it('싱글 하이픈은 구분자로 사용하지 않음 - 더블 하이픈으로만 분리', () => {
            const userId = 'user-with-hyphens';
            const docId = 'doc-with-hyphens';
            const fsDocId = `${userId}--${docId}`;
            // '--' 로 split하면 정확히 2개
            expect(fsDocId.split('--')).toHaveLength(2);
            // '-' 로 split하면 여러 개 (더블 하이픈이 구분자임을 확인)
            expect(fsDocId.split('-').length).toBeGreaterThan(2);
        });

        it('userId가 앞, docId가 뒤 순서로 구성', () => {
            const userId = 'userFirst';
            const docId = 'docSecond';
            const fsDocId = `${userId}--${docId}`;
            expect(fsDocId.startsWith(userId)).toBe(true);
            expect(fsDocId.endsWith(docId)).toBe(true);
        });
    });
});

// ─────────────────────────────────────────────────────────────────────────────

describe('DraftRecoveryModal - formatRelativeTime', () => {

    it('60초 미만이면 "방금 전" 반환', () => {
        const now = new Date();
        const date = new Date(now.getTime() - 30 * 1000); // 30초 전
        expect(formatRelativeTime(date)).toBe('방금 전');
    });

    it('정확히 59초 전도 "방금 전" 반환', () => {
        const now = new Date();
        const date = new Date(now.getTime() - 59 * 1000);
        expect(formatRelativeTime(date)).toBe('방금 전');
    });

    it('60분 미만이면 "N분 전" 반환', () => {
        const now = new Date();
        const date = new Date(now.getTime() - 5 * 60 * 1000); // 5분 전
        expect(formatRelativeTime(date)).toBe('5분 전');
    });

    it('정확히 1분 전은 "1분 전" 반환', () => {
        const now = new Date();
        const date = new Date(now.getTime() - 60 * 1000); // 60초 = 1분
        expect(formatRelativeTime(date)).toBe('1분 전');
    });

    it('정확히 59분 전은 "59분 전" 반환', () => {
        const now = new Date();
        const date = new Date(now.getTime() - 59 * 60 * 1000);
        expect(formatRelativeTime(date)).toBe('59분 전');
    });

    it('24시간 미만이면 "N시간 전" 반환', () => {
        const now = new Date();
        const date = new Date(now.getTime() - 3 * 60 * 60 * 1000); // 3시간 전
        expect(formatRelativeTime(date)).toBe('3시간 전');
    });

    it('정확히 1시간 전은 "1시간 전" 반환', () => {
        const now = new Date();
        const date = new Date(now.getTime() - 60 * 60 * 1000);
        expect(formatRelativeTime(date)).toBe('1시간 전');
    });

    it('정확히 23시간 전은 "23시간 전" 반환', () => {
        const now = new Date();
        const date = new Date(now.getTime() - 23 * 60 * 60 * 1000);
        expect(formatRelativeTime(date)).toBe('23시간 전');
    });

    it('24시간 이상이면 "N일 전" 반환', () => {
        const now = new Date();
        const date = new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000); // 2일 전
        expect(formatRelativeTime(date)).toBe('2일 전');
    });

    it('정확히 24시간 전은 "1일 전" 반환', () => {
        const now = new Date();
        const date = new Date(now.getTime() - 24 * 60 * 60 * 1000);
        expect(formatRelativeTime(date)).toBe('1일 전');
    });

    it('7일 전은 "7일 전" 반환', () => {
        const now = new Date();
        const date = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
        expect(formatRelativeTime(date)).toBe('7일 전');
    });
});
