import React from 'react';
import {render} from '@testing-library/react-native';
import {
  CALLS_QUERY_KEYS,
  MAX_CALL_PARTICIPANTS,
} from '@modules/calls/constants';
import {CallStatus} from '@modules/calls/interfaces';
import {getLastActiveCall} from '@modules/calls/services';
import {useShallowConversation} from '@modules/chats/store';
import {useCommunityFeature} from '@stores/communityFeatures';

import {useCallInChannel} from '../useCallInChannel';

const mockUseQuery = jest.fn();
const mockUseCommunityFeature = useCommunityFeature as jest.Mock;
const mockUseShallowConversation = useShallowConversation as jest.Mock;
const mockGetLastActiveCall = getLastActiveCall as jest.Mock;

jest.mock('react-query', () => ({
  useQuery: (...args: unknown[]) => mockUseQuery(...args),
}));

jest.mock('@modules/calls/services', () => ({
  getLastActiveCall: jest.fn(),
}));

jest.mock('@stores/communityFeatures', () => ({
  useCommunityFeature: jest.fn(),
}));

jest.mock('@modules/chats/store', () => ({
  useShallowConversation: jest.fn(),
}));

describe('useCallInChannel', () => {
  const channel = 'channel-1';
  const baseConversation = {
    is_channel: true,
    num_members: 2,
    isConversationWithCurrentUser: false,
    other_user: {
      status: 'ACTIVE',
      deleted_at: null,
    },
  };

  const mockConversation = (conversation: typeof baseConversation | null) => {
    mockUseShallowConversation.mockImplementation((_id, selector) => {
      if (!conversation) {
        return null;
      }
      return selector(conversation);
    });
  };

  beforeEach(() => {
    mockUseQuery.mockReset();
    mockUseCommunityFeature.mockReset();
    mockUseShallowConversation.mockReset();
    mockGetLastActiveCall.mockReset();

    mockUseCommunityFeature.mockReturnValue(true);
    mockConversation(baseConversation);
    mockUseQuery.mockReturnValue({data: undefined});
  });

  const renderHook = () => {
    let hookResult: ReturnType<typeof useCallInChannel> | undefined;

    const HookWrapper = () => {
      hookResult = useCallInChannel(channel);
      return null;
    };

    render(React.createElement(HookWrapper));

    return hookResult!;
  };

  it('disables calls when permission is missing', () => {
    mockUseCommunityFeature.mockReturnValue(false);

    const result = renderHook();

    expect(result.isCallEnabled).toBe(false);
    expect(mockUseQuery).toHaveBeenCalledWith(
      CALLS_QUERY_KEYS.activeCall(channel),
      expect.any(Function),
      expect.objectContaining({enabled: false}),
    );
  });

  it('disables calls when group size exceeds limit', () => {
    mockConversation({
      ...baseConversation,
      num_members: MAX_CALL_PARTICIPANTS + 1,
    });

    const result = renderHook();

    expect(result.isCallEnabled).toBe(false);
    expect(mockUseQuery).toHaveBeenCalledWith(
      CALLS_QUERY_KEYS.activeCall(channel),
      expect.any(Function),
      expect.objectContaining({enabled: false}),
    );
  });

  it('disables calls when user is unavailable', () => {
    mockConversation({
      ...baseConversation,
      other_user: {
        status: 'DEACTIVATED',
        deleted_at: null,
      },
    });

    const result = renderHook();

    expect(result.isCallEnabled).toBe(false);
  });

  it('returns ongoing call flags for group calls', () => {
    mockUseQuery.mockReturnValue({
      data: {
        id: 'call-1',
        providerCallId: 'external-1',
        initializationConfig: {cameraEnabled: false},
        currentSession: {status: CallStatus.Active},
      },
    });

    const result = renderHook();

    expect(result.callId).toBe('call-1');
    expect(result.externalCallId).toBe('external-1');
    expect(result.isOngoingCall).toBe(true);
    expect(result.isAudioCall).toBe(true);
    expect(result.isVideoCall).toBe(false);
  });

  it('hides ongoing call flags while the call is PENDING', () => {
    mockUseQuery.mockReturnValue({
      data: {
        id: 'call-1',
        providerCallId: 'external-1',
        initializationConfig: {cameraEnabled: false},
        currentSession: {status: CallStatus.Pending},
      },
    });

    const result = renderHook();

    expect(result.isOngoingCall).toBe(false);
    expect(result.isAudioCall).toBe(false);
    expect(result.isVideoCall).toBe(false);
  });

  it('configures refetch interval based on call status', () => {
    let options: {refetchInterval?: (data?: unknown) => false | number} = {};

    mockUseQuery.mockImplementation((_key, _fn, opts) => {
      options = opts;
      return {data: {currentSession: {status: CallStatus.Active}}};
    });

    renderHook();

    expect(
      options.refetchInterval?.({currentSession: {status: CallStatus.Active}}),
    ).toBe(false);
    expect(
      options.refetchInterval?.({currentSession: {status: CallStatus.Ended}}),
    ).toBe(5000);
  });

  it('builds query function with channel', async () => {
    let queryFn: (() => Promise<unknown>) | undefined;

    mockUseQuery.mockImplementation((_key, fn) => {
      queryFn = fn;
      return {data: undefined};
    });
    mockGetLastActiveCall.mockResolvedValue({});

    renderHook();
    await queryFn?.();

    expect(mockGetLastActiveCall).toHaveBeenCalledWith(channel);
  });
});
