import { renderHook, act, waitFor } from '@testing-library/react-native';
import { TimerProvider, useTimerContext } from '../TimerContext';
import { timerStore } from '../TimerStore';
import '@testing-library/jest-native/extend-expect';

jest.mock('../TimerStore', () => ({
  timerStore: {
    getState: jest.fn(),
    saveReps: jest.fn(),
    saveWorkTime: jest.fn(),
    saveRestTime: jest.fn(),
    saveState: jest.fn(),
    savePreparationTime: jest.fn(),
  }
}));

const mockTimerState = {
  reps: 5,
  workTime: 30,
  restTime: 10,
  preparationTime: 3
};

describe('TimerContext', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    // Default configuration for getState
    (timerStore.getState as jest.Mock<Promise<any>>).mockResolvedValue(mockTimerState);
  });

  it('should load initial data', async () => {
    const { result } = renderHook(() => useTimerContext(), {
      wrapper: TimerProvider
    });

    // Check initial loading state
    expect(result.current.isLoading).toBe(true);

    // Wait for loading to complete
    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    // Verify getState was called
    expect(timerStore.getState).toHaveBeenCalledTimes(1);

    // Verify data is correctly loaded
    expect(result.current.timerState).toEqual(mockTimerState);
  });

  it('should handle errors when loading data', async () => {
    // Simulate an error
    (timerStore.getState as jest.Mock<Promise<any>>).mockRejectedValueOnce(new Error('Loading error'));

    // Spy on console.error
    jest.spyOn(console, 'error').mockImplementation(() => {});

    const { result } = renderHook(() => useTimerContext(), {
      wrapper: TimerProvider
    });

    // Wait for loading to complete despite error
    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    // Verify error was logged
    expect(console.error).toHaveBeenCalledWith(
      'Error loading data:',
      expect.any(Error)
    );

    // Restore console.error
    (console.error as jest.MockedFunction<typeof console.error>).mockRestore();
  });

  it('should update repetitions', async () => {
    const { result } = renderHook(() => useTimerContext(), {
      wrapper: TimerProvider
    });

    // Wait for initial loading to complete
    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    // Update repetitions
    await act(async () => {
      await result.current.updateReps(10);
    });

    // Verify saveReps was called with the correct value
    expect(timerStore.saveReps).toHaveBeenCalledWith(10);

    // Verify state was updated
    expect(result.current.timerState.reps).toBe(10);
  });

  it('should handle errors when updating repetitions', async () => {
    // Simulate an error
    (timerStore.saveReps as jest.Mock<Promise<void>>).mockRejectedValueOnce(new Error('Saving error'));

    // Spy on console.error
    jest.spyOn(console, 'error').mockImplementation(() => {});

    const { result } = renderHook(() => useTimerContext(), {
      wrapper: TimerProvider
    });

    // Wait for initial loading to complete
    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    // Attempt to update repetitions
    await act(async () => {
      await result.current.updateReps(10);
    });

    // Verify error was logged
    expect(console.error).toHaveBeenCalledWith(
      'Error updating repetitions:',
      expect.any(Error)
    );

    // Restore console.error
    (console.error as jest.MockedFunction<typeof console.error>).mockRestore();
  });

  it('should update work time', async () => {
    const { result } = renderHook(() => useTimerContext(), {
      wrapper: TimerProvider
    });

    // Wait for initial loading to complete
    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    // Update work time
    await act(async () => {
      await result.current.updateWorkTime(45);
    });

    // Verify saveWorkTime was called with the correct value
    expect(timerStore.saveWorkTime).toHaveBeenCalledWith(45);

    // Verify state was updated
    expect(result.current.timerState.workTime).toBe(45);
  });

  it('should update rest time', async () => {
    const { result } = renderHook(() => useTimerContext(), {
      wrapper: TimerProvider
    });

    // Wait for initial loading to complete
    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    // Update rest time
    await act(async () => {
      await result.current.updateRestTime(15);
    });

    // Verify saveRestTime was called with the correct value
    expect(timerStore.saveRestTime).toHaveBeenCalledWith(15);

    // Verify state was updated
    expect(result.current.timerState.restTime).toBe(15);
  });

  it('should handle errors when updating rest time', async () => {
    // Simulate an error
    (timerStore.saveRestTime as jest.Mock<Promise<void>>).mockRejectedValueOnce(new Error('Saving error'));

    // Spy on console.error
    jest.spyOn(console, 'error').mockImplementation(() => {});

    const { result } = renderHook(() => useTimerContext(), {
      wrapper: TimerProvider
    });

    // Wait for initial loading to complete
    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    // Attempt to update rest time
    await act(async () => {
      await result.current.updateRestTime(15);
    });

    // Verify error was logged
    expect(console.error).toHaveBeenCalledWith(
      'Error updating rest time:',
      expect.any(Error)
    );

    // Restore console.error
    (console.error as jest.MockedFunction<typeof console.error>).mockRestore();
  });

  it('should handle errors when updating work time', async () => {
    // Simulate an error
    (timerStore.saveWorkTime as jest.Mock<Promise<void>>).mockRejectedValueOnce(new Error('Saving error'));

    // Spy on console.error
    jest.spyOn(console, 'error').mockImplementation(() => {});

    const { result } = renderHook(() => useTimerContext(), {
      wrapper: TimerProvider
    });

    // Wait for initial loading to complete
    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    // Attempt to update work time
    await act(async () => {
      await result.current.updateWorkTime(45);
    });

    // Verify error was logged
    expect(console.error).toHaveBeenCalledWith(
      'Error updating work time:',
      expect.any(Error)
    );

    // Restore console.error
    (console.error as jest.MockedFunction<typeof console.error>).mockRestore();
  });

  it('should update preparation time', async () => {
    const { result } = renderHook(() => useTimerContext(), {
      wrapper: TimerProvider
    });

    // Wait for initial loading to complete
    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    // Update preparation time
    await act(async () => {
      await result.current.updatePreparationTime(5);
    });

    // Verify savePreparationTime was called with the correct value
    expect(timerStore.savePreparationTime).toHaveBeenCalledWith(5);

    // Verify state was updated
    expect(result.current.timerState.preparationTime).toBe(5);
  });

  it('should handle errors when updating preparation time', async () => {
    // Simulate an error
    (timerStore.savePreparationTime as jest.Mock<Promise<void>>).mockRejectedValueOnce(new Error('Saving error'));

    // Spy on console.error
    jest.spyOn(console, 'error').mockImplementation(() => {});

    const { result } = renderHook(() => useTimerContext(), {
      wrapper: TimerProvider
    });

    // Wait for initial loading to complete
    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    // Attempt to update preparation time
    await act(async () => {
      await result.current.updatePreparationTime(5);
    });

    // Verify error was logged
    expect(console.error).toHaveBeenCalledWith(
      'Error updating preparation time:',
      expect.any(Error)
    );

    // Restore console.error
    (console.error as jest.MockedFunction<typeof console.error>).mockRestore();
  });

  it('should handle errors when updating multiple values at once', async () => {
    // Simulate an error
    (timerStore.saveState as jest.Mock<Promise<void>>).mockRejectedValueOnce(new Error('Saving error'));

    // Spy on console.error
    jest.spyOn(console, 'error').mockImplementation(() => {});

    const { result } = renderHook(() => useTimerContext(), {
      wrapper: TimerProvider
    });

    // Wait for initial loading to complete
    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    const newState = {
      reps: 8,
      workTime: 60,
      restTime: 20
    };

    // Attempt to update complete state
    await act(async () => {
      await result.current.updateState(newState);
    });

    // Verify error was logged
    expect(console.error).toHaveBeenCalledWith(
      'Error updating state:',
      expect.any(Error)
    );

    // Restore console.error
    (console.error as jest.MockedFunction<typeof console.error>).mockRestore();
  });

  it('should update multiple values at once', async () => {
    const { result } = renderHook(() => useTimerContext(), {
      wrapper: TimerProvider
    });

    // Wait for initial loading to complete
    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    const newState = {
      reps: 8,
      workTime: 60,
      restTime: 20
    };

    // Update complete state
    await act(async () => {
      await result.current.updateState(newState);
    });

    // Verify saveState was called with the correct state
    expect(timerStore.saveState).toHaveBeenCalledWith({
      ...mockTimerState,
      ...newState
    });

    // Verify state was updated
    expect(result.current.timerState).toEqual({
      ...mockTimerState,
      ...newState
    });
  });
});
