import { renderHook, act, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

const mockAuthFetch = vi.fn();
const mockAuth = {
  isAuthenticated: true,
};

vi.mock('../../hooks/useAuthenticatedFetch', () => ({
  useAuthenticatedFetch: () => ({
    authFetch: mockAuthFetch,
    isAuthenticated: mockAuth.isAuthenticated,
  }),
}));

vi.mock('react-hot-toast', () => ({
  default: {
    success: vi.fn(),
    error: vi.fn(),
  },
}));

describe('useUserPreferences - subscription data', () => {
  let useUserPreferences: typeof import('../../hooks/useUserPreferences').useUserPreferences;

  beforeEach(async () => {
    vi.clearAllMocks();
    vi.resetModules();

    mockAuth.isAuthenticated = true;

    const module = await import('../../hooks/useUserPreferences');
    useUserPreferences = module.useUserPreferences;
  });

  afterEach(() => {
    vi.resetAllMocks();
  });

  describe('subscription info fetching', () => {
    it('fetches subscription status along with preferences', async () => {
      mockAuthFetch
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            colorScheme: 'blue',
            aiProvider: 'gemini',
            hasAIAccess: true,
          }),
        })
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            hasAccess: true,
            accessType: 'subscription',
            generationCount: 10,
            generationLimit: 250,
            generationsRemaining: 240,
          }),
        });

      const { result } = renderHook(() => useUserPreferences());

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

      expect(result.current.subscription).toEqual({
        hasAccess: true,
        accessType: 'subscription',
        generationCount: 10,
        generationLimit: 250,
        generationsRemaining: 240,
      });
    });

    it('sets subscription info for group access users', async () => {
      mockAuthFetch
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            colorScheme: 'blue',
            aiProvider: 'gemini',
            hasAIAccess: true,
          }),
        })
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            hasAccess: true,
            accessType: 'group',
            generationCount: null,
            generationLimit: null,
            generationsRemaining: null,
          }),
        });

      const { result } = renderHook(() => useUserPreferences());

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

      expect(result.current.subscription).toEqual({
        hasAccess: true,
        accessType: 'group',
        generationCount: null,
        generationLimit: null,
        generationsRemaining: null,
      });
    });

    it('sets subscription info for users without access', async () => {
      mockAuthFetch
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            colorScheme: 'blue',
            aiProvider: 'gemini',
            hasAIAccess: false,
          }),
        })
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            hasAccess: false,
            accessType: 'none',
            generationCount: 0,
            generationLimit: 250,
            generationsRemaining: 0,
          }),
        });

      const { result } = renderHook(() => useUserPreferences());

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

      expect(result.current.subscription).toEqual({
        hasAccess: false,
        accessType: 'none',
        generationCount: 0,
        generationLimit: 250,
        generationsRemaining: 0,
      });
    });

    it('handles subscription status fetch failure gracefully', async () => {
      mockAuthFetch
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            colorScheme: 'purple',
            aiProvider: 'openrouter',
            hasAIAccess: false,
          }),
        })
        .mockRejectedValueOnce(new Error('Network error'));

      const { result } = renderHook(() => useUserPreferences());

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

      expect(result.current.subscription).toBeNull();
      expect(result.current.preferences.colorScheme).toBe('purple');
    });

    it('handles subscription status non-ok response gracefully', async () => {
      mockAuthFetch
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            colorScheme: 'blue',
            aiProvider: 'gemini',
            hasAIAccess: true,
          }),
        })
        .mockResolvedValueOnce({
          ok: false,
          status: 503,
        });

      const { result } = renderHook(() => useUserPreferences());

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

      expect(result.current.subscription).toBeNull();
    });
  });

  describe('hasAIAccess from preferences', () => {
    it('sets hasAIAccess true when user has AI access', async () => {
      mockAuthFetch
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            colorScheme: 'blue',
            aiProvider: 'gemini',
            hasAIAccess: true,
          }),
        })
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            hasAccess: true,
            accessType: 'group',
          }),
        });

      const { result } = renderHook(() => useUserPreferences());

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

      expect(result.current.hasAIAccess).toBe(true);
    });

    it('sets hasAIAccess false when user does not have AI access', async () => {
      mockAuthFetch
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            colorScheme: 'blue',
            aiProvider: 'gemini',
            hasAIAccess: false,
          }),
        })
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            hasAccess: false,
            accessType: 'none',
          }),
        });

      const { result } = renderHook(() => useUserPreferences());

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

      expect(result.current.hasAIAccess).toBe(false);
    });

    it('defaults hasAIAccess to false when not present', async () => {
      mockAuthFetch
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            colorScheme: 'blue',
            aiProvider: 'gemini',
          }),
        })
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            hasAccess: false,
            accessType: 'none',
          }),
        });

      const { result } = renderHook(() => useUserPreferences());

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

      expect(result.current.hasAIAccess).toBe(false);
    });
  });

  describe('when not authenticated', () => {
    beforeEach(() => {
      mockAuth.isAuthenticated = false;
    });

    it('does not fetch subscription status', async () => {
      const { result } = renderHook(() => useUserPreferences());

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

      expect(mockAuthFetch).not.toHaveBeenCalled();
      expect(result.current.subscription).toBeNull();
    });
  });

  describe('refetching subscription', () => {
    it('can refetch subscription info via fetchPreferences', async () => {
      mockAuthFetch
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            colorScheme: 'blue',
            aiProvider: 'gemini',
            hasAIAccess: false,
          }),
        })
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            hasAccess: false,
            accessType: 'none',
            generationCount: 0,
            generationLimit: 250,
            generationsRemaining: 0,
          }),
        });

      const { result } = renderHook(() => useUserPreferences());

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

      expect(result.current.subscription?.hasAccess).toBe(false);

      mockAuthFetch
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            colorScheme: 'blue',
            aiProvider: 'gemini',
            hasAIAccess: true,
          }),
        })
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            hasAccess: true,
            accessType: 'subscription',
            generationCount: 0,
            generationLimit: 250,
            generationsRemaining: 250,
          }),
        });

      await act(async () => {
        await result.current.fetchPreferences();
      });

      expect(result.current.subscription?.hasAccess).toBe(true);
      expect(result.current.subscription?.accessType).toBe('subscription');
    });
  });

  describe('generation tracking', () => {
    it('tracks generation count correctly', async () => {
      mockAuthFetch
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            colorScheme: 'blue',
            aiProvider: 'gemini',
            hasAIAccess: true,
          }),
        })
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            hasAccess: true,
            accessType: 'subscription',
            generationCount: 50,
            generationLimit: 250,
            generationsRemaining: 200,
          }),
        });

      const { result } = renderHook(() => useUserPreferences());

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

      expect(result.current.subscription?.generationCount).toBe(50);
      expect(result.current.subscription?.generationsRemaining).toBe(200);
    });

    it('shows zero remaining when limit reached', async () => {
      mockAuthFetch
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            colorScheme: 'blue',
            aiProvider: 'gemini',
            hasAIAccess: true,
          }),
        })
        .mockResolvedValueOnce({
          ok: true,
          json: () => Promise.resolve({
            hasAccess: true,
            accessType: 'subscription',
            generationCount: 250,
            generationLimit: 250,
            generationsRemaining: 0,
          }),
        });

      const { result } = renderHook(() => useUserPreferences());

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

      expect(result.current.subscription?.generationsRemaining).toBe(0);
    });
  });
});
