import { CalendarEvent } from '@/features/Events/EventsTypes'
import { updateAttendeesAfterTimeChange } from '@/features/Events/updateEventHelpers/updateAttendeesAfterTimeChange'
import { userAttendee } from '@/features/User/models/attendee'
import { userOrganiser } from '@/features/User/userDataTypes'

describe('updateAttendeesAfterTimeChange', () => {
  const mockAttendee: userAttendee = {
    cal_address: 'attendee@example.com',
    partstat: 'ACCEPTED',
    rsvp: 'FALSE',
    role: 'REQ-PARTICIPANT',
    cutype: 'INDIVIDUAL',
    cn: 'attendee'
  }

  const mockOrganizer: userOrganiser = {
    cn: 'Organizer',
    cal_address: 'organizer@example.com'
  }

  const organizerAttendee: userAttendee = {
    cal_address: 'organizer@example.com',
    partstat: 'ACCEPTED',
    rsvp: 'FALSE',
    role: 'CHAIR',
    cutype: 'INDIVIDUAL',
    cn: 'organizer'
  }

  const baseEvent: CalendarEvent = {
    uid: 'test-event',
    URL: '/calendar/667037022b752d0026472254/cal1/test-event.ics',
    title: 'Test Event',
    calId: '667037022b752d0026472254/cal1',
    start: '2025-01-15T07:00:00.000Z',
    end: '2025-01-15T08:00:00.000Z',
    allday: false,
    organizer: mockOrganizer,
    attendee: [mockAttendee, organizerAttendee],
    timezone: 'ETC/UTC'
  }

  describe('early returns', () => {
    it('should return the event unchanged when attendee list is undefined', () => {
      const event = { ...baseEvent, attendee: undefined }
      const result = updateAttendeesAfterTimeChange(
        event as unknown as CalendarEvent
      )
      expect(result).toBe(event)
    })

    it('should NOT bail out early when organizer is undefined', () => {
      const event = { ...baseEvent, organizer: undefined }
      const result = updateAttendeesAfterTimeChange(event)
      expect(result.attendee).toBeDefined()
      expect(result.attendee).toHaveLength(2)
    })
  })

  describe('without provided attendees list', () => {
    it('should return attendees unchanged when timeChanged is false', () => {
      const result = updateAttendeesAfterTimeChange(baseEvent, false)
      expect(result.attendee?.[0].partstat).toBe('ACCEPTED')
      expect(result.attendee?.[0].rsvp).toBe('FALSE')
    })

    it('should mark non-organizer attendees as NEEDS-ACTION when timeChanged is true', () => {
      const result = updateAttendeesAfterTimeChange(baseEvent, true)
      const attendee = result.attendee?.find(
        a => a.cal_address === 'attendee@example.com'
      )
      expect(attendee?.partstat).toBe('NEEDS-ACTION')
      expect(attendee?.rsvp).toBe('TRUE')
    })

    it('should NOT mark the organizer as NEEDS-ACTION when timeChanged is true', () => {
      const result = updateAttendeesAfterTimeChange(baseEvent, true)
      const organizer = result.attendee?.find(
        a => a.cal_address === 'organizer@example.com'
      )
      expect(organizer?.partstat).toBe('ACCEPTED')
      expect(organizer?.rsvp).toBe('FALSE')
    })

    it('should return attendees unchanged when timeChanged is undefined', () => {
      const result = updateAttendeesAfterTimeChange(baseEvent)
      expect(result.attendee?.[0].partstat).toBe('ACCEPTED')
    })
  })

  describe('with provided attendees list', () => {
    const newAttendee: userAttendee = {
      cal_address: 'new@example.com',
      partstat: 'ACCEPTED',
      rsvp: 'FALSE',
      role: 'REQ-PARTICIPANT',
      cutype: 'INDIVIDUAL',
      cn: 'new'
    }

    it('should use existing attendee data when the address already exists in the event', () => {
      const result = updateAttendeesAfterTimeChange(baseEvent, false, [
        mockAttendee
      ])
      const attendee = result.attendee?.find(
        a => a.cal_address === 'attendee@example.com'
      )
      expect(attendee?.partstat).toBe('ACCEPTED')
    })

    it('should mark new attendee as NEEDS-ACTION when not found in existing list', () => {
      const result = updateAttendeesAfterTimeChange(baseEvent, false, [
        newAttendee
      ])
      const attendee = result.attendee?.find(
        a => a.cal_address === 'new@example.com'
      )
      expect(attendee?.partstat).toBe('NEEDS-ACTION')
      expect(attendee?.rsvp).toBe('TRUE')
    })

    it('should mark all attendees as NEEDS-ACTION when timeChanged is true', () => {
      const result = updateAttendeesAfterTimeChange(baseEvent, true, [
        mockAttendee,
        newAttendee
      ])
      const nonOrganizerAttendees = result.attendee?.filter(
        a => a.cal_address !== 'organizer@example.com'
      )
      nonOrganizerAttendees?.forEach(a => {
        expect(a.partstat).toBe('NEEDS-ACTION')
        expect(a.rsvp).toBe('TRUE')
      })
    })

    it('should append organizer entry at the end of the attendee list', () => {
      const result = updateAttendeesAfterTimeChange(baseEvent, false, [
        mockAttendee
      ])
      const last = result.attendee?.[result.attendee.length - 1]
      expect(last?.cal_address).toBe('organizer@example.com')
    })

    it('should use existing organizer attendee data when found in event attendees', () => {
      const result = updateAttendeesAfterTimeChange(baseEvent, false, [
        mockAttendee
      ])
      const organizer = result.attendee?.find(
        a => a.cal_address === 'organizer@example.com'
      )
      expect(organizer?.partstat).toBe('ACCEPTED')
    })

    it('should fall back to organizer defaults when organizer is not in existing attendees', () => {
      const eventWithoutOrganizerInAttendees: CalendarEvent = {
        ...baseEvent,
        attendee: [mockAttendee] // organizer not in attendee list
      }
      const result = updateAttendeesAfterTimeChange(
        eventWithoutOrganizerInAttendees,
        false,
        [mockAttendee]
      )
      const organizer = result.attendee?.find(
        a => a.cal_address === 'organizer@example.com'
      )
      expect(organizer?.role).toBe('CHAIR')
      expect(organizer?.partstat).toBe('NEEDS-ACTION')
    })
  })

  describe('organizer undefined', () => {
    const eventNoOrganizer: CalendarEvent = {
      ...baseEvent,
      organizer: undefined
    }

    it('should return attendees without bailing out', () => {
      const result = updateAttendeesAfterTimeChange(eventNoOrganizer)
      expect(result.attendee).toHaveLength(2)
    })

    it('should mark attendees as NEEDS-ACTION when timeChanged is true', () => {
      const result = updateAttendeesAfterTimeChange(eventNoOrganizer, true)
      result.attendee?.forEach(a => {
        expect(a.partstat).toBe('NEEDS-ACTION')
        expect(a.rsvp).toBe('TRUE')
      })
    })

    it('should not mutate attendees when timeChanged is false', () => {
      const result = updateAttendeesAfterTimeChange(eventNoOrganizer, false)
      expect(result.attendee?.[0].partstat).toBe('ACCEPTED')
      expect(result.attendee?.[0].rsvp).toBe('FALSE')
    })

    it('should not append an organizer entry when organizer is undefined and attendees list is provided', () => {
      const newAttendee: userAttendee = {
        cal_address: 'new@example.com',
        partstat: 'ACCEPTED',
        rsvp: 'FALSE',
        role: 'REQ-PARTICIPANT',
        cutype: 'INDIVIDUAL',
        cn: 'new'
      }
      const result = updateAttendeesAfterTimeChange(eventNoOrganizer, false, [
        newAttendee
      ])
      expect(result.attendee).toHaveLength(1)
      expect(result.attendee?.[0].cal_address).toBe('new@example.com')
    })
  })
})
