import SettingsPage from '@/features/Settings/SettingsPage'
import settingsReducer from '@/features/Settings/SettingsSlice'
import userReducer, {
  getOpenPaasUserDataAsync
} from '@/features/User/userSlice'
import { api } from '@/utils/apiUtils'
import { configureStore } from '@reduxjs/toolkit'
import '@testing-library/jest-dom'
import { fireEvent, screen, waitFor } from '@testing-library/react'
import { renderWithProviders } from '../../utils/Renderwithproviders'

jest.mock('@/utils/apiUtils')

const basePreloadedState = {
  user: {
    userData: {
      sub: 'test',
      email: 'test@test.com',
      family_name: 'Doe',
      name: 'John',
      sid: 'mockSid',
      openpaasId: '667037022b752d0026472254'
    },
    organiserData: null,
    tokens: null,
    coreConfig: {
      language: 'en',
      datetime: { timeZone: 'UTC' }
    },
    alarmEmailsEnabled: false,
    loading: false,
    error: null
  },
  settings: {
    language: 'en',
    timeZone: 'UTC',
    isBrowserDefaultTimeZone: false,
    view: 'settings',
    businessHours: {
      start: '8:0',
      end: '19:0',
      daysOfWeek: [1, 2, 3, 4, 5]
    },
    workingDays: false
  }
}

describe('Working Days and Business Hours Settings', () => {
  beforeEach(() => {
    jest.clearAllMocks()
    ;(api.patch as jest.Mock).mockResolvedValue({ status: 204 })
  })

  describe('Initial rendering', () => {
    it('renders the working days section with day selector', () => {
      renderWithProviders(<SettingsPage />, basePreloadedState)

      expect(screen.getByText('settings.chooseWorkingDays')).toBeInTheDocument()
    })

    it('renders the show only working days toggle', () => {
      renderWithProviders(<SettingsPage />, basePreloadedState)

      expect(
        screen.getByRole('switch', { name: /settings.showOnlyWorkingDays/i })
      ).toBeInTheDocument()
    })

    it('shows working days toggle as unchecked when workingDays is false', () => {
      renderWithProviders(<SettingsPage />, basePreloadedState)

      const toggle = screen.getByRole('switch', {
        name: /settings.showOnlyWorkingDays/i
      }) as HTMLInputElement
      expect(toggle.checked).toBe(false)
    })

    it('shows working days toggle as checked when workingDays is true', () => {
      renderWithProviders(<SettingsPage />, {
        ...basePreloadedState,
        settings: { ...basePreloadedState.settings, workingDays: true }
      })

      const toggle = screen.getByRole('switch', {
        name: /settings.showOnlyWorkingDays/i
      }) as HTMLInputElement
      expect(toggle.checked).toBe(true)
    })

    it('shows working days toggle as unchecked when workingDays is null', () => {
      renderWithProviders(<SettingsPage />, {
        ...basePreloadedState,
        settings: { ...basePreloadedState.settings, workingDays: null }
      })

      const toggle = screen.getByRole('switch', {
        name: /settings.showOnlyWorkingDays/i
      }) as HTMLInputElement
      expect(toggle.checked).toBe(false)
    })
  })

  describe('workingDays toggle', () => {
    it('updates workingDays immediately (optimistic update) and calls API', async () => {
      const { store } = renderWithProviders(
        <SettingsPage />,
        basePreloadedState
      )

      const toggle = screen.getByRole('switch', {
        name: /settings.showOnlyWorkingDays/i
      })
      fireEvent.click(toggle)

      await waitFor(() => {
        expect(store.getState().settings.workingDays).toBe(true)
      })

      await waitFor(() => {
        expect(api.patch).toHaveBeenCalledWith(
          'api/configurations?scope=user',
          expect.objectContaining({
            json: expect.arrayContaining([
              expect.objectContaining({
                name: 'linagora.esn.calendar',
                configurations: expect.arrayContaining([
                  expect.objectContaining({
                    name: 'workingDays',
                    value: true
                  })
                ])
              })
            ])
          })
        )
      })
    })

    it('rolls back workingDays if API call fails', async () => {
      ;(api.patch as jest.Mock).mockRejectedValue(new Error('API Error'))

      const { store } = renderWithProviders(
        <SettingsPage />,
        basePreloadedState
      )

      const toggle = screen.getByRole('switch', {
        name: /settings.showOnlyWorkingDays/i
      })
      fireEvent.click(toggle)

      await waitFor(
        () => {
          expect(store.getState().settings.workingDays).toBe(false)
        },
        { timeout: 3000 }
      )

      await waitFor(() => {
        expect(
          screen.getByText('settings.workingDaysUpdateError')
        ).toBeInTheDocument()
      })
    })

    it('toggles workingDays from true to false', async () => {
      const { store } = renderWithProviders(<SettingsPage />, {
        ...basePreloadedState,
        settings: { ...basePreloadedState.settings, workingDays: true }
      })

      const toggle = screen.getByRole('switch', {
        name: /settings.showOnlyWorkingDays/i
      })
      fireEvent.click(toggle)

      await waitFor(() => {
        expect(store.getState().settings.workingDays).toBe(false)
      })

      await waitFor(() => {
        expect(api.patch).toHaveBeenCalledWith(
          'api/configurations?scope=user',
          expect.objectContaining({
            json: expect.arrayContaining([
              expect.objectContaining({
                name: 'linagora.esn.calendar',
                configurations: expect.arrayContaining([
                  expect.objectContaining({
                    name: 'workingDays',
                    value: false
                  })
                ])
              })
            ])
          })
        )
      })
    })
  })

  describe('businessHours day selector', () => {
    it('updates businessHours in Redux immediately when a day is clicked', async () => {
      const { store } = renderWithProviders(
        <SettingsPage />,
        basePreloadedState
      )

      // Sunday (0) is not in initial daysOfWeek [1,2,3,4,5], click it to add
      const sundayButton = screen.getByRole('button', {
        name: /sunday/i
      })
      fireEvent.click(sundayButton)

      await waitFor(() => {
        expect(store.getState().settings.businessHours?.daysOfWeek).toContain(0)
      })
    })

    it('removes a day from businessHours when an already selected day is clicked', async () => {
      const { store } = renderWithProviders(
        <SettingsPage />,
        basePreloadedState
      )

      // Monday (1) is in initial daysOfWeek, click to remove
      const mondayButton = screen.getByRole('button', { name: /monday/i })
      fireEvent.click(mondayButton)

      await waitFor(() => {
        expect(
          store.getState().settings.businessHours?.daysOfWeek
        ).not.toContain(1)
      })
    })

    it('debounces API call when multiple days are clicked rapidly', async () => {
      jest.useFakeTimers()

      renderWithProviders(<SettingsPage />, basePreloadedState)

      const sundayButton = screen.getByRole('button', { name: /sunday/i })
      const saturdayButton = screen.getByRole('button', { name: /saturday/i })

      fireEvent.click(sundayButton)
      fireEvent.click(saturdayButton)
      fireEvent.click(sundayButton)

      // API should not have been called yet
      expect(api.patch).not.toHaveBeenCalled()

      // Advance timers past debounce threshold
      jest.runAllTimers()

      await waitFor(() => {
        expect(api.patch).toHaveBeenCalledTimes(1)
      })

      jest.useRealTimers()
    })

    it('sends businessHours as array to API', async () => {
      jest.useFakeTimers()

      renderWithProviders(<SettingsPage />, basePreloadedState)

      const sundayButton = screen.getByRole('button', { name: /sunday/i })
      fireEvent.click(sundayButton)

      jest.runAllTimers()

      await waitFor(() => {
        expect(api.patch).toHaveBeenCalledWith(
          'api/configurations?scope=user',
          expect.objectContaining({
            json: expect.arrayContaining([
              expect.objectContaining({
                name: 'core',
                configurations: expect.arrayContaining([
                  expect.objectContaining({
                    name: 'businessHours',
                    value: expect.arrayContaining([
                      expect.objectContaining({
                        start: '8:0',
                        end: '19:0',
                        daysOfWeek: expect.arrayContaining([0])
                      })
                    ])
                  })
                ])
              })
            ])
          })
        )
      })

      jest.useRealTimers()
    })

    it('rolls back businessHours if API call fails', async () => {
      jest.useFakeTimers()
      ;(api.patch as jest.Mock).mockRejectedValue(new Error('API Error'))

      const { store } = renderWithProviders(
        <SettingsPage />,
        basePreloadedState
      )

      const sundayButton = screen.getByRole('button', { name: /sunday/i })
      fireEvent.click(sundayButton)

      jest.runAllTimers()

      await waitFor(
        () => {
          expect(
            store.getState().settings.businessHours?.daysOfWeek
          ).not.toContain(0)
        },
        { timeout: 3000 }
      )

      await waitFor(() => {
        expect(
          screen.getByText('settings.workingDaysUpdateError')
        ).toBeInTheDocument()
      })

      jest.useRealTimers()
    })
  })

  describe('getOpenPaasUserDataAsync integration', () => {
    it('populates businessHours from core module configurations', async () => {
      const store = configureStore({
        reducer: { user: userReducer, settings: settingsReducer },
        preloadedState: {
          user: {
            userData: {
              sub: 'test',
              email: 'test@test.com',
              family_name: 'Doe',
              name: 'John',
              sid: 'mockSid',
              openpaasId: '667037022b752d0026472254'
            },
            organiserData: null,
            tokens: null,
            coreConfig: { language: 'en', datetime: { timeZone: 'UTC' } },
            alarmEmailsEnabled: null,
            loading: false,
            error: null
          },
          settings: {
            language: 'en',
            timeZone: 'UTC',
            isBrowserDefaultTimeZone: false,
            view: 'calendar',
            businessHours: null,
            workingDays: null
          }
        }
      })

      const mockResponse = {
        id: '667037022b752d0026472254',
        firstname: 'John',
        lastname: 'Doe',
        preferredEmail: 'test@test.com',
        configurations: {
          modules: [
            {
              name: 'core',
              configurations: [
                {
                  name: 'businessHours',
                  value: [
                    { start: '8:0', end: '19:0', daysOfWeek: [1, 2, 3, 4, 5] }
                  ]
                }
              ]
            },
            {
              name: 'linagora.esn.calendar',
              configurations: [{ name: 'workingDays', value: true }]
            }
          ]
        }
      }

      await store.dispatch(
        getOpenPaasUserDataAsync.fulfilled(mockResponse, '', undefined)
      )

      const state = store.getState()
      expect(state.settings.businessHours).toEqual({
        start: '8:0',
        end: '19:0',
        daysOfWeek: [1, 2, 3, 4, 5]
      })
      expect(state.settings.workingDays).toBe(true)
    })

    it('sets businessHours to null when not present in API response', async () => {
      const store = configureStore({
        reducer: { user: userReducer, settings: settingsReducer },
        preloadedState: {
          user: {
            userData: {
              sub: 'test',
              email: 'test@test.com',
              family_name: 'Doe',
              name: 'John',
              sid: 'mockSid',
              openpaasId: '667037022b752d0026472254'
            },
            organiserData: null,
            tokens: null,
            coreConfig: { language: 'en', datetime: { timeZone: 'UTC' } },
            alarmEmailsEnabled: null,
            loading: false,
            error: null
          },
          settings: {
            language: 'en',
            timeZone: 'UTC',
            isBrowserDefaultTimeZone: false,
            view: 'calendar',
            businessHours: null,
            workingDays: null
          }
        }
      })

      const mockResponse = {
        id: '667037022b752d0026472254',
        firstname: 'John',
        lastname: 'Doe',
        preferredEmail: 'test@test.com',
        configurations: {
          modules: [
            {
              name: 'core',
              configurations: []
            },
            {
              name: 'linagora.esn.calendar',
              configurations: [{ name: 'workingDays', value: null }]
            }
          ]
        }
      }

      await store.dispatch(
        getOpenPaasUserDataAsync.fulfilled(mockResponse, '', undefined)
      )

      const state = store.getState()
      expect(state.settings.businessHours).toBeNull()
      expect(state.settings.workingDays).toBeNull()
    })
  })
})
