/**
 * reliabilityBatchRecalc Cloud Function 단위 테스트
 *
 * vi.mock으로 firebase-admin, firebase-functions 모킹
 * 기존 reviewOnCreate.test.ts 패턴 참고
 */

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

// ── mock 상태 (테스트 간 공유) ───────────────────────────────────────────────

declare global {
  // eslint-disable-next-line no-var
  var __batchTest: {
    documents: Array<{
      id: string;
      data: Record<string, unknown>;
      reviews: Array<{ decision: string }>;
    }>;
    batchUpdateCalls: ReturnType<typeof vi.fn>;
    batchCommitCalls: ReturnType<typeof vi.fn>;
    loggerInfo: ReturnType<typeof vi.fn>;
  };
}

globalThis.__batchTest = {
  documents: [],
  batchUpdateCalls: vi.fn(),
  batchCommitCalls: vi.fn().mockResolvedValue(undefined),
  loggerInfo: vi.fn(),
};

// ── firebase-admin mock ──────────────────────────────────────────────────────

vi.mock('firebase-admin', () => {
  const makeBatch = () => ({
    update: (...args: unknown[]) => globalThis.__batchTest.batchUpdateCalls(...args),
    commit: () => globalThis.__batchTest.batchCommitCalls(),
  });

  const makeDocRef = (docData: Record<string, unknown>, reviews: Array<{ decision: string }>, docId: string) => {
    const docRef = {
      id: docId,
      update: vi.fn().mockResolvedValue(undefined),
      collection: (_sub: string) => ({
        get: async () => ({
          size: reviews.length,
          docs: reviews.map((r, i) => ({
            id: `review-${i}`,
            data: () => r,
          })),
        }),
      }),
    };
    return {
      id: docId,
      data: () => docData,
      ref: docRef,
    };
  };

  const firestoreInstance = () => {
    const collectionFn = (_name: string) => {
      let pageCall = 0;

      const buildQuery = (docs: typeof globalThis.__batchTest.documents) => ({
        orderBy: (_field: string) => ({
          limit: (size: number) => ({
            startAfter: (_cursor: unknown) => ({
              get: async () => {
                const pageDocs = docs.slice(0, size);
                return {
                  empty: pageDocs.length === 0,
                  size: pageDocs.length,
                  docs: pageDocs.map((d) =>
                    makeDocRef(d.data, d.reviews, d.id)
                  ),
                };
              },
            }),
            get: async () => {
              const allDocs = globalThis.__batchTest.documents;
              const startIdx = pageCall * size;
              const pageDocs = allDocs.slice(startIdx, startIdx + size);
              pageCall++;
              return {
                empty: pageDocs.length === 0,
                size: pageDocs.length,
                docs: pageDocs.map((d) =>
                  makeDocRef(d.data, d.reviews, d.id)
                ),
              };
            },
          }),
        }),
      });

      return buildQuery(globalThis.__batchTest.documents);
    };

    return {
      collection: (name: string) => collectionFn(name),
      batch: () => makeBatch(),
    };
  };

  firestoreInstance.FieldValue = {
    serverTimestamp: () => 'SERVER_TIMESTAMP',
  };

  return {
    firestore: firestoreInstance,
    apps: [{}],
    initializeApp: () => {},
  };
});

// ── firebase-functions mock ───────────────────────────────────────────────────

vi.mock('firebase-functions', () => ({
  logger: {
    info: (...args: unknown[]) => globalThis.__batchTest.loggerInfo(...args),
    warn: vi.fn(),
    error: vi.fn(),
  },
  pubsub: {
    schedule: (_cron: string) => ({
      timeZone: (_tz: string) => ({
        onRun: (handler: () => Promise<void>) => handler,
      }),
    }),
  },
}));

// ── 모듈 import ───────────────────────────────────────────────────────────────

import { recalculateReliabilityScores } from '../reliabilityBatchRecalc';

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

function makeDoc(
  id: string,
  overrides: Record<string, unknown> = {},
  reviews: Array<{ decision: string }> = []
) {
  return {
    id,
    data: {
      verificationStatus: 'auto_passed',
      status: 'approved',
      authorityTier: 2,
      sourceType: 'newsletter',
      hasSourceRef: true,
      sourceRefQuality: 'medium',
      updatedAt: { toDate: () => new Date('2026-01-01') },
      ...overrides,
    },
    reviews,
  };
}

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

describe('recalculateReliabilityScores', () => {
  beforeEach(() => {
    globalThis.__batchTest.batchUpdateCalls.mockClear();
    globalThis.__batchTest.batchCommitCalls.mockClear();
    globalThis.__batchTest.loggerInfo.mockClear();
    globalThis.__batchTest.batchCommitCalls.mockResolvedValue(undefined);
  });

  it('빈 컬렉션 → 에러 없이 완료, 처리 건수 0 로깅', async () => {
    globalThis.__batchTest.documents = [];

    await expect(
      (recalculateReliabilityScores as unknown as () => Promise<void>)()
    ).resolves.not.toThrow();

    // 빈 컬렉션이면 batch.update 호출 없음
    expect(globalThis.__batchTest.batchUpdateCalls).not.toHaveBeenCalled();
    expect(globalThis.__batchTest.loggerInfo).toHaveBeenCalledWith(
      expect.stringContaining('0건')
    );
  });

  it('문서 1건 → batch.update 1회 호출, reliabilityScores 포함', async () => {
    globalThis.__batchTest.documents = [
      makeDoc('doc-1', {}, [{ decision: 'approve' }]),
    ];

    await (recalculateReliabilityScores as unknown as () => Promise<void>)();

    expect(globalThis.__batchTest.batchUpdateCalls).toHaveBeenCalledTimes(1);

    const updateArg = globalThis.__batchTest.batchUpdateCalls.mock.calls[0][1] as Record<string, unknown>;
    expect(updateArg).toHaveProperty('reliabilityScores');

    const scores = updateArg.reliabilityScores as Record<string, unknown>;
    expect(scores).toHaveProperty('compositeScore');
    expect(scores).toHaveProperty('dimensions');
    expect(scores).toHaveProperty('knockedOut');
  });

  it('문서 3건 → batch.update 3회 호출', async () => {
    globalThis.__batchTest.documents = [
      makeDoc('doc-1', {}, [{ decision: 'approve' }]),
      makeDoc('doc-2', {}, [{ decision: 'approve' }, { decision: 'approve' }]),
      makeDoc('doc-3', {}, []),
    ];

    await (recalculateReliabilityScores as unknown as () => Promise<void>)();

    expect(globalThis.__batchTest.batchUpdateCalls).toHaveBeenCalledTimes(3);
    expect(globalThis.__batchTest.loggerInfo).toHaveBeenCalledWith(
      expect.stringContaining('3건')
    );
  });

  it('batch.commit이 호출됨 (문서가 있는 경우)', async () => {
    globalThis.__batchTest.documents = [
      makeDoc('doc-1', {}, [{ decision: 'approve' }]),
    ];

    await (recalculateReliabilityScores as unknown as () => Promise<void>)();

    expect(globalThis.__batchTest.batchCommitCalls).toHaveBeenCalled();
  });

  it('리뷰 없는 문서 → review 점수 0 → knockedOut=true', async () => {
    globalThis.__batchTest.documents = [
      makeDoc('doc-knockout', {
        verificationStatus: 'expert_verified',
        authorityTier: 1,
        sourceType: 'regulation',
        sourceRef: 'https://example.com/ref',
        sourceRefQuality: 'high',
        updatedAt: { toDate: () => new Date('2026-04-10') },
      }, []),  // 리뷰 없음
    ];

    await (recalculateReliabilityScores as unknown as () => Promise<void>)();

    const updateArg = globalThis.__batchTest.batchUpdateCalls.mock.calls[0][1] as Record<string, unknown>;
    const scores = updateArg.reliabilityScores as { knockedOut: boolean; compositeScore: number };
    expect(scores.knockedOut).toBe(true);
    expect(scores.compositeScore).toBe(0.1);
  });

  it('완료 후 처리 건수를 logger.info로 로깅', async () => {
    globalThis.__batchTest.documents = [
      makeDoc('doc-1', {}, [{ decision: 'approve' }]),
      makeDoc('doc-2', {}, [{ decision: 'approve' }]),
    ];

    await (recalculateReliabilityScores as unknown as () => Promise<void>)();

    expect(globalThis.__batchTest.loggerInfo).toHaveBeenCalledWith(
      expect.stringContaining('2건')
    );
  });

  it('updatedAt이 없는 문서도 에러 없이 처리 (기본값 사용)', async () => {
    globalThis.__batchTest.documents = [
      makeDoc('doc-no-date', {
        updatedAt: undefined,  // toDate 없음
      }, [{ decision: 'approve' }]),
    ];

    await expect(
      (recalculateReliabilityScores as unknown as () => Promise<void>)()
    ).resolves.not.toThrow();
  });

  it('sourceRef 필드 있는 문서 → hasSourceRef=true 처리', async () => {
    globalThis.__batchTest.documents = [
      makeDoc('doc-with-ref', {
        sourceRef: 'https://example.com/doc',
        sourceRefQuality: 'high',
        updatedAt: { toDate: () => new Date('2026-04-01') },
      }, [{ decision: 'approve' }]),
    ];

    await (recalculateReliabilityScores as unknown as () => Promise<void>)();

    const updateArg = globalThis.__batchTest.batchUpdateCalls.mock.calls[0][1] as Record<string, unknown>;
    const scores = updateArg.reliabilityScores as { dimensions: Record<string, number> };
    // sourceRef가 있으므로 sourceRef 점수가 0이 아님
    expect(scores.dimensions.sourceRef).toBeGreaterThan(0);
  });
});
