/* eslint-disable @typescript-eslint/no-unused-vars */
/**
 * Tests for referrals migration repository — dual-write pattern.
 */

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { MemoryDatastoreProvider } from '@bytelyst/datastore';
import { setProvider, _resetDatastoreProvider } from '../../lib/datastore.js';
import * as migrationRepo from './migration-repository.js';
import type { ReferralDoc } from './types.js';

const baseReferral: ReferralDoc = {
  id: 'ref_1',
  productId: 'lysnrai',
  referrerId: 'user_1',
  referrerEmail: 'referrer@example.com',
  referredUserId: null,
  referredEmail: 'new@example.com',
  status: 'pending',
  referrerRewardTokens: 1000,
  referredRewardTokens: 500,
  referrerRewarded: false,
  referredRewarded: false,
  createdAt: '2026-02-16T00:00:00Z',
  completedAt: null,
};

describe('migration repository', () => {
  let originalEnv: string | undefined;

  beforeEach(() => {
    setProvider(new MemoryDatastoreProvider());
    originalEnv = process.env.REFERRAL_MIGRATION_MODE;
    process.env.REFERRAL_MIGRATION_MODE = 'dual-write';
  });

  afterEach(() => {
    _resetDatastoreProvider();
    process.env.REFERRAL_MIGRATION_MODE = originalEnv;
  });

  describe('dual-write mode', () => {
    it('create writes to both containers', async () => {
      await migrationRepo.create(baseReferral);

      // Should be able to read from new container
      const fromNew = await migrationRepo.getById('ref_1', 'user_1');
      expect(fromNew).toEqual(baseReferral);
    });

    it('getById returns doc from new container', async () => {
      await migrationRepo.create(baseReferral);
      const result = await migrationRepo.getById('ref_1', 'user_1');
      expect(result).toEqual(baseReferral);
    });

    it('getByReferrer returns merged results', async () => {
      await migrationRepo.create(baseReferral);
      await migrationRepo.create({ ...baseReferral, id: 'ref_2', referrerId: 'user_1' });

      const results = await migrationRepo.getByReferrer('user_1', 'lysnrai');
      expect(results).toHaveLength(2);
    });

    it('update modifies doc in new container', async () => {
      await migrationRepo.create(baseReferral);
      const updated = await migrationRepo.update('ref_1', 'user_1', { status: 'signed_up' });

      expect(updated).not.toBeNull();
      expect(updated!.status).toBe('signed_up');
    });

    it('getByReferredEmail finds doc by email', async () => {
      await migrationRepo.create(baseReferral);
      const result = await migrationRepo.getByReferredEmail('new@example.com', 'lysnrai');
      expect(result).toEqual(baseReferral);
    });

    it('countReferrals returns correct counts', async () => {
      await migrationRepo.create(baseReferral);
      await migrationRepo.create({ ...baseReferral, id: 'ref_2', status: 'signed_up' });
      await migrationRepo.create({ ...baseReferral, id: 'ref_3', status: 'rewarded' });

      const counts = await migrationRepo.countReferrals('lysnrai');
      expect(counts.total).toBe(3);
      expect(counts.completed).toBe(2);
      expect(counts.rewarded).toBe(1);
    });
  });

  describe('new-only mode', () => {
    beforeEach(() => {
      process.env.REFERRAL_MIGRATION_MODE = 'new-only';
    });

    it('getById only reads from new container', async () => {
      await migrationRepo.create(baseReferral);
      const result = await migrationRepo.getById('ref_1', 'user_1');
      expect(result).toEqual(baseReferral);
    });

    it('listAll returns only new container docs', async () => {
      await migrationRepo.create(baseReferral);
      const results = await migrationRepo.listAll(100, 0, 'lysnrai');
      expect(results).toHaveLength(1);
    });
  });

  describe('old-only mode', () => {
    beforeEach(() => {
      process.env.REFERRAL_MIGRATION_MODE = 'old-only';
    });

    it('reads from old container (shared storage in memory provider)', async () => {
      // In old-only mode with MemoryDatastoreProvider, the storage is shared
      // so documents created in dual-write mode are still visible
      // This is a test artifact - real Cosmos containers are separate
      await migrationRepo.create(baseReferral);
      const result = await migrationRepo.getById('ref_1', 'user_1');
      // With MemoryDatastoreProvider, old and new collections share the same underlying storage
      expect(result).not.toBeNull();
    });
  });

  describe('backfill operations', () => {
    it('backfillOldToNew migrates docs without referrerId errors', async () => {
      // Create doc with valid referrerId
      await migrationRepo.create(baseReferral);

      const result = await migrationRepo.backfillOldToNew('lysnrai', 100);
      expect(result.migrated).toBe(0); // Already in new container via dual-write
      expect(result.errors).toHaveLength(0);
    });

    it('verifyConsistency detects pending backfill', async () => {
      // Create doc via normal repo (only writes to new in dual-write)
      await migrationRepo.create(baseReferral);

      // Should be consistent since dual-write puts it in both
      const inconsistencies = await migrationRepo.verifyConsistency('lysnrai');
      const realIssues = inconsistencies.filter(i => !i.issue.includes('pending backfill'));
      expect(realIssues).toHaveLength(0);
    });
  });

  describe('getMigrationStatus', () => {
    it('returns correct counts', async () => {
      await migrationRepo.create(baseReferral);
      await migrationRepo.create({ ...baseReferral, id: 'ref_2', referrerId: 'user_2' });

      const status = await migrationRepo.getMigrationStatus('lysnrai');
      expect(status.mode).toBe('dual-write');
      expect(status.newContainerCount).toBe(2);
    });
  });
});

describe('migration repository with missing referrerId', () => {
  beforeEach(() => {
    setProvider(new MemoryDatastoreProvider());
    process.env.REFERRAL_MIGRATION_MODE = 'dual-write';
  });

  afterEach(() => {
    _resetDatastoreProvider();
  });

  it('backfill skips docs without referrerId', async () => {
    // We can't easily test this with MemoryDatastoreProvider
    // since it doesn't have separate containers
    // This would be tested via integration tests with real Cosmos
    const result = await migrationRepo.backfillOldToNew('lysnrai', 100);
    expect(result.errors).toHaveLength(0);
  });
});
