import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'

import DisplayNameEditor from './DisplayNameEditor.vue'

describe('DisplayNameEditor', () => {
  describe('view mode', () => {
    it('displays the current name', () => {
      /**
       * Test that the display name is shown in view mode.
       *
       * By default, the component shows the current name
       * with an edit button, not an input field.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: false,
        },
      })

      expect(wrapper.text()).toContain('Test User')
    })

    it('shows edit button', () => {
      /**
       * Test that an edit button is visible in view mode.
       *
       * Users need a way to enter edit mode to change their name.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: false,
        },
      })

      const editButton = wrapper.find('button')
      expect(editButton.exists()).toBe(true)
    })

    it('enters edit mode when edit button is clicked', async () => {
      /**
       * Test entering edit mode.
       *
       * Clicking the edit button should switch from viewing
       * to editing the display name.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: false,
        },
      })

      await wrapper.find('button').trigger('click')

      // Should now show an input field
      expect(wrapper.find('input').exists()).toBe(true)
    })
  })

  describe('edit mode', () => {
    it('shows input with current value', async () => {
      /**
       * Test that input is pre-filled with current name.
       *
       * When entering edit mode, the input should contain
       * the current display name for easy editing.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: false,
        },
      })

      // Enter edit mode
      await wrapper.find('button').trigger('click')

      const input = wrapper.find('input')
      expect((input.element as HTMLInputElement).value).toBe('Test User')
    })

    it('shows save and cancel buttons', async () => {
      /**
       * Test that save and cancel buttons are visible.
       *
       * Users need clear actions to either save changes
       * or cancel and revert to the original value.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: false,
        },
      })

      await wrapper.find('button').trigger('click')

      const buttons = wrapper.findAll('button')
      const buttonTexts = buttons.map(b => b.text())

      expect(buttonTexts.some(t => t.includes('Save'))).toBe(true)
      expect(buttonTexts.some(t => t.includes('Cancel'))).toBe(true)
    })

    it('shows character count', async () => {
      /**
       * Test that character count is displayed.
       *
       * Users should know how many characters they've used
       * relative to the maximum allowed.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: false,
        },
      })

      await wrapper.find('button').trigger('click')

      expect(wrapper.text()).toContain('/32')
    })

    it('cancels and reverts on cancel button click', async () => {
      /**
       * Test cancel functionality.
       *
       * Clicking cancel should exit edit mode and discard
       * any changes made to the input.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: false,
        },
      })

      // Enter edit mode
      await wrapper.find('button').trigger('click')

      // Change the value
      await wrapper.find('input').setValue('New Name')

      // Click cancel
      const cancelButton = wrapper.findAll('button').find(b => b.text().includes('Cancel'))
      await cancelButton!.trigger('click')

      // Should be back in view mode with original name
      expect(wrapper.find('input').exists()).toBe(false)
      expect(wrapper.text()).toContain('Test User')
    })

    it('cancels on Escape key', async () => {
      /**
       * Test keyboard shortcut for cancel.
       *
       * Pressing Escape should cancel editing as a
       * common keyboard shortcut.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: false,
        },
      })

      await wrapper.find('button').trigger('click')
      await wrapper.find('input').trigger('keydown', { key: 'Escape' })

      expect(wrapper.find('input').exists()).toBe(false)
    })
  })

  describe('validation', () => {
    it('shows error for empty name', async () => {
      /**
       * Test validation of empty display name.
       *
       * Empty or whitespace-only names should be rejected
       * with a clear error message.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: false,
        },
      })

      await wrapper.find('button').trigger('click')
      await wrapper.find('input').setValue('')

      // Click save
      const saveButton = wrapper.findAll('button').find(b => b.text().includes('Save'))
      await saveButton!.trigger('click')

      expect(wrapper.text()).toContain('cannot be empty')
    })

    it('shows error for name shorter than 2 characters', async () => {
      /**
       * Test minimum length validation.
       *
       * Very short names are likely typos and should be rejected.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: false,
        },
      })

      await wrapper.find('button').trigger('click')
      await wrapper.find('input').setValue('A')

      const saveButton = wrapper.findAll('button').find(b => b.text().includes('Save'))
      await saveButton!.trigger('click')

      expect(wrapper.text()).toContain('at least 2 characters')
    })

    it('shows error for name longer than 32 characters', async () => {
      /**
       * Test maximum length validation.
       *
       * Names that are too long should be rejected to prevent
       * UI issues and database constraints.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: false,
        },
      })

      await wrapper.find('button').trigger('click')
      await wrapper.find('input').setValue('A'.repeat(33))

      const saveButton = wrapper.findAll('button').find(b => b.text().includes('Save'))
      await saveButton!.trigger('click')

      expect(wrapper.text()).toContain('cannot exceed 32')
    })

    it('does not emit save with validation errors', async () => {
      /**
       * Test that invalid input does not trigger save.
       *
       * The component should validate locally before emitting
       * the save event to avoid unnecessary API calls.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: false,
        },
      })

      await wrapper.find('button').trigger('click')
      await wrapper.find('input').setValue('')

      const saveButton = wrapper.findAll('button').find(b => b.text().includes('Save'))
      await saveButton!.trigger('click')

      expect(wrapper.emitted('save')).toBeFalsy()
    })
  })

  describe('saving', () => {
    it('emits save event with trimmed value', async () => {
      /**
       * Test save event emission.
       *
       * When the user saves, the component should emit the
       * trimmed display name for the parent to handle.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: false,
        },
      })

      await wrapper.find('button').trigger('click')
      await wrapper.find('input').setValue('  New Name  ')

      const saveButton = wrapper.findAll('button').find(b => b.text().includes('Save'))
      await saveButton!.trigger('click')

      expect(wrapper.emitted('save')).toBeTruthy()
      expect(wrapper.emitted('save')![0]).toEqual(['New Name'])
    })

    it('saves on Enter key', async () => {
      /**
       * Test keyboard shortcut for save.
       *
       * Pressing Enter should save the changes as a
       * common keyboard shortcut.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: false,
        },
      })

      await wrapper.find('button').trigger('click')
      await wrapper.find('input').setValue('New Name')
      await wrapper.find('input').trigger('keydown', { key: 'Enter' })

      expect(wrapper.emitted('save')).toBeTruthy()
    })

    it('disables buttons while saving', async () => {
      /**
       * Test button states during save.
       *
       * While saving, buttons should be disabled to prevent
       * duplicate submissions.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: true,
        },
      })

      await wrapper.find('button').trigger('click')

      const buttons = wrapper.findAll('button')
      buttons.forEach(button => {
        if (button.text().includes('Save') || button.text().includes('Cancel')) {
          expect(button.attributes('disabled')).toBeDefined()
        }
      })
    })

    it('shows "Saving..." text while saving', async () => {
      /**
       * Test loading state display.
       *
       * Users should see feedback that their save is in progress.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: true,
        },
      })

      await wrapper.find('button').trigger('click')

      expect(wrapper.text()).toContain('Saving')
    })

    it('exits edit mode when modelValue matches saved value', async () => {
      /**
       * Test automatic exit from edit mode on success.
       *
       * When the parent updates the modelValue to match the
       * saved value, edit mode should close automatically.
       */
      const wrapper = mount(DisplayNameEditor, {
        props: {
          modelValue: 'Test User',
          isSaving: false,
        },
      })

      // Enter edit mode and save
      await wrapper.find('button').trigger('click')
      await wrapper.find('input').setValue('New Name')

      const saveButton = wrapper.findAll('button').find(b => b.text().includes('Save'))
      await saveButton!.trigger('click')

      // Simulate parent updating the value
      await wrapper.setProps({ modelValue: 'New Name' })

      // Should exit edit mode
      expect(wrapper.find('input').exists()).toBe(false)
    })
  })
})
