/**
 * Tests for useEntitlements hook
 */

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderHook, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';
import { useEntitlements } from '../useEntitlements';
import * as billingApi from '../../api/billing';

// Mock the billing API
vi.mock('../../api/billing', () => ({
  getEntitlements: vi.fn(),
  getCurrentSubscription: vi.fn(),
}));

const createWrapper = () => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
      },
    },
  });
  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );
};

describe('useEntitlements', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('fetches and returns entitlements', async () => {
    const mockEntitlements = {
      can_use_sms_reminders: true,
      can_use_mobile_app: false,
      max_users: 10,
    };

    vi.mocked(billingApi.getEntitlements).mockResolvedValueOnce(mockEntitlements);

    const { result } = renderHook(() => useEntitlements(), {
      wrapper: createWrapper(),
    });

    // Initially loading
    expect(result.current.isLoading).toBe(true);

    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    expect(result.current.entitlements).toEqual(mockEntitlements);
  });

  it('hasFeature returns true for enabled boolean features', async () => {
    const mockEntitlements = {
      can_use_sms_reminders: true,
      can_use_mobile_app: false,
    };

    vi.mocked(billingApi.getEntitlements).mockResolvedValueOnce(mockEntitlements);

    const { result } = renderHook(() => useEntitlements(), {
      wrapper: createWrapper(),
    });

    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    expect(result.current.hasFeature('can_use_sms_reminders')).toBe(true);
    expect(result.current.hasFeature('can_use_mobile_app')).toBe(false);
  });

  it('hasFeature returns false for non-existent features', async () => {
    const mockEntitlements = {
      can_use_sms_reminders: true,
    };

    vi.mocked(billingApi.getEntitlements).mockResolvedValueOnce(mockEntitlements);

    const { result } = renderHook(() => useEntitlements(), {
      wrapper: createWrapper(),
    });

    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    expect(result.current.hasFeature('nonexistent_feature')).toBe(false);
  });

  it('getLimit returns integer value for limit features', async () => {
    const mockEntitlements = {
      max_users: 10,
      max_resources: 25,
    };

    vi.mocked(billingApi.getEntitlements).mockResolvedValueOnce(mockEntitlements);

    const { result } = renderHook(() => useEntitlements(), {
      wrapper: createWrapper(),
    });

    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    expect(result.current.getLimit('max_users')).toBe(10);
    expect(result.current.getLimit('max_resources')).toBe(25);
  });

  it('getLimit returns null for non-existent limits', async () => {
    const mockEntitlements = {
      max_users: 10,
    };

    vi.mocked(billingApi.getEntitlements).mockResolvedValueOnce(mockEntitlements);

    const { result } = renderHook(() => useEntitlements(), {
      wrapper: createWrapper(),
    });

    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    expect(result.current.getLimit('nonexistent_limit')).toBeNull();
  });

  it('getLimit returns null for boolean features', async () => {
    const mockEntitlements = {
      can_use_sms_reminders: true,
    };

    vi.mocked(billingApi.getEntitlements).mockResolvedValueOnce(mockEntitlements);

    const { result } = renderHook(() => useEntitlements(), {
      wrapper: createWrapper(),
    });

    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    // Boolean features should not be returned as limits
    expect(result.current.getLimit('can_use_sms_reminders')).toBeNull();
  });

  it('returns loading state initially', () => {
    vi.mocked(billingApi.getEntitlements).mockImplementation(
      () => new Promise(() => {}) // Never resolves
    );

    const { result } = renderHook(() => useEntitlements(), {
      wrapper: createWrapper(),
    });

    expect(result.current.isLoading).toBe(true);
    expect(result.current.entitlements).toEqual({});
  });

  it('returns empty entitlements when API returns empty', async () => {
    // When getEntitlements encounters an error, it returns {} (see billing.ts)
    // So we test that behavior by having the mock return {}
    vi.mocked(billingApi.getEntitlements).mockResolvedValueOnce({});

    const { result } = renderHook(() => useEntitlements(), {
      wrapper: createWrapper(),
    });

    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    expect(result.current.entitlements).toEqual({});
    expect(result.current.hasFeature('any_feature')).toBe(false);
    expect(result.current.getLimit('any_limit')).toBeNull();
  });
});
