import * as CalendarApi from '@/features/Calendars/CalendarApi'
import * as EventApi from '@/features/Events/EventApi'
import { CalendarEvent } from '@/features/Events/EventsTypes'
import EventUpdateModal from '@/features/Events/EventUpdateModal'
import { act, fireEvent, screen, waitFor } from '@testing-library/react'
import { renderWithProviders } from '../../utils/Renderwithproviders'

jest.mock('@/features/Events/EventApi')
jest.mock('@/features/Calendars/CalendarApi')

describe('EventUpdateModal Timezone Handling', () => {
  const mockOnClose = jest.fn()

  beforeEach(() => {
    jest.clearAllMocks()
    sessionStorage.clear()
  })

  const preloadedState = {
    user: {
      userData: {
        sub: 'test',
        email: 'test@test.com',
        sid: 'test-sid',
        openpaasId: '667037022b752d0026472254'
      },
      organiserData: {
        cn: 'test',
        cal_address: 'test@test.com'
      }
    },
    calendars: {
      list: {
        '667037022b752d0026472254/cal1': {
          id: '667037022b752d0026472254/cal1',
          name: 'Test Calendar',
          color: '#FF0000',
          events: {}
        }
      },
      pending: false
    }
  }

  it('displays event time in original timezone when editing event', async () => {
    // GIVEN user timezone is UTC+2
    // WHEN the user edits an event at 2PM UTC+7 (Asia/Bangkok)
    // THEN the update modal prompts him date with 2PM UTC+7

    // Create event at 2PM UTC+7 (Asia/Bangkok)
    // 2PM UTC+7 = 14:00 in Bangkok = 07:00 UTC
    const eventDateUTC = new Date('2025-01-15T07:00:00.000Z')

    const eventData = {
      uid: 'test-event-1',
      title: 'Timezone Event',
      calId: '667037022b752d0026472254/cal1',
      start: eventDateUTC.toISOString(),
      end: new Date(eventDateUTC.getTime() + 3600000).toISOString(),
      timezone: 'Asia/Bangkok',
      allday: false,
      organizer: { cn: 'test', cal_address: 'test@test.com' },
      attendee: [{ cn: 'test', cal_address: 'test@test.com' }],
      URL: '/calendars/667037022b752d0026472254/cal1/test-event-1.ics'
    } as CalendarEvent

    const stateWithEvent = {
      ...preloadedState,
      calendars: {
        ...preloadedState.calendars,
        list: {
          '667037022b752d0026472254/cal1': {
            ...preloadedState.calendars.list['667037022b752d0026472254/cal1'],
            events: {
              'test-event-1': eventData
            }
          }
        }
      }
    }

    renderWithProviders(
      <EventUpdateModal
        open={true}
        onClose={mockOnClose}
        calId={'667037022b752d0026472254/cal1'}
        eventId={'test-event-1'}
        eventData={eventData}
      />,
      stateWithEvent
    )

    // Verify title is displayed
    const titleInput = screen.getByDisplayValue('Timezone Event')
    expect(titleInput).toBeInTheDocument()

    // Expand to show date/time inputs (normal mode shows DateTimeSummary)
    fireEvent.click(screen.getByRole('button', { name: 'common.moreOptions' }))

    // Verify the start date and time inputs exist
    await waitFor(() => {
      const startDateInput = screen.getByTestId('start-date-input')
      const startTimeInput = screen.getByTestId('start-time-input')

      expect(startDateInput).toBeInTheDocument()
      expect(startTimeInput).toBeInTheDocument()

      // MUI DatePicker/TimePicker values are stored differently - just check elements exist
      // The actual values are verified through the form submission
    })
  })

  it('preserves original timezone when editing event fields', async () => {
    const eventDateUTC = new Date('2025-01-15T07:00:00.000Z')

    const eventData = {
      uid: 'test-event-2',
      title: 'Original Event',
      calId: '667037022b752d0026472254/cal1',
      start: eventDateUTC.toISOString(),
      end: new Date(eventDateUTC.getTime() + 3600000).toISOString(),
      timezone: 'Asia/Bangkok',
      allday: false,
      organizer: { cn: 'test', cal_address: 'test@test.com' },
      attendee: [{ cn: 'test', cal_address: 'test@test.com' }],
      URL: '/calendars/667037022b752d0026472254/cal1/test-event-2.ics'
    } as CalendarEvent

    const stateWithEvent = {
      ...preloadedState,
      calendars: {
        ...preloadedState.calendars,
        list: {
          '667037022b752d0026472254/cal1': {
            ...preloadedState.calendars.list['667037022b752d0026472254/cal1'],
            events: {
              'test-event-2': eventData
            }
          }
        }
      }
    }

    renderWithProviders(
      <EventUpdateModal
        open={true}
        onClose={mockOnClose}
        calId={'667037022b752d0026472254/cal1'}
        eventId={'test-event-2'}
        eventData={eventData}
      />,
      stateWithEvent
    )

    // Edit the title
    const titleInput = screen.getByDisplayValue('Original Event')
    fireEvent.change(titleInput, { target: { value: 'Updated Event' } })

    // Verify the timezone is still preserved (should be Asia/Bangkok)
    expect(titleInput).toHaveValue('Updated Event')
  })

  it('preserves resources when editing an event', async () => {
    const eventDateUTC = new Date('2025-01-15T07:00:00.000Z')

    const eventData = {
      uid: 'test-event-resource',
      title: 'Resource Event',
      calId: '667037022b752d0026472254/cal1',
      start: eventDateUTC.toISOString(),
      end: new Date(eventDateUTC.getTime() + 3600000).toISOString(),
      timezone: 'Asia/Bangkok',
      allday: false,
      organizer: { cn: 'test', cal_address: 'test@test.com' },
      attendee: [
        { cn: 'test', cal_address: 'test@test.com' },
        {
          cn: 'Conference Room',
          cal_address: 'room@test.com',
          partstat: 'ACCEPTED',
          rsvp: 'TRUE',
          role: 'REQ-PARTICIPANT',
          cutype: 'RESOURCE'
        }
      ],
      URL: '/calendars/667037022b752d0026472254/cal1/test-event-resource.ics'
    } as CalendarEvent

    const stateWithEvent = {
      ...preloadedState,
      calendars: {
        ...preloadedState.calendars,
        list: {
          '667037022b752d0026472254/cal1': {
            ...preloadedState.calendars.list['667037022b752d0026472254/cal1'],
            events: {
              'test-event-resource': eventData
            }
          }
        }
      }
    }

    const mockPutEvent = jest.spyOn(EventApi, 'putEvent').mockResolvedValue({
      status: 201,
      url: `/calendars/667037022b752d0026472254/cal1/test-event-resource.ics`
    } as any)

    renderWithProviders(
      <EventUpdateModal
        open={true}
        onClose={mockOnClose}
        calId={'667037022b752d0026472254/cal1'}
        eventId={'test-event-resource'}
        eventData={eventData}
      />,
      stateWithEvent
    )

    // Edit the title
    const titleInput = screen.getByDisplayValue('Resource Event')
    fireEvent.change(titleInput, {
      target: { value: 'Updated Resource Event' }
    })

    // Click Save
    const saveButton = screen.getByRole('button', { name: 'actions.save' })
    await act(async () => {
      fireEvent.click(saveButton)
    })

    await waitFor(() => {
      expect(mockPutEvent).toHaveBeenCalled()
    })

    const putEventCall = mockPutEvent.mock.calls[0][0]
    expect(putEventCall.title).toBe('Updated Resource Event')

    // Check that the resource is still in the attendee list!
    const attendees = putEventCall.attendee
    const resource = attendees.find((a: any) => a.cutype === 'RESOURCE')
    expect(resource).toBeDefined()
    expect(resource!.cn).toBe('Conference Room')
    expect(resource!.cal_address).toBe('room@test.com')
    expect(resource!.partstat).toBe('ACCEPTED')
    expect(resource!.rsvp).toBe('TRUE')
    expect(resource!.role).toBe('REQ-PARTICIPANT')
    expect(resource!.cutype).toBe('RESOURCE')
  })
})

describe('EventUpdateModal Recurring to Non-Recurring Conversion', () => {
  const mockOnClose = jest.fn()

  beforeEach(() => {
    jest.clearAllMocks()
    jest.restoreAllMocks()
  })

  const baseUID = 'recurring-event-base'
  const calId = '667037022b752d0026472254/cal1'

  const preloadedState = {
    user: {
      userData: {
        sub: 'test',
        email: 'test@test.com',
        sid: 'test-sid',
        openpaasId: '667037022b752d0026472254'
      },
      organiserData: {
        cn: 'test',
        cal_address: 'test@test.com'
      }
    },
    calendars: {
      list: {
        [calId]: {
          id: calId,
          name: 'Test Calendar',
          color: '#FF0000',
          events: {}
        }
      },
      pending: false
    }
  }

  it('converts recurring event to non-recurring when repeat is disabled in edit all mode', async () => {
    // Create recurring event with multiple instances
    const eventDate = new Date('2025-01-15T10:00:00.000Z')

    const masterEvent = {
      uid: baseUID,
      title: 'Recurring Meeting',
      calId,
      start: eventDate.toISOString(),
      end: new Date(eventDate.getTime() + 3600000).toISOString(),
      repetition: { freq: 'daily', interval: 1 },
      allday: false,
      organizer: { cn: 'test', cal_address: 'test@test.com' },
      attendee: [{ cn: 'test', cal_address: 'test@test.com' }],
      URL: `/calendars/${calId}/${baseUID}.ics`,
      timezone: 'UTC'
    } as CalendarEvent

    const instance1 = {
      ...masterEvent,
      uid: `${baseUID}/20250115`,
      start: '2025-01-15T10:00:00.000Z',
      end: '2025-01-15T11:00:00.000Z'
    }

    const instance2 = {
      ...masterEvent,
      uid: `${baseUID}/20250116`,
      start: '2025-01-16T10:00:00.000Z',
      end: '2025-01-16T11:00:00.000Z'
    }

    const instance3 = {
      ...masterEvent,
      uid: `${baseUID}/20250117`,
      start: '2025-01-17T10:00:00.000Z',
      end: '2025-01-17T11:00:00.000Z'
    }

    const stateWithRecurringEvent = {
      ...preloadedState,
      calendars: {
        ...preloadedState.calendars,
        list: {
          [calId]: {
            ...preloadedState.calendars.list[calId],
            events: {
              [baseUID]: masterEvent,
              [`${baseUID}/20250115`]: instance1,
              [`${baseUID}/20250116`]: instance2,
              [`${baseUID}/20250117`]: instance3
            }
          }
        }
      }
    }

    // Mock API calls
    const mockDeleteEvent = jest
      .spyOn(EventApi, 'deleteEvent')
      .mockResolvedValue({} as any)
    const mockPutEvent = jest.spyOn(EventApi, 'putEvent').mockResolvedValue({
      status: 201,
      url: `/calendars/${calId}/new-event.ics`
    } as any)
    const mockGetMasterEvent = jest
      .spyOn(EventApi, 'getEvent')
      .mockResolvedValue(masterEvent)
    jest.spyOn(CalendarApi, 'getCalendar').mockResolvedValue({
      _embedded: {
        'dav:item': []
      }
    } as any)

    const { store } = renderWithProviders(
      <EventUpdateModal
        open={true}
        onClose={mockOnClose}
        calId={calId}
        eventId={`${baseUID}/20250115`}
        typeOfAction="all"
      />,
      stateWithRecurringEvent
    )

    // Wait for master event to be fetched AND form to be initialized
    await waitFor(() => {
      expect(mockGetMasterEvent).toHaveBeenCalled()
    })

    fireEvent.click(screen.getByRole('button', { name: 'common.moreOptions' }))
    // Wait for the repeat checkbox to be checked (indicating form is initialized)
    const repeatCheckbox = await waitFor(() => {
      const checkbox = screen.getByLabelText('event.form.repeat')
      expect(checkbox).toBeChecked()
      return checkbox
    })

    // Now uncheck repeat checkbox
    await act(async () => {
      fireEvent.click(repeatCheckbox)
    })

    // Wait for checkbox to be unchecked
    await waitFor(() => {
      expect(repeatCheckbox).not.toBeChecked()
    })

    // Click Save button
    const saveButton = screen.getByRole('button', { name: 'actions.save' })

    await act(async () => {
      fireEvent.click(saveButton)
    })

    await waitFor(() => {
      expect(mockPutEvent).toHaveBeenCalled()
    })

    // Verify API calls - should delete all instances
    await waitFor(
      () => {
        // Should have called deleteEvent for each instance (4 total: base + 3 recurrences)
        expect(mockDeleteEvent).toHaveBeenCalled()
        // At least one instance should be deleted
        expect(mockDeleteEvent.mock.calls.length).toBeGreaterThan(0)
      },
      { timeout: 3000 }
    )

    // Verify new event was created via putEvent
    expect(mockPutEvent).toHaveBeenCalled()
    const putEventCall = mockPutEvent.mock.calls[0][0]
    expect(putEventCall.title).toBe('Recurring Meeting')
    expect(putEventCall.repetition?.freq).toBeFalsy()
    expect(putEventCall.uid).not.toContain(baseUID)

    // Verify Redux store state changes
    const finalState = store.getState()
    const calendar = finalState.calendars.list[calId]

    // Old recurring instances should be removed from store
    expect(calendar.events[baseUID]).toBeUndefined()
    expect(calendar.events[`${baseUID}/20250115`]).toBeUndefined()
    expect(calendar.events[`${baseUID}/20250116`]).toBeUndefined()
    expect(calendar.events[`${baseUID}/20250117`]).toBeUndefined()

    // Verify modal was closed after completion
    await waitFor(() => {
      expect(mockOnClose).toHaveBeenCalled()
    })
  })

  it('gracefully handles deletion errors and continues to create new event', async () => {
    const eventDate = new Date('2025-01-15T10:00:00.000Z')

    const masterEvent = {
      uid: baseUID,
      title: 'Recurring Meeting',
      calId,
      start: eventDate.toISOString(),
      end: new Date(eventDate.getTime() + 3600000).toISOString(),
      repetition: { freq: 'weekly', interval: 1 },
      allday: false,
      organizer: { cn: 'test', cal_address: 'test@test.com' },
      attendee: [{ cn: 'test', cal_address: 'test@test.com' }],
      URL: `/calendars/${calId}/${baseUID}.ics`,
      timezone: 'UTC'
    } as CalendarEvent

    const instance1 = {
      ...masterEvent,
      uid: `${baseUID}/20250115`
    }

    const stateWithRecurringEvent = {
      ...preloadedState,
      calendars: {
        ...preloadedState.calendars,
        list: {
          [calId]: {
            ...preloadedState.calendars.list[calId],
            events: {
              [baseUID]: masterEvent,
              [`${baseUID}/20250115`]: instance1
            }
          }
        }
      }
    }

    // Mock deleteEvent to fail with a non-404 error
    const mockDeleteEvent = jest
      .spyOn(EventApi, 'deleteEvent')
      .mockRejectedValue(new Error('Network error'))
    const mockPutEvent = jest
      .spyOn(EventApi, 'putEvent')
      .mockResolvedValue({ status: 201 } as any)
    const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation()
    const mockGetMasterEvent = jest
      .spyOn(EventApi, 'getEvent')
      .mockResolvedValue(masterEvent)

    const { store } = renderWithProviders(
      <EventUpdateModal
        open={true}
        onClose={mockOnClose}
        calId={calId}
        eventId={`${baseUID}/20250115`}
        typeOfAction="all"
      />,
      stateWithRecurringEvent
    )

    // Wait for master event to be fetched
    await waitFor(() => {
      expect(mockGetMasterEvent).toHaveBeenCalled()
    })

    // Wait for component to load
    await waitFor(() => {
      expect(screen.getByDisplayValue('Recurring Meeting')).toBeInTheDocument()
    })

    fireEvent.click(screen.getByRole('button', { name: 'common.moreOptions' }))
    // Wait for repeat checkbox to be checked
    const repeatCheckbox = await waitFor(() => {
      const checkbox = screen.getByLabelText('event.form.repeat')
      expect(checkbox).toBeChecked()
      return checkbox
    })

    // Uncheck repeat checkbox and save
    await act(async () => {
      fireEvent.click(repeatCheckbox)
    })

    // Wait for checkbox to be unchecked
    await waitFor(() => {
      expect(repeatCheckbox).not.toBeChecked()
    })

    const saveButton = screen.getByRole('button', { name: 'actions.save' })

    await act(async () => {
      fireEvent.click(saveButton)
    })

    // Wait for delete to be attempted
    await waitFor(
      () => {
        expect(mockDeleteEvent).toHaveBeenCalled()
      },
      { timeout: 3000 }
    )

    // Verify error was logged for failed deletion
    await waitFor(
      () => {
        expect(consoleErrorSpy).toHaveBeenCalledWith(
          expect.stringContaining('Failed to delete event file')
        )
      },
      { timeout: 3000 }
    )

    // New implementation gracefully handles deletion errors:
    // Even if some instances fail to delete, we still create the new event
    // This ensures user gets their non-recurring event
    await waitFor(
      () => {
        expect(mockPutEvent).toHaveBeenCalled()
      },
      { timeout: 3000 }
    )

    const putEventCall = mockPutEvent.mock.calls[0][0]
    expect(putEventCall.title).toBe('Recurring Meeting')
    expect(putEventCall.repetition?.freq).toBeFalsy()

    // Modal should close after operation completes
    await waitFor(
      () => {
        expect(mockOnClose).toHaveBeenCalled()
      },
      { timeout: 3000 }
    )

    consoleErrorSpy.mockRestore()
  })

  it('closes all modals after converting recurring to non-recurring', async () => {
    const eventDate = new Date('2025-01-15T10:00:00.000Z')

    const masterEvent = {
      uid: baseUID,
      title: 'Recurring Meeting',
      calId,
      start: eventDate.toISOString(),
      end: new Date(eventDate.getTime() + 3600000).toISOString(),
      repetition: { freq: 'monthly', interval: 1 },
      allday: false,
      organizer: { cn: 'test', cal_address: 'test@test.com' },
      attendee: [{ cn: 'test', cal_address: 'test@test.com' }],
      URL: `/calendars/${calId}/${baseUID}.ics`,
      timezone: 'UTC'
    } as CalendarEvent

    const instance1 = {
      ...masterEvent,
      uid: `${baseUID}/20250115`
    }

    const stateWithRecurringEvent = {
      ...preloadedState,
      calendars: {
        ...preloadedState.calendars,
        list: {
          [calId]: {
            ...preloadedState.calendars.list[calId],
            events: {
              [baseUID]: masterEvent,
              [`${baseUID}/20250115`]: instance1
            }
          }
        }
      }
    }

    // Mock API calls
    jest.spyOn(EventApi, 'deleteEvent').mockResolvedValue({} as any)
    jest.spyOn(EventApi, 'putEvent').mockResolvedValue({ status: 201 } as any)
    jest.spyOn(CalendarApi, 'getCalendar').mockResolvedValue({
      _embedded: {
        'dav:item': []
      }
    } as any)

    // Mock onCloseAll to test closing both modals
    const mockOnCloseAll = jest.fn()

    renderWithProviders(
      <EventUpdateModal
        open={true}
        onClose={mockOnClose}
        onCloseAll={mockOnCloseAll}
        calId={calId}
        eventId={`${baseUID}/20250115`}
        typeOfAction="all"
      />,
      stateWithRecurringEvent
    )

    // Wait for component to load
    await waitFor(() => {
      expect(screen.getByDisplayValue('Recurring Meeting')).toBeInTheDocument()
    })

    fireEvent.click(screen.getByRole('button', { name: 'common.moreOptions' }))
    // Uncheck repeat and save
    const repeatCheckbox = screen.getByLabelText('event.form.repeat')

    await act(async () => {
      fireEvent.click(repeatCheckbox)
    })

    const saveButton = screen.getByRole('button', { name: 'actions.save' })

    await act(async () => {
      fireEvent.click(saveButton)
    })

    // Verify onCloseAll was called to close both preview and update modals
    await waitFor(
      () => {
        expect(mockOnCloseAll).toHaveBeenCalled()
      },
      { timeout: 3000 }
    )

    // Verify onClose was NOT called (onCloseAll is used instead)
    expect(mockOnClose).not.toHaveBeenCalled()
  })
})
