/**
 * Subscription Entity Tests
 *
 * Unit tests for the Subscription domain entity
 */

import { Subscription } from './subscription.entity';
import { SubscriptionPlan } from '../value-objects/subscription-plan.vo';
import { SubscriptionStatus } from '../value-objects/subscription-status.vo';
import {
  InvalidSubscriptionDowngradeException,
  SubscriptionNotActiveException,
} from '../exceptions/subscription.exceptions';

describe('Subscription Entity', () => {
  const createValidSubscription = () => {
    return Subscription.create({
      id: 'sub-123',
      organizationId: 'org-123',
    });
  };

  describe('create', () => {
    it('should create a subscription with default BRONZE plan', () => {
      const subscription = createValidSubscription();

      expect(subscription.id).toBe('sub-123');
      expect(subscription.organizationId).toBe('org-123');
      expect(subscription.plan.value).toBe('BRONZE');
      expect(subscription.status.value).toBe('ACTIVE');
      expect(subscription.cancelAtPeriodEnd).toBe(false);
    });

    it('should create a subscription with custom plan', () => {
      const subscription = Subscription.create({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: SubscriptionPlan.silver(),
      });

      expect(subscription.plan.value).toBe('SILVER');
    });

    it('should create a subscription with Stripe IDs', () => {
      const subscription = Subscription.create({
        id: 'sub-123',
        organizationId: 'org-123',
        stripeCustomerId: 'cus_123',
        stripeSubscriptionId: 'sub_stripe_123',
      });

      expect(subscription.stripeCustomerId).toBe('cus_123');
      expect(subscription.stripeSubscriptionId).toBe('sub_stripe_123');
    });
  });

  describe('fromPersistence', () => {
    it('should reconstitute a subscription from persistence data', () => {
      const subscription = Subscription.fromPersistence({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: 'GOLD',
        status: 'ACTIVE',
        stripeCustomerId: 'cus_123',
        stripeSubscriptionId: 'sub_stripe_123',
        currentPeriodStart: new Date('2024-01-01'),
        currentPeriodEnd: new Date('2024-02-01'),
        cancelAtPeriodEnd: true,
        createdAt: new Date('2024-01-01'),
        updatedAt: new Date('2024-01-15'),
      });

      expect(subscription.id).toBe('sub-123');
      expect(subscription.plan.value).toBe('GOLD');
      expect(subscription.status.value).toBe('ACTIVE');
      expect(subscription.cancelAtPeriodEnd).toBe(true);
    });
  });

  describe('maxLicenses', () => {
    it('should return correct limits for BRONZE plan', () => {
      const subscription = createValidSubscription();
      expect(subscription.maxLicenses).toBe(1);
    });

    it('should return correct limits for SILVER plan', () => {
      const subscription = Subscription.create({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: SubscriptionPlan.silver(),
      });
      expect(subscription.maxLicenses).toBe(5);
    });

    it('should return correct limits for GOLD plan', () => {
      const subscription = Subscription.create({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: SubscriptionPlan.gold(),
      });
      expect(subscription.maxLicenses).toBe(20);
    });

    it('should return -1 for PLATINIUM plan (unlimited)', () => {
      const subscription = Subscription.create({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: SubscriptionPlan.platinium(),
      });
      expect(subscription.maxLicenses).toBe(-1);
    });
  });

  describe('isUnlimited', () => {
    it('should return false for BRONZE plan', () => {
      const subscription = createValidSubscription();
      expect(subscription.isUnlimited()).toBe(false);
    });

    it('should return true for PLATINIUM plan', () => {
      const subscription = Subscription.create({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: SubscriptionPlan.platinium(),
      });
      expect(subscription.isUnlimited()).toBe(true);
    });
  });

  describe('isActive', () => {
    it('should return true for ACTIVE status', () => {
      const subscription = createValidSubscription();
      expect(subscription.isActive()).toBe(true);
    });

    it('should return true for TRIALING status', () => {
      const subscription = Subscription.fromPersistence({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: 'BRONZE',
        status: 'TRIALING',
        stripeCustomerId: null,
        stripeSubscriptionId: null,
        currentPeriodStart: null,
        currentPeriodEnd: null,
        cancelAtPeriodEnd: false,
        createdAt: new Date(),
        updatedAt: new Date(),
      });
      expect(subscription.isActive()).toBe(true);
    });

    it('should return false for CANCELED status', () => {
      const subscription = Subscription.fromPersistence({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: 'BRONZE',
        status: 'CANCELED',
        stripeCustomerId: null,
        stripeSubscriptionId: null,
        currentPeriodStart: null,
        currentPeriodEnd: null,
        cancelAtPeriodEnd: false,
        createdAt: new Date(),
        updatedAt: new Date(),
      });
      expect(subscription.isActive()).toBe(false);
    });
  });

  describe('canAllocateLicenses', () => {
    it('should return true when licenses are available', () => {
      const subscription = createValidSubscription(); // BRONZE = 1 license
      expect(subscription.canAllocateLicenses(0, 1)).toBe(true);
    });

    it('should return false when no licenses available', () => {
      const subscription = createValidSubscription();
      expect(subscription.canAllocateLicenses(1, 1)).toBe(false); // BRONZE has 1 license max
    });

    it('should always return true for PLATINIUM plan', () => {
      const subscription = Subscription.create({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: SubscriptionPlan.platinium(),
      });
      expect(subscription.canAllocateLicenses(1000, 100)).toBe(true);
    });

    it('should return false when subscription is not active', () => {
      const subscription = Subscription.fromPersistence({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: 'BRONZE',
        status: 'CANCELED',
        stripeCustomerId: null,
        stripeSubscriptionId: null,
        currentPeriodStart: null,
        currentPeriodEnd: null,
        cancelAtPeriodEnd: false,
        createdAt: new Date(),
        updatedAt: new Date(),
      });
      expect(subscription.canAllocateLicenses(0, 1)).toBe(false);
    });
  });

  describe('canUpgradeTo', () => {
    it('should allow upgrade from BRONZE to SILVER', () => {
      const subscription = createValidSubscription();
      expect(subscription.canUpgradeTo(SubscriptionPlan.silver())).toBe(true);
    });

    it('should allow upgrade from BRONZE to GOLD', () => {
      const subscription = createValidSubscription();
      expect(subscription.canUpgradeTo(SubscriptionPlan.gold())).toBe(true);
    });

    it('should not allow downgrade via canUpgradeTo', () => {
      const subscription = Subscription.create({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: SubscriptionPlan.silver(),
      });
      expect(subscription.canUpgradeTo(SubscriptionPlan.bronze())).toBe(false);
    });
  });

  describe('canDowngradeTo', () => {
    it('should allow downgrade when user count fits', () => {
      const subscription = Subscription.create({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: SubscriptionPlan.silver(),
      });
      expect(subscription.canDowngradeTo(SubscriptionPlan.bronze(), 1)).toBe(true);
    });

    it('should prevent downgrade when user count exceeds new plan', () => {
      const subscription = Subscription.create({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: SubscriptionPlan.silver(),
      });
      expect(subscription.canDowngradeTo(SubscriptionPlan.bronze(), 5)).toBe(false);
    });
  });

  describe('updatePlan', () => {
    it('should update to new plan when valid', () => {
      const subscription = createValidSubscription();
      const updated = subscription.updatePlan(SubscriptionPlan.silver(), 1);

      expect(updated.plan.value).toBe('SILVER');
    });

    it('should throw when subscription is not active', () => {
      const subscription = Subscription.fromPersistence({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: 'BRONZE',
        status: 'CANCELED',
        stripeCustomerId: null,
        stripeSubscriptionId: null,
        currentPeriodStart: null,
        currentPeriodEnd: null,
        cancelAtPeriodEnd: false,
        createdAt: new Date(),
        updatedAt: new Date(),
      });

      expect(() => subscription.updatePlan(SubscriptionPlan.silver(), 0)).toThrow(
        SubscriptionNotActiveException
      );
    });

    it('should throw when downgrading with too many users', () => {
      const subscription = Subscription.create({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: SubscriptionPlan.gold(),
      });

      expect(() => subscription.updatePlan(SubscriptionPlan.silver(), 10)).toThrow(
        InvalidSubscriptionDowngradeException
      );
    });
  });

  describe('updateStatus', () => {
    it('should update subscription status', () => {
      const subscription = createValidSubscription();
      const updated = subscription.updateStatus(SubscriptionStatus.pastDue());

      expect(updated.status.value).toBe('PAST_DUE');
    });
  });

  describe('updateStripeCustomerId', () => {
    it('should update Stripe customer ID', () => {
      const subscription = createValidSubscription();
      const updated = subscription.updateStripeCustomerId('cus_new_123');

      expect(updated.stripeCustomerId).toBe('cus_new_123');
    });
  });

  describe('updateStripeSubscription', () => {
    it('should update Stripe subscription details', () => {
      const subscription = createValidSubscription();
      const periodStart = new Date('2024-02-01');
      const periodEnd = new Date('2024-03-01');

      const updated = subscription.updateStripeSubscription({
        stripeSubscriptionId: 'sub_new_123',
        currentPeriodStart: periodStart,
        currentPeriodEnd: periodEnd,
        cancelAtPeriodEnd: true,
      });

      expect(updated.stripeSubscriptionId).toBe('sub_new_123');
      expect(updated.currentPeriodStart).toEqual(periodStart);
      expect(updated.currentPeriodEnd).toEqual(periodEnd);
      expect(updated.cancelAtPeriodEnd).toBe(true);
    });
  });

  describe('scheduleCancellation', () => {
    it('should mark subscription for cancellation', () => {
      const subscription = createValidSubscription();
      const updated = subscription.scheduleCancellation();

      expect(updated.cancelAtPeriodEnd).toBe(true);
    });
  });

  describe('unscheduleCancellation', () => {
    it('should unmark subscription for cancellation', () => {
      const subscription = Subscription.fromPersistence({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: 'SILVER',
        status: 'ACTIVE',
        stripeCustomerId: 'cus_123',
        stripeSubscriptionId: 'sub_123',
        currentPeriodStart: new Date(),
        currentPeriodEnd: new Date(),
        cancelAtPeriodEnd: true,
        createdAt: new Date(),
        updatedAt: new Date(),
      });

      const updated = subscription.unscheduleCancellation();
      expect(updated.cancelAtPeriodEnd).toBe(false);
    });
  });

  describe('cancel', () => {
    it('should cancel the subscription immediately', () => {
      const subscription = createValidSubscription();
      const updated = subscription.cancel();

      expect(updated.status.value).toBe('CANCELED');
      expect(updated.cancelAtPeriodEnd).toBe(false);
    });
  });

  describe('isFree and isPaid', () => {
    it('should return true for isFree when BRONZE plan', () => {
      const subscription = createValidSubscription();
      expect(subscription.isFree()).toBe(true);
      expect(subscription.isPaid()).toBe(false);
    });

    it('should return true for isPaid when SILVER plan', () => {
      const subscription = Subscription.create({
        id: 'sub-123',
        organizationId: 'org-123',
        plan: SubscriptionPlan.silver(),
      });
      expect(subscription.isFree()).toBe(false);
      expect(subscription.isPaid()).toBe(true);
    });
  });

  describe('toObject', () => {
    it('should convert to plain object for persistence', () => {
      const subscription = Subscription.create({
        id: 'sub-123',
        organizationId: 'org-123',
        stripeCustomerId: 'cus_123',
      });

      const obj = subscription.toObject();

      expect(obj.id).toBe('sub-123');
      expect(obj.organizationId).toBe('org-123');
      expect(obj.plan).toBe('BRONZE');
      expect(obj.status).toBe('ACTIVE');
      expect(obj.stripeCustomerId).toBe('cus_123');
    });
  });
});
