/**
 * Monetization Service Tests
 */
import { describe, it, expect, beforeEach } from 'vitest';
import { Person } from '../../src/models/Person';

// Mock refill tiers
const REFILL_TIERS = [
  { id: 'instant', energy: 50, diamonds: 10, name: 'Instant Boost' },
  { id: 'small', energy: 100, diamonds: 18, name: 'Small Refill' },
  { id: 'medium', energy: 200, diamonds: 30, name: 'Medium Refill' },
  { id: 'large', energy: 500, diamonds: 60, name: 'Large Refill' },
  { id: 'unlimited', energy: -1, diamonds: 100, duration: 86400000, name: 'Unlimited (24h)' },
];

// Mock time skip tiers
const SKIP_TIERS = [
  { id: '1hour', duration: 3600000, diamonds: 5, name: 'Skip 1 Hour' },
  { id: '12hours', duration: 43200000, diamonds: 25, name: 'Skip 12 Hours' },
  { id: '1day', duration: 86400000, diamonds: 40, name: 'Skip 1 Day' },
  { id: '1week', duration: 604800000, diamonds: 150, name: 'Skip 1 Week' },
  { id: '1month', duration: 2592000000, diamonds: 500, name: 'Skip 1 Month' },
];

describe('Monetization Service', () => {
  describe('Energy Refill Tiers', () => {
    it('should have all tier IDs defined', () => {
      const expectedIds = ['instant', 'small', 'medium', 'large', 'unlimited'];

      expectedIds.forEach(id => {
        const tier = REFILL_TIERS.find(t => t.id === id);
        expect(tier).toBeDefined();
      });
    });

    it('should have increasing energy amounts', () => {
      const energyTiers = REFILL_TIERS.filter(t => t.energy > 0);

      for (let i = 1; i < energyTiers.length; i++) {
        expect(energyTiers[i].energy).toBeGreaterThan(energyTiers[i - 1].energy);
      }
    });

    it('should have increasing diamond costs', () => {
      for (let i = 1; i < REFILL_TIERS.length; i++) {
        expect(REFILL_TIERS[i].diamonds).toBeGreaterThan(REFILL_TIERS[i - 1].diamonds);
      }
    });

    it('should have names for all tiers', () => {
      REFILL_TIERS.forEach(tier => {
        expect(tier.name).toBeDefined();
        expect(tier.name.length).toBeGreaterThan(0);
      });
    });

    it('should have unlimited tier with duration', () => {
      const unlimited = REFILL_TIERS.find(t => t.id === 'unlimited');

      expect(unlimited).toBeDefined();
      expect(unlimited?.duration).toBe(86400000); // 24 hours
    });
  });

  describe('Time Skip Tiers', () => {
    it('should have all tier IDs defined', () => {
      const expectedIds = ['1hour', '12hours', '1day', '1week', '1month'];

      expectedIds.forEach(id => {
        const tier = SKIP_TIERS.find(t => t.id === id);
        expect(tier).toBeDefined();
      });
    });

    it('should have increasing durations', () => {
      for (let i = 1; i < SKIP_TIERS.length; i++) {
        expect(SKIP_TIERS[i].duration).toBeGreaterThan(SKIP_TIERS[i - 1].duration);
      }
    });

    it('should have increasing diamond costs', () => {
      for (let i = 1; i < SKIP_TIERS.length; i++) {
        expect(SKIP_TIERS[i].diamonds).toBeGreaterThan(SKIP_TIERS[i - 1].diamonds);
      }
    });

    it('should have correct duration for 1 hour', () => {
      const tier = SKIP_TIERS.find(t => t.id === '1hour');
      expect(tier?.duration).toBe(3600000);
    });

    it('should have correct duration for 1 day', () => {
      const tier = SKIP_TIERS.find(t => t.id === '1day');
      expect(tier?.duration).toBe(86400000);
    });

    it('should have correct duration for 1 week', () => {
      const tier = SKIP_TIERS.find(t => t.id === '1week');
      expect(tier?.duration).toBe(604800000);
    });

    it('should have correct duration for 1 month', () => {
      const tier = SKIP_TIERS.find(t => t.id === '1month');
      expect(tier?.duration).toBe(2592000000);
    });
  });

  describe('Energy Refill Purchase', () => {
    let player: Person;

    beforeEach(() => {
      player = new Person({
        id: 'player-1',
        energy: 50,
        diamonds: 100,
      });
    });

    it('should refill energy if enough diamonds', () => {
      const tier = REFILL_TIERS.find(t => t.id === 'small')!;

      if ((player.diamonds ?? 0) >= tier.diamonds) {
        player.diamonds = (player.diamonds ?? 0) - tier.diamonds;
        player.energy = Math.min(100, (player.energy ?? 0) + tier.energy);

        expect(player.diamonds).toBe(82);
        expect(player.energy).toBe(100); // Capped at 100
      }
    });

    it('should not exceed 100 energy', () => {
      player.energy = 80;
      const tier = REFILL_TIERS.find(t => t.id === 'small')!;

      player.energy = Math.min(100, (player.energy ?? 0) + tier.energy);

      expect(player.energy).toBe(100);
    });

    it('should fail if not enough diamonds', () => {
      player.diamonds = 5;
      const tier = REFILL_TIERS.find(t => t.id === 'large')!;

      const canAfford = (player.diamonds ?? 0) >= tier.diamonds;

      expect(canAfford).toBe(false);
    });

    it('should deduct correct diamond amount', () => {
      const tier = REFILL_TIERS.find(t => t.id === 'instant')!;
      const initialDiamonds = player.diamonds ?? 0;

      player.diamonds = initialDiamonds - tier.diamonds;

      expect(player.diamonds).toBe(initialDiamonds - 10);
    });
  });

  describe('Time Skip Purchase', () => {
    let player: Person;

    beforeEach(() => {
      player = new Person({
        id: 'player-1',
        diamonds: 200,
      });
    });

    it('should skip time if enough diamonds', () => {
      const tier = SKIP_TIERS.find(t => t.id === '1day')!;
      const canAfford = (player.diamonds ?? 0) >= tier.diamonds;

      expect(canAfford).toBe(true);

      player.diamonds = (player.diamonds ?? 0) - tier.diamonds;
      expect(player.diamonds).toBe(160);
    });

    it('should fail if not enough diamonds', () => {
      player.diamonds = 10;
      const tier = SKIP_TIERS.find(t => t.id === '1month')!;

      const canAfford = (player.diamonds ?? 0) >= tier.diamonds;

      expect(canAfford).toBe(false);
    });

    it('should calculate days skipped correctly', () => {
      const tier = SKIP_TIERS.find(t => t.id === '1week')!;
      const daysSkipped = tier.duration / 86400000;

      expect(daysSkipped).toBe(7);
    });

    it('should calculate hours skipped correctly', () => {
      const tier = SKIP_TIERS.find(t => t.id === '12hours')!;
      const hoursSkipped = tier.duration / 3600000;

      expect(hoursSkipped).toBe(12);
    });
  });

  describe('Value Calculation', () => {
    it('should have better value at higher tiers (energy)', () => {
      const instantValue = 50 / 10; // energy per diamond
      const largeValue = 500 / 60;

      expect(largeValue).toBeGreaterThan(instantValue);
    });

    it('should have better value at higher tiers (time)', () => {
      const hourValue = 3600000 / 5; // ms per diamond
      const monthValue = 2592000000 / 500;

      expect(monthValue).toBeGreaterThan(hourValue);
    });
  });

  describe('In-App Purchase Products', () => {
    const IAP_PRODUCTS = [
      { id: 'diamond1', diamonds: 100, priceUSD: 0.99 },
      { id: 'diamond2', diamonds: 500, priceUSD: 4.99 },
      { id: 'diamond3', diamonds: 1200, priceUSD: 9.99 },
      { id: 'diamond4', diamonds: 2500, priceUSD: 19.99 },
      { id: 'diamond5', diamonds: 6500, priceUSD: 49.99 },
    ];

    it('should have increasing diamond amounts', () => {
      for (let i = 1; i < IAP_PRODUCTS.length; i++) {
        expect(IAP_PRODUCTS[i].diamonds).toBeGreaterThan(IAP_PRODUCTS[i - 1].diamonds);
      }
    });

    it('should have increasing prices', () => {
      for (let i = 1; i < IAP_PRODUCTS.length; i++) {
        expect(IAP_PRODUCTS[i].priceUSD).toBeGreaterThan(IAP_PRODUCTS[i - 1].priceUSD);
      }
    });

    it('should have better value at higher tiers', () => {
      const tier1Value = 100 / 0.99;
      const tier5Value = 6500 / 49.99;

      expect(tier5Value).toBeGreaterThan(tier1Value);
    });

    it('should have unique product IDs', () => {
      const ids = new Set(IAP_PRODUCTS.map(p => p.id));
      expect(ids.size).toBe(IAP_PRODUCTS.length);
    });
  });

  describe('Receipt Validation', () => {
    it('should validate receipt format', () => {
      const validReceipt = {
        transactionId: 'txn_123456',
        productId: 'diamond1',
        purchaseTime: Date.now(),
        signature: 'valid_signature_here',
      };

      expect(validReceipt.transactionId).toBeDefined();
      expect(validReceipt.productId).toBeDefined();
      expect(validReceipt.purchaseTime).toBeLessThanOrEqual(Date.now());
    });

    it('should reject expired receipts', () => {
      const expiredReceipt = {
        transactionId: 'txn_old',
        productId: 'diamond1',
        purchaseTime: Date.now() - 86400000 * 30, // 30 days ago
      };

      const isExpired = Date.now() - expiredReceipt.purchaseTime > 86400000 * 7;
      expect(isExpired).toBe(true);
    });

    it('should detect duplicate transactions', () => {
      const processedTransactions = new Set(['txn_123', 'txn_456']);
      const newTransaction = 'txn_123';

      const isDuplicate = processedTransactions.has(newTransaction);
      expect(isDuplicate).toBe(true);
    });
  });

  describe('Rate Limiting', () => {
    it('should limit purchases per time period', () => {
      const maxPurchasesPerHour = 10;
      const purchasesThisHour = 8;

      const canPurchase = purchasesThisHour < maxPurchasesPerHour;
      expect(canPurchase).toBe(true);
    });

    it('should block excessive purchases', () => {
      const maxPurchasesPerHour = 10;
      const purchasesThisHour = 12;

      const canPurchase = purchasesThisHour < maxPurchasesPerHour;
      expect(canPurchase).toBe(false);
    });
  });

  describe('Subscription Features', () => {
    const SUBSCRIPTION_TIERS = [
      { id: 'basic', priceMonthly: 2.99, bonusDiamonds: 50, noAds: false },
      { id: 'premium', priceMonthly: 9.99, bonusDiamonds: 200, noAds: true },
    ];

    it('should have subscription tiers', () => {
      expect(SUBSCRIPTION_TIERS.length).toBeGreaterThan(0);
    });

    it('should include daily bonus diamonds', () => {
      const premium = SUBSCRIPTION_TIERS.find(t => t.id === 'premium');
      expect(premium?.bonusDiamonds).toBeGreaterThan(0);
    });

    it('should have no-ads feature in premium', () => {
      const premium = SUBSCRIPTION_TIERS.find(t => t.id === 'premium');
      expect(premium?.noAds).toBe(true);
    });
  });

  describe('Free Rewards', () => {
    it('should provide daily login reward', () => {
      const dailyReward = { diamonds: 5, money: 100 };

      expect(dailyReward.diamonds).toBeGreaterThan(0);
      expect(dailyReward.money).toBeGreaterThan(0);
    });

    it('should provide streak bonuses', () => {
      const streakBonuses = [
        { day: 1, diamonds: 5 },
        { day: 7, diamonds: 50 },
        { day: 30, diamonds: 200 },
      ];

      expect(streakBonuses[2].diamonds).toBeGreaterThan(streakBonuses[0].diamonds);
    });

    it('should provide achievement rewards', () => {
      const achievementReward = { diamonds: 10, unlockMessage: 'Congratulations!' };

      expect(achievementReward.diamonds).toBeGreaterThan(0);
    });
  });
});
