import { describe, it, expect, vi } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { createRouter, createMemoryHistory } from 'vue-router'
import EventCreateView from '../EventCreateView.vue'
import { api } from '@/api/client'
import type { StoredEvent } from '@/composables/useEventStorage'
vi.mock('@/api/client', () => ({
  api: {
    POST: vi.fn<typeof api.POST>(),
  },
}))

vi.mock('@/composables/useEventStorage', () => ({
  useEventStorage: vi.fn<() => unknown>(() => ({
    saveCreatedEvent: vi.fn<() => void>(),
    getStoredEvents: vi.fn<() => unknown[]>(() => []),
    getOrganizerToken: vi.fn<() => string | undefined>(),
    saveRsvp: vi.fn<() => void>(),
    getRsvp: vi.fn<() => unknown>(),
  })),
}))

function createTestRouter() {
  return createRouter({
    history: createMemoryHistory(),
    routes: [
      { path: '/', name: 'home', component: { template: '<div />' } },
      { path: '/create', name: 'create-event', component: EventCreateView },
      { path: '/events/:eventToken', name: 'event', component: { template: '<div />' } },
    ],
  })
}

describe('EventCreateView', () => {
  it('renders all form fields', async () => {
    const router = createTestRouter()
    await router.push('/create')
    await router.isReady()

    const wrapper = mount(EventCreateView, {
      global: { plugins: [router] },
    })

    expect(wrapper.find('#title').exists()).toBe(true)
    expect(wrapper.find('#description').exists()).toBe(true)
    expect(wrapper.find('#dateTime').exists()).toBe(true)
    expect(wrapper.find('#location').exists()).toBe(true)
  })

  it('has required attribute on required fields', async () => {
    const router = createTestRouter()
    await router.push('/create')
    await router.isReady()

    const wrapper = mount(EventCreateView, {
      global: { plugins: [router] },
    })

    expect(wrapper.find('#title').attributes('required')).toBeDefined()
    expect(wrapper.find('#dateTime').attributes('required')).toBeDefined()
  })

  it('does not have required attribute on optional fields', async () => {
    const router = createTestRouter()
    await router.push('/create')
    await router.isReady()

    const wrapper = mount(EventCreateView, {
      global: { plugins: [router] },
    })

    expect(wrapper.find('#description').attributes('required')).toBeUndefined()
    expect(wrapper.find('#location').attributes('required')).toBeUndefined()
  })

  it('has a submit button', async () => {
    const router = createTestRouter()
    await router.push('/create')
    await router.isReady()

    const wrapper = mount(EventCreateView, {
      global: { plugins: [router] },
    })

    const button = wrapper.find('button[type="submit"]')
    expect(button.exists()).toBe(true)
    expect(button.text()).toBe('Create Event')
  })

  it('shows server error when network request fails', async () => {
    vi.mocked(api.POST).mockRejectedValueOnce(new TypeError('Failed to fetch'))

    const router = createTestRouter()
    await router.push('/create')
    await router.isReady()

    const wrapper = mount(EventCreateView, {
      global: { plugins: [router] },
    })

    // Fill required fields
    await wrapper.find('#title').setValue('My Event')
    await wrapper.find('#dateTime').setValue('2026-12-25T18:00')

    await wrapper.find('form').trigger('submit')
    await flushPromises()

    const alerts = wrapper.findAll('[role="alert"]').map((el) => el.text()).filter((t) => t.length > 0)
    expect(alerts).toContain('Could not reach the server. Please try again.')

    // Submit button should not remain disabled
    expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBeUndefined()
  })

  it('clears field error when user types into that field', async () => {
    const router = createTestRouter()
    await router.push('/create')
    await router.isReady()

    const wrapper = mount(EventCreateView, {
      global: { plugins: [router] },
    })

    // Submit empty form to trigger validation errors
    await wrapper.find('form').trigger('submit')

    const errorsBefore = wrapper.findAll('[role="alert"]').map((el) => el.text()).filter((t) => t.length > 0)
    expect(errorsBefore.length).toBeGreaterThanOrEqual(2)

    // Type into title field
    await wrapper.find('#title').setValue('My Event')

    // Title error should be cleared (span removed from DOM), but other errors should remain
    const titleError = wrapper.find('#title').element.closest('.form-group')!.querySelector('[role="alert"]')
    expect(titleError).toBeNull()

    const dateTimeError = wrapper.find('#dateTime').element.closest('.form-group')!.querySelector('[role="alert"]')!
    expect(dateTimeError.textContent).not.toBe('')
  })

  it('shows validation errors when submitting empty form', async () => {
    const router = createTestRouter()
    await router.push('/create')
    await router.isReady()

    const wrapper = mount(EventCreateView, {
      global: { plugins: [router] },
    })

    await wrapper.find('form').trigger('submit')

    const errorElements = wrapper.findAll('[role="alert"]')
    const errorTexts = errorElements.map((el) => el.text()).filter((t) => t.length > 0)
    expect(errorTexts.length).toBeGreaterThanOrEqual(2)
  })

  it('submits successfully, saves to storage, and navigates to event page', async () => {
    const mockSave = vi.fn<(...args: unknown[]) => void>()
    vi.mocked(vi.importActual<typeof import('@/composables/useEventStorage')>)
    const { useEventStorage } = await import('@/composables/useEventStorage')
    vi.mocked(useEventStorage).mockReturnValue({
      saveCreatedEvent: mockSave,
      getStoredEvents: vi.fn<() => StoredEvent[]>(() => []),
      getOrganizerToken: vi.fn<() => string | undefined>(),
      saveRsvp: vi.fn<() => void>(),
      getRsvp: vi.fn<() => { rsvpToken: string; rsvpName: string } | undefined>(),
      removeRsvp: vi.fn<() => void>(),
      saveWatch: vi.fn<() => void>(),
      isStored: vi.fn<() => boolean>(() => false),
      removeEvent: vi.fn<() => void>(),
    })

    vi.mocked(api.POST).mockResolvedValueOnce({
      data: {
        eventToken: 'abc-123',
        organizerToken: 'org-456',
        title: 'Birthday Party',
        dateTime: '2026-12-25T18:00:00+01:00',
        timezone: 'Europe/Berlin',
      },
      error: undefined,
      response: new Response(),
    })

    const router = createTestRouter()
    const pushSpy = vi.spyOn(router, 'push')
    await router.push('/create')
    await router.isReady()

    const wrapper = mount(EventCreateView, {
      global: { plugins: [router] },
    })

    await wrapper.find('#title').setValue('Birthday Party')
    await wrapper.find('#description').setValue('Come celebrate!')
    await wrapper.find('#dateTime').setValue('2026-12-25T18:00')
    await wrapper.find('#location').setValue('Berlin')

    await wrapper.find('form').trigger('submit')
    await flushPromises()

    expect(vi.mocked(api.POST)).toHaveBeenCalledWith('/events', {
      body: expect.objectContaining({
        title: 'Birthday Party',
        description: 'Come celebrate!',
        location: 'Berlin',
      }),
    })

    expect(mockSave).toHaveBeenCalledWith({
      eventToken: 'abc-123',
      organizerToken: 'org-456',
      title: 'Birthday Party',
      dateTime: '2026-12-25T18:00:00+01:00',
    })

    expect(pushSpy).toHaveBeenCalledWith({
      name: 'event',
      params: { eventToken: 'abc-123' },
    })
  })

  it('displays server-side field errors on the correct fields', async () => {
    vi.mocked(api.POST).mockResolvedValueOnce({
      data: undefined,
      error: {
        fieldErrors: [{ field: 'title', message: 'Title already taken' }],
      },
      response: new Response(),
    } as ReturnType<typeof api.POST> extends Promise<infer R> ? R : never)

    const router = createTestRouter()
    await router.push('/create')
    await router.isReady()

    const wrapper = mount(EventCreateView, {
      global: { plugins: [router] },
    })

    await wrapper.find('#title').setValue('Duplicate Event')
    await wrapper.find('#dateTime').setValue('2026-12-25T18:00')

    await wrapper.find('form').trigger('submit')
    await flushPromises()

    const titleError = wrapper.find('#title-error')
    expect(titleError.exists()).toBe(true)
    expect(titleError.text()).toBe('Title already taken')

    // Other field errors should not be present
    expect(wrapper.find('#dateTime-error').exists()).toBe(false)
  })
})
