import { describe, it, expect, vi, beforeEach } from 'vitest'
import { shallowMount, type VueWrapper } from '@vue/test-utils'
import { nextTick } from 'vue'

import ModelTypeForm from '~/components/model-types/ModelTypeForm.vue'

// ---------------------------------------------------------------------------
// Mocks — modelUtils (structure functions)
// ---------------------------------------------------------------------------

vi.mock('~/shared/modelUtils', () => ({
  normalizeStructureForEditor: (s: any) => s ?? { customFields: [] },
  normalizeStructureForSave: (s: any) => s ?? { customFields: [] },
  normalizePieceStructureForSave: (s: any) => s ?? { customFields: [], products: [] },
  normalizeProductStructureForSave: (s: any) => s ?? { customFields: [], products: [] },
  formatStructurePreview: () => 'Component preview',
  formatPieceStructurePreview: () => 'Piece preview',
  formatProductStructurePreview: () => 'Product preview',
  defaultStructure: () => ({ customFields: [] }),
  defaultPieceStructure: () => ({ customFields: [], products: [] }),
  defaultProductStructure: () => ({ customFields: [], products: [] }),
  cloneStructure: (s: any) => JSON.parse(JSON.stringify(s ?? { customFields: [] })),
  clonePieceStructure: (s: any) => JSON.parse(JSON.stringify(s ?? { customFields: [], products: [] })),
  cloneProductStructure: (s: any) => JSON.parse(JSON.stringify(s ?? { customFields: [], products: [] })),
}))

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

const globalStubs = {
  ComponentModelStructureEditor: { template: '<div data-testid="comp-editor" />' },
  PieceModelStructureEditor: { template: '<div data-testid="piece-editor" />' },
}

function mountForm(props: Record<string, any> = {}) {
  return shallowMount(ModelTypeForm, {
    props: {
      mode: 'create',
      initialCategory: 'COMPONENT',
      lockCategory: true,
      ...props,
    },
    global: {
      stubs: globalStubs,
    },
  })
}

function getNameInput(wrapper: VueWrapper) {
  return wrapper.find('input[name="name"]')
}

function getCategorySelect(wrapper: VueWrapper) {
  return wrapper.find('select[name="category"]')
}

function getNotesTextarea(wrapper: VueWrapper) {
  return wrapper.find('textarea[name="notes"]')
}

function getSubmitButton(wrapper: VueWrapper) {
  return wrapper.find('button[type="submit"]')
}

function getCancelButton(wrapper: VueWrapper) {
  return wrapper.find('button[type="button"]')
}

async function submitForm(wrapper: VueWrapper) {
  await wrapper.find('form').trigger('submit.prevent')
  await nextTick()
}

beforeEach(() => {
  vi.clearAllMocks()
})

// ---------------------------------------------------------------------------
// Rendering
// ---------------------------------------------------------------------------
describe('rendering', () => {
  it('renders form with name, category, and notes fields', () => {
    const wrapper = mountForm()
    expect(getNameInput(wrapper).exists()).toBe(true)
    expect(getCategorySelect(wrapper).exists()).toBe(true)
    expect(getNotesTextarea(wrapper).exists()).toBe(true)
  })

  it('shows "Créer" button in create mode', () => {
    const wrapper = mountForm({ mode: 'create' })
    expect(getSubmitButton(wrapper).text()).toBe('Créer')
  })

  it('shows "Enregistrer" button in edit mode', () => {
    const wrapper = mountForm({ mode: 'edit' })
    expect(getSubmitButton(wrapper).text()).toBe('Enregistrer')
  })

  it('populates form from initialData in edit mode', async () => {
    const wrapper = mountForm({
      mode: 'edit',
      initialData: {
        name: 'Existing Type',
        code: 'existing-type',
        category: 'COMPONENT',
        notes: 'Some notes',
      },
    })
    await nextTick()

    expect((getNameInput(wrapper).element as HTMLInputElement).value).toBe('Existing Type')
    expect((getNotesTextarea(wrapper).element as HTMLTextAreaElement).value).toBe('Some notes')
  })
})

// ---------------------------------------------------------------------------
// Validation
// ---------------------------------------------------------------------------
describe('validation', () => {
  it('shows error for name shorter than 2 characters', async () => {
    const wrapper = mountForm()
    await getNameInput(wrapper).setValue('A')
    await submitForm(wrapper)

    expect(wrapper.text()).toContain('au moins 2 caractères')
    expect(wrapper.emitted('submit')).toBeUndefined()
  })

  it('shows error for name longer than 120 characters', async () => {
    const wrapper = mountForm()
    await getNameInput(wrapper).setValue('A'.repeat(121))
    await submitForm(wrapper)

    expect(wrapper.text()).toContain('ne peut pas dépasser 120')
    expect(wrapper.emitted('submit')).toBeUndefined()
  })

  it('submits with valid name', async () => {
    const wrapper = mountForm()
    await getNameInput(wrapper).setValue('Mon Type')
    await submitForm(wrapper)

    expect(wrapper.emitted('submit')).toBeTruthy()
    expect(wrapper.emitted('submit')![0]).toBeTruthy()
  })

  it('does not show error with valid name', async () => {
    const wrapper = mountForm()
    await getNameInput(wrapper).setValue('Valid Name')
    await submitForm(wrapper)

    expect(wrapper.text()).not.toContain('au moins 2')
    expect(wrapper.text()).not.toContain('ne peut pas dépasser')
  })
})

// ---------------------------------------------------------------------------
// Code generation
// ---------------------------------------------------------------------------
describe('code generation', () => {
  it('auto-generates code from name in create mode', async () => {
    const wrapper = mountForm({ mode: 'create' })
    await getNameInput(wrapper).setValue('Ma Catégorie')
    await nextTick()
    await submitForm(wrapper)

    const payload = (wrapper.emitted('submit')![0] as any[])[0]
    expect(payload.code).toBe('ma-categorie')
  })

  it('preserves initialData code in edit mode', async () => {
    const wrapper = mountForm({
      mode: 'edit',
      initialData: {
        name: 'Type',
        code: 'custom-code',
        category: 'COMPONENT',
      },
    })
    await nextTick()
    await submitForm(wrapper)

    const payload = (wrapper.emitted('submit')![0] as any[])[0]
    expect(payload.code).toBe('custom-code')
  })
})

// ---------------------------------------------------------------------------
// Category-based structure editor rendering
// ---------------------------------------------------------------------------
describe('structure editor rendering', () => {
  it('renders ComponentModelStructureEditor for COMPONENT', () => {
    const wrapper = mountForm({ initialCategory: 'COMPONENT' })
    expect(wrapper.find('[data-testid="comp-editor"]').exists()).toBe(true)
    expect(wrapper.find('[data-testid="piece-editor"]').exists()).toBe(false)
  })

  it('renders PieceModelStructureEditor for PIECE', () => {
    const wrapper = mountForm({ initialCategory: 'PIECE' })
    expect(wrapper.find('[data-testid="piece-editor"]').exists()).toBe(true)
    expect(wrapper.find('[data-testid="comp-editor"]').exists()).toBe(false)
  })

  it('renders PieceModelStructureEditor for PRODUCT', () => {
    const wrapper = mountForm({ initialCategory: 'PRODUCT' })
    // PRODUCT reuses PieceModelStructureEditor
    expect(wrapper.find('[data-testid="piece-editor"]').exists()).toBe(true)
    expect(wrapper.find('[data-testid="comp-editor"]').exists()).toBe(false)
  })
})

// ---------------------------------------------------------------------------
// Category lock
// ---------------------------------------------------------------------------
describe('category lock', () => {
  it('disables category select when lockCategory is true', () => {
    const wrapper = mountForm({ lockCategory: true })
    expect((getCategorySelect(wrapper).element as HTMLSelectElement).disabled).toBe(true)
  })

  it('enables category select when lockCategory is false', () => {
    const wrapper = mountForm({ lockCategory: false })
    expect((getCategorySelect(wrapper).element as HTMLSelectElement).disabled).toBe(false)
  })
})

// ---------------------------------------------------------------------------
// Saving state
// ---------------------------------------------------------------------------
describe('saving state', () => {
  it('disables submit button when saving', () => {
    const wrapper = mountForm({ saving: true })
    expect((getSubmitButton(wrapper).element as HTMLButtonElement).disabled).toBe(true)
  })

  it('disables cancel button when saving', () => {
    const wrapper = mountForm({ saving: true })
    expect((getCancelButton(wrapper).element as HTMLButtonElement).disabled).toBe(true)
  })

  it('shows spinner when saving', () => {
    const wrapper = mountForm({ saving: true })
    expect(wrapper.find('.loading-spinner').exists()).toBe(true)
  })
})

// ---------------------------------------------------------------------------
// Cancel
// ---------------------------------------------------------------------------
describe('cancel', () => {
  it('emits cancel on cancel button click', async () => {
    const wrapper = mountForm()
    await getCancelButton(wrapper).trigger('click')
    expect(wrapper.emitted('cancel')).toBeTruthy()
  })
})

// ---------------------------------------------------------------------------
// Submit payload
// ---------------------------------------------------------------------------
describe('submit payload', () => {
  it('emits payload with COMPONENT category and structure', async () => {
    const wrapper = mountForm({ initialCategory: 'COMPONENT' })
    await getNameInput(wrapper).setValue('Test Component')
    await submitForm(wrapper)

    const payload = (wrapper.emitted('submit')![0] as any[])[0]
    expect(payload.category).toBe('COMPONENT')
    expect(payload.name).toBe('Test Component')
    expect(payload.structure).toBeDefined()
  })

  it('emits payload with PIECE category', async () => {
    const wrapper = mountForm({ initialCategory: 'PIECE' })
    await getNameInput(wrapper).setValue('Test Piece')
    await submitForm(wrapper)

    const payload = (wrapper.emitted('submit')![0] as any[])[0]
    expect(payload.category).toBe('PIECE')
  })

  it('emits payload with PRODUCT category', async () => {
    const wrapper = mountForm({ initialCategory: 'PRODUCT' })
    await getNameInput(wrapper).setValue('Test Product')
    await submitForm(wrapper)

    const payload = (wrapper.emitted('submit')![0] as any[])[0]
    expect(payload.category).toBe('PRODUCT')
  })

  it('includes trimmed notes in payload', async () => {
    const wrapper = mountForm()
    await getNameInput(wrapper).setValue('Test')
    await getNotesTextarea(wrapper).setValue('  Some notes  ')
    await submitForm(wrapper)

    const payload = (wrapper.emitted('submit')![0] as any[])[0]
    expect(payload.notes).toBe('Some notes')
  })

  it('omits empty notes from payload', async () => {
    const wrapper = mountForm()
    await getNameInput(wrapper).setValue('Test')
    await getNotesTextarea(wrapper).setValue('')
    await submitForm(wrapper)

    const payload = (wrapper.emitted('submit')![0] as any[])[0]
    expect(payload.notes).toBeUndefined()
  })
})
