import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderHook, waitFor, act } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';

// Mock the business API
vi.mock('../../api/business', () => ({
  getBusinessOAuthSettings: vi.fn(),
  updateBusinessOAuthSettings: vi.fn(),
}));

import {
  useBusinessOAuthSettings,
  useUpdateBusinessOAuthSettings,
} from '../useBusinessOAuth';
import * as businessApi from '../../api/business';

// Create wrapper for React Query
const createWrapper = () => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });

  return function Wrapper({ children }: { children: React.ReactNode }) {
    return React.createElement(QueryClientProvider, { client: queryClient }, children);
  };
};

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

  describe('useBusinessOAuthSettings', () => {
    it('fetches business OAuth settings successfully', async () => {
      const mockResponse = {
        settings: {
          enabledProviders: ['google', 'microsoft'],
          allowRegistration: true,
          autoLinkByEmail: false,
          useCustomCredentials: false,
        },
        availableProviders: [
          {
            id: 'google',
            name: 'Google',
            icon: 'google-icon',
            description: 'Sign in with Google',
          },
          {
            id: 'microsoft',
            name: 'Microsoft',
            icon: 'microsoft-icon',
            description: 'Sign in with Microsoft',
          },
        ],
      };

      vi.mocked(businessApi.getBusinessOAuthSettings).mockResolvedValue(mockResponse);

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

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

      // Wait for success
      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(businessApi.getBusinessOAuthSettings).toHaveBeenCalledTimes(1);
      expect(result.current.data).toEqual(mockResponse);
      expect(result.current.data?.settings.enabledProviders).toHaveLength(2);
      expect(result.current.data?.availableProviders).toHaveLength(2);
    });

    it('handles empty enabled providers', async () => {
      const mockResponse = {
        settings: {
          enabledProviders: [],
          allowRegistration: false,
          autoLinkByEmail: false,
          useCustomCredentials: false,
        },
        availableProviders: [
          {
            id: 'google',
            name: 'Google',
            icon: 'google-icon',
            description: 'Sign in with Google',
          },
        ],
      };

      vi.mocked(businessApi.getBusinessOAuthSettings).mockResolvedValue(mockResponse);

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

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data?.settings.enabledProviders).toEqual([]);
      expect(result.current.data?.availableProviders).toHaveLength(1);
    });

    it('handles custom credentials enabled', async () => {
      const mockResponse = {
        settings: {
          enabledProviders: ['google'],
          allowRegistration: true,
          autoLinkByEmail: true,
          useCustomCredentials: true,
        },
        availableProviders: [
          {
            id: 'google',
            name: 'Google',
            icon: 'google-icon',
            description: 'Sign in with Google',
          },
        ],
      };

      vi.mocked(businessApi.getBusinessOAuthSettings).mockResolvedValue(mockResponse);

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

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data?.settings.useCustomCredentials).toBe(true);
      expect(result.current.data?.settings.autoLinkByEmail).toBe(true);
    });

    it('handles API error gracefully', async () => {
      const mockError = new Error('Failed to fetch OAuth settings');
      vi.mocked(businessApi.getBusinessOAuthSettings).mockRejectedValue(mockError);

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

      await waitFor(() => {
        expect(result.current.isError).toBe(true);
      });

      expect(result.current.error).toEqual(mockError);
      expect(result.current.data).toBeUndefined();
    });

    it('does not retry on failure', async () => {
      vi.mocked(businessApi.getBusinessOAuthSettings).mockRejectedValue(
        new Error('404 Not Found')
      );

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

      await waitFor(() => {
        expect(result.current.isError).toBe(true);
      });

      // Should be called only once (no retries)
      expect(businessApi.getBusinessOAuthSettings).toHaveBeenCalledTimes(1);
    });

    it('caches data with 5 minute stale time', async () => {
      const mockResponse = {
        settings: {
          enabledProviders: ['google'],
          allowRegistration: true,
          autoLinkByEmail: false,
          useCustomCredentials: false,
        },
        availableProviders: [
          {
            id: 'google',
            name: 'Google',
            icon: 'google-icon',
            description: 'Sign in with Google',
          },
        ],
      };

      vi.mocked(businessApi.getBusinessOAuthSettings).mockResolvedValue(mockResponse);

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

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      // Rerender should use cached data (within stale time)
      rerender();

      // Should still only be called once
      expect(businessApi.getBusinessOAuthSettings).toHaveBeenCalledTimes(1);
    });
  });

  describe('useUpdateBusinessOAuthSettings', () => {
    it('updates enabled providers successfully', async () => {
      const mockResponse = {
        settings: {
          enabledProviders: ['google', 'microsoft', 'github'],
          allowRegistration: true,
          autoLinkByEmail: false,
          useCustomCredentials: false,
        },
        availableProviders: [
          {
            id: 'google',
            name: 'Google',
            icon: 'google-icon',
            description: 'Sign in with Google',
          },
          {
            id: 'microsoft',
            name: 'Microsoft',
            icon: 'microsoft-icon',
            description: 'Sign in with Microsoft',
          },
          {
            id: 'github',
            name: 'GitHub',
            icon: 'github-icon',
            description: 'Sign in with GitHub',
          },
        ],
      };

      vi.mocked(businessApi.updateBusinessOAuthSettings).mockResolvedValue(mockResponse);

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

      await act(async () => {
        await result.current.mutateAsync({
          enabledProviders: ['google', 'microsoft', 'github'],
        });
      });

      expect(businessApi.updateBusinessOAuthSettings).toHaveBeenCalledWith({
        enabledProviders: ['google', 'microsoft', 'github'],
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });
    });

    it('updates allowRegistration flag', async () => {
      const mockResponse = {
        settings: {
          enabledProviders: ['google'],
          allowRegistration: false,
          autoLinkByEmail: false,
          useCustomCredentials: false,
        },
        availableProviders: [
          {
            id: 'google',
            name: 'Google',
            icon: 'google-icon',
            description: 'Sign in with Google',
          },
        ],
      };

      vi.mocked(businessApi.updateBusinessOAuthSettings).mockResolvedValue(mockResponse);

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

      await act(async () => {
        await result.current.mutateAsync({
          allowRegistration: false,
        });
      });

      expect(businessApi.updateBusinessOAuthSettings).toHaveBeenCalledWith({
        allowRegistration: false,
      });
    });

    it('updates autoLinkByEmail flag', async () => {
      const mockResponse = {
        settings: {
          enabledProviders: ['google'],
          allowRegistration: true,
          autoLinkByEmail: true,
          useCustomCredentials: false,
        },
        availableProviders: [
          {
            id: 'google',
            name: 'Google',
            icon: 'google-icon',
            description: 'Sign in with Google',
          },
        ],
      };

      vi.mocked(businessApi.updateBusinessOAuthSettings).mockResolvedValue(mockResponse);

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

      await act(async () => {
        await result.current.mutateAsync({
          autoLinkByEmail: true,
        });
      });

      expect(businessApi.updateBusinessOAuthSettings).toHaveBeenCalledWith({
        autoLinkByEmail: true,
      });
    });

    it('updates useCustomCredentials flag', async () => {
      const mockResponse = {
        settings: {
          enabledProviders: ['google'],
          allowRegistration: true,
          autoLinkByEmail: false,
          useCustomCredentials: true,
        },
        availableProviders: [
          {
            id: 'google',
            name: 'Google',
            icon: 'google-icon',
            description: 'Sign in with Google',
          },
        ],
      };

      vi.mocked(businessApi.updateBusinessOAuthSettings).mockResolvedValue(mockResponse);

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

      await act(async () => {
        await result.current.mutateAsync({
          useCustomCredentials: true,
        });
      });

      expect(businessApi.updateBusinessOAuthSettings).toHaveBeenCalledWith({
        useCustomCredentials: true,
      });
    });

    it('updates multiple settings at once', async () => {
      const mockResponse = {
        settings: {
          enabledProviders: ['google', 'microsoft'],
          allowRegistration: false,
          autoLinkByEmail: true,
          useCustomCredentials: true,
        },
        availableProviders: [
          {
            id: 'google',
            name: 'Google',
            icon: 'google-icon',
            description: 'Sign in with Google',
          },
          {
            id: 'microsoft',
            name: 'Microsoft',
            icon: 'microsoft-icon',
            description: 'Sign in with Microsoft',
          },
        ],
      };

      vi.mocked(businessApi.updateBusinessOAuthSettings).mockResolvedValue(mockResponse);

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

      await act(async () => {
        await result.current.mutateAsync({
          enabledProviders: ['google', 'microsoft'],
          allowRegistration: false,
          autoLinkByEmail: true,
          useCustomCredentials: true,
        });
      });

      expect(businessApi.updateBusinessOAuthSettings).toHaveBeenCalledWith({
        enabledProviders: ['google', 'microsoft'],
        allowRegistration: false,
        autoLinkByEmail: true,
        useCustomCredentials: true,
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data?.settings.enabledProviders).toHaveLength(2);
      expect(result.current.data?.settings.allowRegistration).toBe(false);
      expect(result.current.data?.settings.autoLinkByEmail).toBe(true);
      expect(result.current.data?.settings.useCustomCredentials).toBe(true);
    });

    it('updates query cache on success', async () => {
      const mockResponse = {
        settings: {
          enabledProviders: ['google'],
          allowRegistration: true,
          autoLinkByEmail: false,
          useCustomCredentials: false,
        },
        availableProviders: [
          {
            id: 'google',
            name: 'Google',
            icon: 'google-icon',
            description: 'Sign in with Google',
          },
        ],
      };

      vi.mocked(businessApi.updateBusinessOAuthSettings).mockResolvedValue(mockResponse);

      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
          mutations: { retry: false },
        },
      });

      const wrapper = ({ children }: { children: React.ReactNode }) =>
        React.createElement(QueryClientProvider, { client: queryClient }, children);

      const { result } = renderHook(() => useUpdateBusinessOAuthSettings(), {
        wrapper,
      });

      await act(async () => {
        await result.current.mutateAsync({
          enabledProviders: ['google'],
        });
      });

      // Verify cache was updated
      const cachedData = queryClient.getQueryData(['businessOAuthSettings']);
      expect(cachedData).toEqual(mockResponse);
    });

    it('handles update error gracefully', async () => {
      const mockError = new Error('Failed to update settings');
      vi.mocked(businessApi.updateBusinessOAuthSettings).mockRejectedValue(mockError);

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

      let caughtError: any = null;

      await act(async () => {
        try {
          await result.current.mutateAsync({
            allowRegistration: true,
          });
        } catch (error) {
          caughtError = error;
        }
      });

      expect(caughtError).toEqual(mockError);

      await waitFor(() => {
        expect(result.current.isError).toBe(true);
      });

      expect(result.current.error).toEqual(mockError);
    });

    it('handles partial update with only enabledProviders', async () => {
      const mockResponse = {
        settings: {
          enabledProviders: ['github'],
          allowRegistration: true,
          autoLinkByEmail: false,
          useCustomCredentials: false,
        },
        availableProviders: [
          {
            id: 'github',
            name: 'GitHub',
            icon: 'github-icon',
            description: 'Sign in with GitHub',
          },
        ],
      };

      vi.mocked(businessApi.updateBusinessOAuthSettings).mockResolvedValue(mockResponse);

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

      await act(async () => {
        await result.current.mutateAsync({
          enabledProviders: ['github'],
        });
      });

      expect(businessApi.updateBusinessOAuthSettings).toHaveBeenCalledWith({
        enabledProviders: ['github'],
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data?.settings.enabledProviders).toEqual(['github']);
    });

    it('handles empty enabled providers array', async () => {
      const mockResponse = {
        settings: {
          enabledProviders: [],
          allowRegistration: true,
          autoLinkByEmail: false,
          useCustomCredentials: false,
        },
        availableProviders: [
          {
            id: 'google',
            name: 'Google',
            icon: 'google-icon',
            description: 'Sign in with Google',
          },
        ],
      };

      vi.mocked(businessApi.updateBusinessOAuthSettings).mockResolvedValue(mockResponse);

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

      await act(async () => {
        await result.current.mutateAsync({
          enabledProviders: [],
        });
      });

      expect(businessApi.updateBusinessOAuthSettings).toHaveBeenCalledWith({
        enabledProviders: [],
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data?.settings.enabledProviders).toEqual([]);
    });

    it('preserves availableProviders from backend response', async () => {
      const mockResponse = {
        settings: {
          enabledProviders: ['google'],
          allowRegistration: true,
          autoLinkByEmail: false,
          useCustomCredentials: false,
        },
        availableProviders: [
          {
            id: 'google',
            name: 'Google',
            icon: 'google-icon',
            description: 'Sign in with Google',
          },
          {
            id: 'microsoft',
            name: 'Microsoft',
            icon: 'microsoft-icon',
            description: 'Sign in with Microsoft',
          },
          {
            id: 'github',
            name: 'GitHub',
            icon: 'github-icon',
            description: 'Sign in with GitHub',
          },
        ],
      };

      vi.mocked(businessApi.updateBusinessOAuthSettings).mockResolvedValue(mockResponse);

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

      await act(async () => {
        await result.current.mutateAsync({
          enabledProviders: ['google'],
        });
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data?.availableProviders).toHaveLength(3);
      expect(result.current.data?.availableProviders.map(p => p.id)).toEqual([
        'google',
        'microsoft',
        'github',
      ]);
    });
  });

  describe('integration tests', () => {
    it('fetches settings then updates them', async () => {
      const initialResponse = {
        settings: {
          enabledProviders: ['google'],
          allowRegistration: true,
          autoLinkByEmail: false,
          useCustomCredentials: false,
        },
        availableProviders: [
          {
            id: 'google',
            name: 'Google',
            icon: 'google-icon',
            description: 'Sign in with Google',
          },
          {
            id: 'microsoft',
            name: 'Microsoft',
            icon: 'microsoft-icon',
            description: 'Sign in with Microsoft',
          },
        ],
      };

      const updatedResponse = {
        settings: {
          enabledProviders: ['google', 'microsoft'],
          allowRegistration: true,
          autoLinkByEmail: true,
          useCustomCredentials: false,
        },
        availableProviders: [
          {
            id: 'google',
            name: 'Google',
            icon: 'google-icon',
            description: 'Sign in with Google',
          },
          {
            id: 'microsoft',
            name: 'Microsoft',
            icon: 'microsoft-icon',
            description: 'Sign in with Microsoft',
          },
        ],
      };

      vi.mocked(businessApi.getBusinessOAuthSettings).mockResolvedValue(initialResponse);
      vi.mocked(businessApi.updateBusinessOAuthSettings).mockResolvedValue(updatedResponse);

      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
          mutations: { retry: false },
        },
      });

      const wrapper = ({ children }: { children: React.ReactNode }) =>
        React.createElement(QueryClientProvider, { client: queryClient }, children);

      // Fetch initial settings
      const { result: fetchResult } = renderHook(() => useBusinessOAuthSettings(), {
        wrapper,
      });

      await waitFor(() => {
        expect(fetchResult.current.isSuccess).toBe(true);
      });

      expect(fetchResult.current.data?.settings.enabledProviders).toEqual(['google']);
      expect(fetchResult.current.data?.settings.autoLinkByEmail).toBe(false);

      // Update settings
      const { result: updateResult } = renderHook(() => useUpdateBusinessOAuthSettings(), {
        wrapper,
      });

      await act(async () => {
        await updateResult.current.mutateAsync({
          enabledProviders: ['google', 'microsoft'],
          autoLinkByEmail: true,
        });
      });

      // Verify cache was updated
      const cachedData = queryClient.getQueryData(['businessOAuthSettings']);
      expect(cachedData).toEqual(updatedResponse);
    });
  });
});
