import SettingsPage from '@/features/Settings/SettingsPage'
import settingsReducer, {
  setIsBrowserDefaultTimeZone,
  setTimeZone
} from '@/features/Settings/SettingsSlice'
import userReducer, {
  getOpenPaasUserDataAsync,
  setTimezone as setUserTimeZone
} from '@/features/User/userSlice'
import { api } from '@/utils/apiUtils'
import { browserDefaultTimeZone } from '@/utils/timezone'
import { configureStore } from '@reduxjs/toolkit'
import { fireEvent, screen, waitFor } from '@testing-library/react'
import { renderWithProviders } from '../../utils/Renderwithproviders'

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

describe('Timezone synchronization after getOpenPaasUserDataAsync', () => {
  let apiGetSpy: jest.SpyInstance

  beforeEach(() => {
    jest.clearAllMocks()
    localStorage.clear()
    apiGetSpy = jest.spyOn(api, 'get')
  })

  afterEach(() => {
    apiGetSpy.mockRestore()
  })

  it('should sync timezone to both user and settings state after fetching user data', async () => {
    const mockUserData = {
      id: '667037022b752d0026472254',
      firstname: 'John',
      lastname: 'Doe',
      preferredEmail: ['test@test.com'],
      configurations: {
        modules: [
          {
            name: 'core',
            configurations: [
              { name: 'language', value: 'fr' },
              { name: 'datetime', value: { timeZone: 'Europe/Paris' } }
            ]
          }
        ]
      }
    }

    // Return a fake fetch-like response
    apiGetSpy.mockResolvedValue({
      json: async () => mockUserData
    })

    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' } },
          loading: false,
          error: null
        },
        settings: { language: 'en', timeZone: 'UTC', view: 'calendar' }
      }
    })

    const result = await store.dispatch(getOpenPaasUserDataAsync())

    expect(result.type).toBe('user/getOpenPaasUserData/fulfilled')

    const state = store.getState()
    expect(state.user.coreConfig.datetime.timeZone).toBe('Europe/Paris')
    expect(state.settings.timeZone).toBe('Europe/Paris')
    expect(localStorage.getItem('timeZone')).toBe('Europe/Paris')
  })

  it('should keep browser timezone if API response has no timezone configuration', async () => {
    const mockUserData = {
      id: '667037022b752d0026472254',
      firstname: 'John',
      lastname: 'Doe',
      preferredEmail: '[test@test.com](mailto:test@test.com)',
      configurations: {
        modules: [
          { name: 'core', configurations: [{ name: 'language', value: 'en' }] }
        ]
      }
    }

    apiGetSpy.mockResolvedValue({
      json: async () => mockUserData
    })

    const browserTimezone = browserDefaultTimeZone ?? 'UTC'

    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: browserTimezone }
          },
          loading: false,
          error: null
        },
        settings: {
          language: 'en',
          timeZone: browserTimezone,
          view: 'calendar'
        }
      }
    })

    const result = await store.dispatch(getOpenPaasUserDataAsync())

    expect(result.type).toBe('user/getOpenPaasUserData/fulfilled')

    const state = store.getState()
    expect(state.user.coreConfig.datetime.timeZone).toBe(null)
    expect(state.settings.timeZone).toBe(browserTimezone)
  })
})

// Mock localStorage
const localStorageMock = (() => {
  let store: Record<string, string> = {}
  return {
    getItem: (key: string) => store[key] || null,
    setItem: (key: string, value: string) => {
      store[key] = value
    },
    removeItem: (key: string) => {
      delete store[key]
    },
    clear: () => {
      store = {}
    }
  }
})()

Object.defineProperty(window, 'localStorage', {
  value: localStorageMock
})

describe('Timezone Logic - Backend to Frontend Flow', () => {
  let store: any

  beforeEach(() => {
    localStorageMock.clear()
    store = configureStore({
      reducer: {
        settings: settingsReducer,
        user: userReducer
      },
      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' } },
          loading: false,
          error: null
        },
        settings: { language: 'en', timeZone: 'UTC', view: 'calendar' }
      }
    })
  })

  describe('Backend Response Handling', () => {
    test('Backend returns NULL => Should use browser default timezone', async () => {
      const backendResponse = {
        firstname: 'John',
        lastname: 'Doe',
        id: '123',
        preferredEmail: 'john@example.com',
        configurations: {
          modules: [
            {
              name: 'core',
              configurations: [
                {
                  name: 'datetime',
                  value: {
                    timeZone: null
                  }
                }
              ]
            }
          ]
        }
      }

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

      const settingsState = store.getState().settings
      const userState = store.getState().user

      expect(settingsState.timeZone).toBe(browserDefaultTimeZone)
      expect(settingsState.isBrowserDefaultTimeZone).toBe(true)
      expect(userState.coreConfig.datetime.timeZone).toBe(null)
      expect(localStorage.getItem('timeZone')).toBe(browserDefaultTimeZone)
    })

    test('Backend returns SPECIFIC VALUE => Should use that value', async () => {
      const specificTimezone = 'America/New_York'
      const backendResponse = {
        firstname: 'Jane',
        lastname: 'Smith',
        id: '456',
        preferredEmail: 'jane@example.com',
        configurations: {
          modules: [
            {
              name: 'core',
              configurations: [
                {
                  name: 'datetime',
                  value: {
                    timeZone: specificTimezone
                  }
                }
              ]
            }
          ]
        }
      }

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

      const settingsState = store.getState().settings
      const userState = store.getState().user

      expect(settingsState.timeZone).toBe(specificTimezone)
      expect(settingsState.isBrowserDefaultTimeZone).toBe(false)
      expect(userState.coreConfig.datetime.timeZone).toBe(specificTimezone)
      expect(localStorage.getItem('timeZone')).toBe(specificTimezone)
    })

    test('Backend returns NO datetime config => Should use browser default', async () => {
      const backendResponse = {
        firstname: 'Bob',
        lastname: 'Johnson',
        id: '789',
        preferredEmail: 'bob@example.com',
        configurations: {
          modules: [
            {
              name: 'core',
              configurations: [
                // No datetime config
              ]
            }
          ]
        }
      }

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

      const settingsState = store.getState().settings
      const userState = store.getState().user

      expect(settingsState.timeZone).toBe(browserDefaultTimeZone)
      expect(settingsState.isBrowserDefaultTimeZone).toBe(true)
      expect(userState.coreConfig.datetime.timeZone).toBe(null)
      expect(localStorage.getItem('timeZone')).toBe(browserDefaultTimeZone)
    })
  })

  test('Settings state ALWAYS has a concrete value (never null)', () => {
    // Test 1: Browser default scenario
    store.dispatch(setIsBrowserDefaultTimeZone(true))
    let settingsState = store.getState().settings

    expect(settingsState.timeZone).toBe(browserDefaultTimeZone)
    expect(settingsState.timeZone).not.toBe(null)

    // Test 2: Specific timezone scenario
    store.dispatch(setTimeZone('Europe/Paris'))
    settingsState = store.getState().settings

    expect(settingsState.timeZone).toBe('Europe/Paris')
    expect(settingsState.timeZone).not.toBe(null)
  })

  describe('User Actions - Changing Timezone', () => {
    beforeEach(() => {
      ;(api.patch as jest.Mock).mockResolvedValue({ status: 204 })
    })
    test('User enables browser default => Settings gets browser TZ, User gets null', async () => {
      const { store } = renderWithProviders(<SettingsPage />, {
        user: {
          userData: { sub: 'test' },
          organiserData: null,
          tokens: null,
          coreConfig: {
            language: 'en',
            datetime: { timeZone: 'America/Los_Angeles' }
          },
          loading: false,
          error: null
        },
        settings: {
          language: 'en',
          timeZone: 'America/Los_Angeles',
          isBrowserDefaultTimeZone: false,
          view: 'settings'
        }
      })

      const browserDefaultSwitch = screen.getAllByLabelText(
        'settings.timeZoneBrowserDefault'
      )[0]
      expect(browserDefaultSwitch).toBeInTheDocument()

      // Enable browser default
      fireEvent.click(browserDefaultSwitch)

      await waitFor(() => {
        const state = store.getState()
        expect(state.settings.isBrowserDefaultTimeZone).toBe(true)
      })
      await waitFor(() => {
        const state = store.getState()
        expect(state.settings.timeZone).toBe(browserDefaultTimeZone)
      })
      await waitFor(() => {
        const state = store.getState()
        expect(state.user.coreConfig.datetime.timeZone).toBe(null)
      })
    })

    test('User selects specific timezone => Both states get the value', async () => {
      const { store } = renderWithProviders(<SettingsPage />, {
        user: {
          userData: { sub: 'test' },
          organiserData: null,
          tokens: null,
          coreConfig: { language: 'en', datetime: { timeZone: null } },
          loading: false,
          error: null
        },
        settings: {
          language: 'en',
          timeZone: browserDefaultTimeZone,
          isBrowserDefaultTimeZone: true,
          view: 'settings'
        }
      })

      const browserDefaultSwitch = screen.getAllByLabelText(
        'settings.timeZoneBrowserDefault'
      )[0]

      // Disable browser default so manual selector appears
      fireEvent.click(browserDefaultSwitch)

      // Now timezone combobox is visible
      const timezoneInput = screen.getAllByRole('combobox')[1]

      // Type to filter options
      fireEvent.change(timezoneInput, {
        target: { value: 'Australia/Sydney' }
      })

      // Select from autocomplete dropdown
      const option = await screen.findByText(/Australia\/Sydney/i)
      fireEvent.click(option)

      await waitFor(() => {
        const state = store.getState()
        expect(state.settings.isBrowserDefaultTimeZone).toBe(false)
      })
      await waitFor(() => {
        const state = store.getState()
        expect(state.settings.timeZone).toBe('Australia/Sydney')
      })
      await waitFor(() => {
        const state = store.getState()
        expect(state.user.coreConfig.datetime.timeZone).toBe('Australia/Sydney')
      })
    })
  })

  describe('LocalStorage Persistence', () => {
    test('LocalStorage always stores concrete values (browser TZ or specific TZ)', () => {
      // Scenario 1: Browser default
      store.dispatch(setTimeZone(browserDefaultTimeZone))
      expect(localStorage.getItem('timeZone')).toBe(browserDefaultTimeZone)
      expect(localStorage.getItem('timeZone')).not.toBe('null')

      // Scenario 2: Specific timezone
      store.dispatch(setTimeZone('Africa/Cairo'))
      expect(localStorage.getItem('timeZone')).toBe('Africa/Cairo')
    })
  })

  test('Full user journey: Backend null -> User changes -> Backend saves', async () => {
    // Step 1: Backend returns null
    const backendResponseNull = {
      firstname: 'Test',
      lastname: 'User',
      id: '999',
      preferredEmail: 'test@example.com',
      configurations: {
        modules: [
          {
            name: 'core',
            configurations: [
              {
                name: 'datetime',
                value: { timeZone: null }
              }
            ]
          }
        ]
      }
    }

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

    // Verify initial state
    expect(store.getState().settings.timeZone).toBe(browserDefaultTimeZone)
    expect(store.getState().user.coreConfig.datetime.timeZone).toBe(null)

    // Step 2: User changes to specific timezone
    const userSelectedTZ = 'Europe/London'
    store.dispatch(setIsBrowserDefaultTimeZone(false))
    store.dispatch(setTimeZone(userSelectedTZ))
    store.dispatch(setUserTimeZone(userSelectedTZ))

    expect(store.getState().settings.timeZone).toBe(userSelectedTZ)
    expect(store.getState().user.coreConfig.datetime.timeZone).toBe(
      userSelectedTZ
    )

    // Step 3: User switches back to browser default
    store.dispatch(setIsBrowserDefaultTimeZone(true))
    store.dispatch(setUserTimeZone(null))
    store.dispatch(setTimeZone(browserDefaultTimeZone))

    expect(store.getState().settings.timeZone).toBe(browserDefaultTimeZone)
    expect(store.getState().user.coreConfig.datetime.timeZone).toBe(null)
  })
})
