/**
 * activity-tracker.ts 단위 테스트
 *
 * 테스트 대상:
 *  - trackActivity(): 첫 호출 / 5분 이내 재호출 / 5분 이후 호출 / Firestore 실패 시 재시도
 */

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

// ─────────────────────────────────────────────
// vi.hoisted()로 mock 함수 선언 (hoisting 안전)
// ─────────────────────────────────────────────

const { mockUpdate, mockDoc, mockCollection, mockGetFirestore, mockGetFirebaseAdmin } = vi.hoisted(() => {
    const mockUpdate = vi.fn();
    const mockDoc = vi.fn(() => ({ update: mockUpdate }));
    const mockCollection = vi.fn(() => ({ doc: mockDoc }));
    const mockGetFirestore = vi.fn(() => ({ collection: mockCollection }));
    const mockGetFirebaseAdmin = vi.fn();
    return { mockUpdate, mockDoc, mockCollection, mockGetFirestore, mockGetFirebaseAdmin };
});

vi.mock('server-only', () => ({}));

vi.mock('firebase-admin/firestore', () => ({
    getFirestore: mockGetFirestore,
    FieldValue: {
        serverTimestamp: vi.fn(() => 'SERVER_TIMESTAMP'),
    },
}));

vi.mock('@/lib/firebase-admin', () => ({
    getFirebaseAdmin: mockGetFirebaseAdmin,
}));

// ─────────────────────────────────────────────
// 모듈 임포트 (mock 선언 이후)
// ─────────────────────────────────────────────

import { trackActivity } from '../activity-tracker';

// ─────────────────────────────────────────────
// beforeEach: mock 및 모듈 상태 초기화
// ─────────────────────────────────────────────

beforeEach(() => {
    vi.clearAllMocks();
    // Firestore update 기본 동작: resolve
    mockUpdate.mockResolvedValue(undefined);
    // 모듈의 lastUpdateMap을 초기화하기 위해 vi.resetModules는 사용하지 않고
    // 대신 Date.now()를 mock하여 시간 제어
    vi.useFakeTimers();
});

// ─────────────────────────────────────────────
// trackActivity 테스트
// ─────────────────────────────────────────────

describe('trackActivity', () => {
    it('첫 호출 시 Firestore update를 호출해야 한다', async () => {
        const uid = 'user-first-call';

        trackActivity(uid);

        // fire-and-forget이므로 Promise가 처리될 때까지 대기
        await vi.runAllTimersAsync();

        expect(mockGetFirebaseAdmin).toHaveBeenCalledOnce();
        expect(mockGetFirestore).toHaveBeenCalledOnce();
        expect(mockCollection).toHaveBeenCalledWith('users');
        expect(mockDoc).toHaveBeenCalledWith(uid);
        expect(mockUpdate).toHaveBeenCalledOnce();
        expect(mockUpdate).toHaveBeenCalledWith({ lastActiveAt: 'SERVER_TIMESTAMP' });
    });

    it('5분 이내 재호출 시 Firestore update를 호출하지 않아야 한다 (디바운스)', async () => {
        const uid = 'user-debounce';

        // 첫 호출
        trackActivity(uid);
        await vi.runAllTimersAsync();
        expect(mockUpdate).toHaveBeenCalledOnce();

        // mock 초기화 후 4분 59초 진행
        vi.clearAllMocks();
        mockUpdate.mockResolvedValue(undefined);
        vi.advanceTimersByTime(4 * 60 * 1000 + 59 * 1000); // 4분 59초

        // 5분 이내 재호출
        trackActivity(uid);
        await vi.runAllTimersAsync();

        expect(mockUpdate).not.toHaveBeenCalled();
    });

    it('5분 이후 호출 시 Firestore update를 재호출해야 한다', async () => {
        const uid = 'user-after-debounce';

        // 첫 호출
        trackActivity(uid);
        await vi.runAllTimersAsync();
        expect(mockUpdate).toHaveBeenCalledOnce();

        // mock 초기화 후 5분 1초 진행
        vi.clearAllMocks();
        mockUpdate.mockResolvedValue(undefined);
        vi.advanceTimersByTime(5 * 60 * 1000 + 1000); // 5분 1초

        // 5분 이후 재호출
        trackActivity(uid);
        await vi.runAllTimersAsync();

        expect(mockUpdate).toHaveBeenCalledOnce();
        expect(mockUpdate).toHaveBeenCalledWith({ lastActiveAt: 'SERVER_TIMESTAMP' });
    });

    it('Firestore update 실패 시 lastUpdateMap을 초기화해 다음 요청에서 재시도할 수 있어야 한다', async () => {
        const uid = 'user-firestore-error';

        // 첫 호출 시 Firestore update 실패
        mockUpdate.mockRejectedValueOnce(new Error('Firestore write failed'));

        trackActivity(uid);
        await vi.runAllTimersAsync();

        expect(mockUpdate).toHaveBeenCalledOnce();

        // mock 초기화 (시간은 진행하지 않음 - lastUpdateMap이 초기화되었으므로 바로 재시도 가능)
        vi.clearAllMocks();
        mockUpdate.mockResolvedValue(undefined);

        // 에러 후 재호출: lastUpdateMap이 삭제되었으므로 바로 Firestore update 호출되어야 함
        trackActivity(uid);
        await vi.runAllTimersAsync();

        expect(mockUpdate).toHaveBeenCalledOnce();
    });
});
