import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import StickerTray from '@/components/StickerTray.vue'
import { CUSTOM_STICKER_ASSETS } from '@/assets/stickers/customAssets'

vi.mock('@/components/BaseBottomSheet.vue', () => ({
  default: {
    name: 'BaseBottomSheet',
    template: '<div class="bottom-sheet-stub"><slot /></div>',
    props: ['modelValue', 'label'],
    emits: ['update:modelValue'],
  },
}))

beforeEach(() => {
  localStorage.clear()
})

describe('StickerTray (emoji-keyboard picker)', () => {
  it('renders the custom-sticker row', async () => {
    const wrapper = mount(StickerTray, { props: { modelValue: true } })
    await flushPromises()
    const chips = wrapper.findAll('.sticker-tray__chip')
    expect(chips.length).toBeGreaterThanOrEqual(CUSTOM_STICKER_ASSETS.length)
  })

  it('emits pick with imageAsset when a custom sticker is tapped', async () => {
    const wrapper = mount(StickerTray, { props: { modelValue: true } })
    await flushPromises()
    const santa = wrapper.findAll('.sticker-tray__chip').find(c => c.attributes('aria-label') === 'Santa hat')!
    expect(santa).toBeTruthy()
    await santa.trigger('click')
    const events = wrapper.emitted('pick')
    expect(events).toBeTruthy()
    expect(events![0][0]).toEqual({ imageAsset: 'santa-hat' })
  })

  it('emits pick with the typed emoji when the input fires', async () => {
    const wrapper = mount(StickerTray, { props: { modelValue: true } })
    await flushPromises()
    const input = wrapper.find('.sticker-tray__emoji-input')
    expect(input.exists()).toBe(true)
    ;(input.element as HTMLInputElement).value = '🎉'
    await input.trigger('input')
    const events = wrapper.emitted('pick')
    expect(events).toBeTruthy()
    expect(events![0][0]).toEqual({ emoji: '🎉' })
  })

  it('clears the input after each emoji pick so the next pick is independent', async () => {
    const wrapper = mount(StickerTray, { props: { modelValue: true } })
    await flushPromises()
    const input = wrapper.find('.sticker-tray__emoji-input').element as HTMLInputElement
    input.value = '😀'
    await wrapper.find('.sticker-tray__emoji-input').trigger('input')
    expect(input.value).toBe('')
  })

  it('persists picks to a Recent row across mounts', async () => {
    const first = mount(StickerTray, { props: { modelValue: true } })
    await flushPromises()
    const input = first.find('.sticker-tray__emoji-input')
    ;(input.element as HTMLInputElement).value = '🌈'
    await input.trigger('input')

    const second = mount(StickerTray, { props: { modelValue: true } })
    await flushPromises()
    const headings = second.findAll('.sticker-tray__heading').map(h => h.text())
    expect(headings).toContain('Recent')
    const recentChip = second.findAll('.sticker-tray__chip').find(c => c.attributes('aria-label') === '🌈')
    expect(recentChip).toBeTruthy()
  })

  it('forwards update:modelValue from the wrapped sheet', async () => {
    const wrapper = mount(StickerTray, { props: { modelValue: true } })
    await wrapper.findComponent({ name: 'BaseBottomSheet' }).vm.$emit('update:modelValue', false)
    expect(wrapper.emitted('update:modelValue')).toEqual([[false]])
  })

  it('re-emits pick from a recent emoji chip without re-typing it', async () => {
    // First mount: seed a recent emoji via the input.
    const first = mount(StickerTray, { props: { modelValue: true } })
    await flushPromises()
    const input = first.find('.sticker-tray__emoji-input')
    ;(input.element as HTMLInputElement).value = '🦄'
    await input.trigger('input')

    // Second mount picks up the persisted recent — clicking the chip should
    // re-emit pick with the original emoji (covers pickRecent's emoji branch).
    const second = mount(StickerTray, { props: { modelValue: true } })
    await flushPromises()
    const recentChip = second.findAll('.sticker-tray__chip').find(c => c.attributes('aria-label') === '🦄')!
    expect(recentChip).toBeTruthy()
    await recentChip.trigger('click')
    const events = second.emitted('pick')
    expect(events).toBeTruthy()
    expect(events![0][0]).toEqual({ emoji: '🦄' })
  })

  it('re-emits pick from a recent custom-sticker chip with the original imageAsset', async () => {
    // Seed by picking a custom sticker, then remount and click its recent chip.
    const first = mount(StickerTray, { props: { modelValue: true } })
    await flushPromises()
    const santa = first.findAll('.sticker-tray__chip').find(c => c.attributes('aria-label') === 'Santa hat')!
    await santa.trigger('click')

    const second = mount(StickerTray, { props: { modelValue: true } })
    await flushPromises()
    // The first chip in the Recent row should be the just-picked Santa hat.
    const recentSection = second.findAll('.sticker-tray__section')[0]
    const recentChip = recentSection.findAll('.sticker-tray__chip').find(c => c.attributes('aria-label') === 'Santa hat')!
    expect(recentChip).toBeTruthy()
    await recentChip.trigger('click')
    const events = second.emitted('pick')
    expect(events).toBeTruthy()
    // First emit on the second mount should be the recent re-pick.
    expect(events![0][0]).toEqual({ imageAsset: 'santa-hat' })
  })

  it('treats malformed stored recents as empty (JSON.parse throws)', async () => {
    // The catch branch in loadRecents — protects against a corrupted entry
    // wedging the tray on subsequent loads.
    localStorage.setItem('pf.stickerTray.recents', '{not valid json')
    const wrapper = mount(StickerTray, { props: { modelValue: true } })
    await flushPromises()
    // No Recent heading should render when recents resolved to [].
    const headings = wrapper.findAll('.sticker-tray__heading').map(h => h.text())
    expect(headings).not.toContain('Recent')
  })

  it('drops persisted image recents whose asset id is no longer known', async () => {
    // Simulates an old build's recent referring to a sticker that has since
    // been removed; the filter in loadRecents should strip it so we don't
    // render a broken <img>.
    localStorage.setItem('pf.stickerTray.recents', JSON.stringify([
      { key: 'image:long-gone', kind: 'image', label: 'Gone', imageAsset: 'long-gone' },
      { key: 'image:santa-hat', kind: 'image', label: 'Santa hat', imageAsset: 'santa-hat' },
    ]))
    const wrapper = mount(StickerTray, { props: { modelValue: true } })
    await flushPromises()
    // Only the still-valid recent should appear in the Recent section.
    const recentSection = wrapper.findAll('.sticker-tray__section')[0]
    const recentChips = recentSection.findAll('.sticker-tray__chip')
    expect(recentChips.length).toBe(1)
    expect(recentChips[0].attributes('aria-label')).toBe('Santa hat')
  })

  it('uses the [...input].pop() fallback when Intl.Segmenter is unavailable', async () => {
    // Older Safari / very old Android browsers don't have Intl.Segmenter;
    // the spread+pop fallback still has to extract the right grapheme.
    const original = (Intl as any).Segmenter
    // Drop Segmenter from the Intl namespace for this test.
    delete (Intl as any).Segmenter
    try {
      const wrapper = mount(StickerTray, { props: { modelValue: true } })
      await flushPromises()
      const input = wrapper.find('.sticker-tray__emoji-input')
      ;(input.element as HTMLInputElement).value = '🌟'
      await input.trigger('input')
      const events = wrapper.emitted('pick')
      expect(events).toBeTruthy()
      expect(events![0][0]).toEqual({ emoji: '🌟' })
    } finally {
      ;(Intl as any).Segmenter = original
    }
  })

  it('ignores an empty input event (defensive — no grapheme, no pick)', async () => {
    // firstGrapheme returns null for an empty string; onEmojiInput should
    // bail out before emitting. Without this, every keystroke that clears
    // the field would emit a noisy pick.
    const wrapper = mount(StickerTray, { props: { modelValue: true } })
    await flushPromises()
    const input = wrapper.find('.sticker-tray__emoji-input')
    ;(input.element as HTMLInputElement).value = ''
    await input.trigger('input')
    expect(wrapper.emitted('pick')).toBeUndefined()
  })
})
