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

import {
  PlanKey,
  SubscriptionInfo,
  UsagePlanSchema,
} from '@/state/sessionStore';
import { SubscriptionPeriod } from '@/utils/session';

import {
  SubscriptionAction,
  getSubscriptionAction,
} from './subscriptionActions';

describe('getSubscriptionAction', () => {
  const _mockFreePlan: UsagePlanSchema = {
    id: '1',
    plan_key: PlanKey.Free,
    level: 0,
    name: 'Free',
    features: '',
    monthly_price_usd: 0,
    annual_price_usd: 0,
    prices: [],
    usage_plan_features: [],
  };

  const mockBasicPlan: UsagePlanSchema = {
    id: '0',
    plan_key: PlanKey.Basic,
    level: 0,
    name: 'Basic',
    features: '',
    monthly_price_usd: 5,
    annual_price_usd: 48,
    prices: [],
    usage_plan_features: [],
  };

  const mockProPlan: UsagePlanSchema = {
    id: '2',
    plan_key: PlanKey.Pro,
    level: 1,
    name: 'Pro',
    features: '',
    monthly_price_usd: 10,
    annual_price_usd: 96,
    prices: [],
    usage_plan_features: [],
  };

  const mockPremierPlan: UsagePlanSchema = {
    id: '3',
    plan_key: PlanKey.Premier,
    level: 2,
    name: 'Premier',
    features: '',
    monthly_price_usd: 30,
    annual_price_usd: 288,
    prices: [],
    usage_plan_features: [],
  };

  it('should return "upgradeDelayedOnly" when can_upgrade_immediately is false', () => {
    const currentSubscription: Partial<SubscriptionInfo> = {
      plan: mockProPlan,
      period: SubscriptionPeriod.Monthly,
      can_upgrade_immediately: false,
    };

    const action = getSubscriptionAction(
      currentSubscription as SubscriptionInfo,
      mockPremierPlan,
      SubscriptionPeriod.Monthly
    );

    expect(action).toBe(SubscriptionAction.UpgradeDelayedOnly);
  });

  it('should return "upgrade" when can_upgrade_immediately is true', () => {
    const currentSubscription: Partial<SubscriptionInfo> = {
      plan: mockProPlan,
      period: SubscriptionPeriod.Monthly,
      can_upgrade_immediately: true,
    };

    const action = getSubscriptionAction(
      currentSubscription as SubscriptionInfo,
      mockPremierPlan,
      SubscriptionPeriod.Monthly
    );

    expect(action).toBe(SubscriptionAction.Upgrade);
  });

  it('should return "upgrade" when can_upgrade_immediately is null (default behavior)', () => {
    const currentSubscription: Partial<SubscriptionInfo> = {
      plan: mockProPlan,
      period: SubscriptionPeriod.Monthly,
      can_upgrade_immediately: null,
    };

    const action = getSubscriptionAction(
      currentSubscription as SubscriptionInfo,
      mockPremierPlan,
      SubscriptionPeriod.Monthly
    );

    expect(action).toBe(SubscriptionAction.Upgrade);
  });

  it('should return "upgrade" when can_upgrade_immediately is undefined (default behavior)', () => {
    const currentSubscription: Partial<SubscriptionInfo> = {
      plan: mockProPlan,
      period: SubscriptionPeriod.Monthly,
      // can_upgrade_immediately is undefined
    };

    const action = getSubscriptionAction(
      currentSubscription as SubscriptionInfo,
      mockPremierPlan,
      SubscriptionPeriod.Monthly
    );

    expect(action).toBe(SubscriptionAction.Upgrade);
  });

  it('should still allow downgrades regardless of can_upgrade_immediately', () => {
    const currentSubscription: Partial<SubscriptionInfo> = {
      plan: mockPremierPlan,
      period: SubscriptionPeriod.Monthly,
      can_upgrade_immediately: false,
    };

    const action = getSubscriptionAction(
      currentSubscription as SubscriptionInfo,
      mockProPlan,
      SubscriptionPeriod.Monthly
    );

    expect(action).toBe(SubscriptionAction.Downgrade);
  });

  it('should allow subscriptions for new users regardless of can_upgrade_immediately', () => {
    const action = getSubscriptionAction(
      undefined,
      mockProPlan,
      SubscriptionPeriod.Monthly
    );

    expect(action).toBe(SubscriptionAction.Subscribe);
  });

  describe('UPI payment method restrictions', () => {
    it('should return "unavailable" when UPI user tries to upgrade to Premier Annual', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Monthly,
        payment_method_type: 'upi',
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockPremierPlan,
        SubscriptionPeriod.Annual
      );

      expect(action).toBe(SubscriptionAction.Unavailable);
    });

    it('should allow UPI user to upgrade to Premier Monthly', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Monthly,
        payment_method_type: 'upi',
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockPremierPlan,
        SubscriptionPeriod.Monthly
      );

      expect(action).toBe(SubscriptionAction.Upgrade);
    });

    it('should allow UPI user to upgrade to Pro Annual', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockBasicPlan,
        period: SubscriptionPeriod.Monthly,
        payment_method_type: 'upi',
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockProPlan,
        SubscriptionPeriod.Annual
      );

      expect(action).toBe(SubscriptionAction.Upgrade);
    });

    it('should allow non-UPI user to upgrade to Premier Annual', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Monthly,
        payment_method_type: 'card',
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockPremierPlan,
        SubscriptionPeriod.Annual
      );

      expect(action).toBe(SubscriptionAction.Upgrade);
    });

    it('should prioritize UPI restriction over can_upgrade_immediately check', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Monthly,
        payment_method_type: 'upi',
        can_upgrade_immediately: false,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockPremierPlan,
        SubscriptionPeriod.Annual
      );

      expect(action).toBe(SubscriptionAction.Unavailable);
    });

    it('should return "unavailable" when UPI user tries to change commitment from Premier Monthly to Premier Annual', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockPremierPlan,
        period: SubscriptionPeriod.Monthly,
        payment_method_type: 'upi',
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockPremierPlan,
        SubscriptionPeriod.Annual
      );

      expect(action).toBe(SubscriptionAction.Unavailable);
    });

    it('should return "currentPlan" when UPI user stays on Premier Monthly', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockPremierPlan,
        period: SubscriptionPeriod.Monthly,
        payment_method_type: 'upi',
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockPremierPlan,
        SubscriptionPeriod.Monthly
      );

      expect(action).toBe(SubscriptionAction.CurrentPlan);
    });

    it('should allow non-UPI user to change commitment from Premier Monthly to Premier Annual', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockPremierPlan,
        period: SubscriptionPeriod.Monthly,
        payment_method_type: 'card',
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockPremierPlan,
        SubscriptionPeriod.Annual
      );

      expect(action).toBe(SubscriptionAction.ChangeCommitment);
    });
  });

  describe('changeCommitment tests', () => {
    it('should return "changeCommitment" when changing from monthly to annual on same plan and can_upgrade_immediately is true', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Monthly,
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockProPlan,
        SubscriptionPeriod.Annual
      );

      expect(action).toBe(SubscriptionAction.ChangeCommitment);
    });

    it('should return "changeCommitmentDelayedOnly" when changing from annual to monthly (period downgrade requires end-of-cycle)', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Annual,
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockProPlan,
        SubscriptionPeriod.Monthly
      );

      expect(action).toBe(SubscriptionAction.ChangeCommitmentDelayedOnly);
    });

    it('should return "changeCommitmentDelayedOnly" when can_upgrade_immediately is false (monthly to annual)', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockPremierPlan,
        period: SubscriptionPeriod.Monthly,
        can_upgrade_immediately: false,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockPremierPlan,
        SubscriptionPeriod.Annual
      );

      expect(action).toBe(SubscriptionAction.ChangeCommitmentDelayedOnly);
    });

    it('should return "changeCommitmentDelayedOnly" when can_upgrade_immediately is false (annual to monthly)', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Annual,
        can_upgrade_immediately: false,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockProPlan,
        SubscriptionPeriod.Monthly
      );

      expect(action).toBe(SubscriptionAction.ChangeCommitmentDelayedOnly);
    });

    it('should return "changeCommitmentDelayedOnly" when can_upgrade_immediately is null (period downgrade takes precedence)', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Annual,
        can_upgrade_immediately: null,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockProPlan,
        SubscriptionPeriod.Monthly
      );

      expect(action).toBe(SubscriptionAction.ChangeCommitmentDelayedOnly);
    });

    it('should return "changeCommitment" when can_upgrade_immediately is undefined', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockPremierPlan,
        period: SubscriptionPeriod.Monthly,
        // can_upgrade_immediately is undefined
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockPremierPlan,
        SubscriptionPeriod.Annual
      );

      expect(action).toBe(SubscriptionAction.ChangeCommitment);
    });

    it('should return "currentPlan" when plan and period both match', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Monthly,
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockProPlan,
        SubscriptionPeriod.Monthly
      );

      expect(action).toBe(SubscriptionAction.CurrentPlan);
    });
  });

  describe('period downgrade restrictions', () => {
    // Core business logic: ALL period downgrades (Annual → Monthly) require end-of-cycle transitions
    // This prevents billing complexity from downgrading billing commitment

    it('should return "upgradeDelayedOnly" when Pro Annual user tries to upgrade to Premier Monthly', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Annual,
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockPremierPlan,
        SubscriptionPeriod.Monthly
      );

      expect(action).toBe(SubscriptionAction.UpgradeDelayedOnly);
    });

    it('should return "upgrade" when Pro Monthly user tries to upgrade to Premier Annual', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Monthly,
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockPremierPlan,
        SubscriptionPeriod.Annual
      );

      expect(action).toBe(SubscriptionAction.Upgrade);
    });

    it('should return "upgradeDelayedOnly" when Basic Annual user tries to upgrade to Pro Monthly', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockBasicPlan,
        period: SubscriptionPeriod.Annual,
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockProPlan,
        SubscriptionPeriod.Monthly
      );

      expect(action).toBe(SubscriptionAction.UpgradeDelayedOnly);
    });

    it('should return "upgrade" when Basic Monthly user tries to upgrade to Pro Annual', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockBasicPlan,
        period: SubscriptionPeriod.Monthly,
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockProPlan,
        SubscriptionPeriod.Annual
      );

      expect(action).toBe(SubscriptionAction.Upgrade);
    });

    // Commitment changes with period downgrade also require end-of-cycle
    it('should return "changeCommitmentDelayedOnly" when Pro Annual user changes to Pro Monthly', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Annual,
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockProPlan,
        SubscriptionPeriod.Monthly
      );

      expect(action).toBe(SubscriptionAction.ChangeCommitmentDelayedOnly);
    });

    it('should return "changeCommitment" when Pro Monthly user changes to Pro Annual', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Monthly,
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockProPlan,
        SubscriptionPeriod.Annual
      );

      expect(action).toBe(SubscriptionAction.ChangeCommitment);
    });

    // Edge cases
    it('should return "currentPlan" when user stays on same tier and period', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Annual,
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockProPlan,
        SubscriptionPeriod.Annual
      );

      expect(action).toBe(SubscriptionAction.CurrentPlan);
    });

    it('should return "downgrade" when Premier Annual user downgrades tier to Pro Monthly', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockPremierPlan,
        period: SubscriptionPeriod.Annual,
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockProPlan,
        SubscriptionPeriod.Monthly
      );

      expect(action).toBe(SubscriptionAction.Downgrade);
    });

    it('should return "subscribe" when new user subscribes to Pro Annual', () => {
      const action = getSubscriptionAction(
        undefined,
        mockProPlan,
        SubscriptionPeriod.Annual
      );

      expect(action).toBe(SubscriptionAction.Subscribe);
    });

    it('should still return "upgradeDelayedOnly" when Pro Annual → Premier Monthly with can_upgrade_immediately: false', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Annual,
        can_upgrade_immediately: false,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockPremierPlan,
        SubscriptionPeriod.Monthly
      );

      expect(action).toBe(SubscriptionAction.UpgradeDelayedOnly);
    });

    // Interaction with UPI restrictions
    it('should return "unavailable" when UPI user on Pro Annual tries to upgrade to Premier Annual (UPI restriction takes precedence)', () => {
      const currentSubscription: Partial<SubscriptionInfo> = {
        plan: mockProPlan,
        period: SubscriptionPeriod.Annual,
        payment_method_type: 'upi',
        can_upgrade_immediately: true,
      };

      const action = getSubscriptionAction(
        currentSubscription as SubscriptionInfo,
        mockPremierPlan,
        SubscriptionPeriod.Annual
      );

      expect(action).toBe(SubscriptionAction.Unavailable);
    });
  });
});
