import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import ShareSheet from '@/components/ShareSheet.vue'
import { useImagesStore } from '@/stores/images'

const BaseBottomSheetStub = {
  name: 'BaseBottomSheet',
  template: '<div><slot /></div>',
  props: ['modelValue', 'label'],
  emits: ['update:modelValue'],
}

const BaseButtonStub = {
  template: '<button :disabled="disabled" @click="$emit(\'click\')"><slot /></button>',
  props: ['variant', 'disabled'],
  emits: ['click'],
}

describe('ShareSheet', () => {
  beforeEach(() => {
    vi.unstubAllGlobals()
    vi.restoreAllMocks()
    setActivePinia(createPinia())
  })

  function mountShareSheet(imageId = 1) {
    return mount(ShareSheet, {
      props: { modelValue: true, imageId },
      global: {
        stubs: {
          BaseBottomSheet: BaseBottomSheetStub,
          BaseButton: BaseButtonStub,
        },
      },
    })
  }

  // SS-01: successful share shows success message and clears email field
  it('shows success message and clears email on successful share', async () => {
    const store = useImagesStore()
    vi.spyOn(store, 'shareImage').mockResolvedValue(undefined)

    const wrapper = mountShareSheet()
    const input = wrapper.find('input[type="email"]')

    await input.setValue('friend@example.com')
    await wrapper.find('button').trigger('click')
    await flushPromises()

    expect(store.shareImage).toHaveBeenCalledWith(1, 'friend@example.com')
    expect(wrapper.text()).toContain('Invite sent to friend@example.com')
    expect((input.element as HTMLInputElement).value).toBe('')
  })

  // SS-02: failed share shows error message
  it('shows error message on failed share', async () => {
    const store = useImagesStore()
    vi.spyOn(store, 'shareImage').mockRejectedValue(new Error('Server error'))

    const wrapper = mountShareSheet()
    const input = wrapper.find('input[type="email"]')

    await input.setValue('friend@example.com')
    await wrapper.find('button').trigger('click')
    await flushPromises()

    expect(wrapper.text()).toContain('Server error')
    expect(wrapper.find('.share-sheet__error').exists()).toBe(true)
  })

  // SS-03: button is disabled when email input is empty
  it('button is disabled when email is empty', () => {
    const wrapper = mountShareSheet()
    const button = wrapper.find('button')
    expect(button.attributes('disabled')).toBeDefined()
  })

  // SS-04: Enter key on input triggers submit
  it('submits when Enter is pressed inside the email input', async () => {
    const store = useImagesStore()
    vi.spyOn(store, 'shareImage').mockResolvedValue(undefined)

    const wrapper = mountShareSheet()
    const input = wrapper.find('input[type="email"]')
    await input.setValue('friend@example.com')
    await input.trigger('keydown.enter')
    await flushPromises()
    expect(store.shareImage).toHaveBeenCalledWith(1, 'friend@example.com')
  })

  // SS-05: empty/whitespace email is a no-op even if submit() is called directly
  it('does not call shareImage when email is whitespace-only', async () => {
    const store = useImagesStore()
    const spy = vi.spyOn(store, 'shareImage').mockResolvedValue(undefined)
    const wrapper = mountShareSheet()
    const input = wrapper.find('input[type="email"]')
    await input.setValue('   ')
    await input.trigger('keydown.enter')
    await flushPromises()
    expect(spy).not.toHaveBeenCalled()
  })

  // SS-06: non-Error rejections fall back to a generic error message
  it('renders a generic error when the rejection is not an Error', async () => {
    const store = useImagesStore()
    vi.spyOn(store, 'shareImage').mockRejectedValue('something weird')
    const wrapper = mountShareSheet()
    const input = wrapper.find('input[type="email"]')
    await input.setValue('a@b')
    await wrapper.find('button').trigger('click')
    await flushPromises()
    expect(wrapper.text()).toContain('Failed to send')
  })

  // SS-07: forwards update:modelValue from the underlying sheet
  it('forwards update:modelValue from the wrapped sheet', async () => {
    const wrapper = mountShareSheet()
    await wrapper.findComponent({ name: 'BaseBottomSheet' }).vm.$emit('update:modelValue', false)
    expect(wrapper.emitted('update:modelValue')).toEqual([[false]])
  })
})
