import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import { h, nextTick, reactive } from 'vue'
import Form from '../src/canonical/Form.vue'
import FormItem from '../src/canonical/FormItem.vue'
import ButtonBridge from '../src/canonical/ButtonBridge.vue'

describe('Form — exposed methods + rendering', () => {
  it('validate() with no registered FormItem is valid (nothing to validate)', async () => {
    const wrapper = mount(Form, {
      props: { model: { email: '' }, rules: { email: { required: true } } },
    })
    const vm = wrapper.vm as unknown as {
      validate: () => Promise<{ valid: boolean; errors: Record<string, string> }>
    }
    const res = await vm.validate()
    expect(res.valid).toBe(true)
  })

  it('renders a .tvu-form wrapper and its default slot', () => {
    const wrapper = mount(Form, {
      props: { model: {} },
      slots: { default: '<span class="child">hi</span>' },
    })
    expect(wrapper.find('.tvu-form').exists()).toBe(true)
    expect(wrapper.find('.child').exists()).toBe(true)
  })

  it('applies --disabled modifier class when disabled', () => {
    const wrapper = mount(Form, { props: { model: {}, disabled: true } })
    expect(wrapper.find('.tvu-form--disabled').exists()).toBe(true)
  })
})

describe('Form + FormItem — SFC integration', () => {
  it('validate() surfaces a required error onto the FormItem', async () => {
    const wrapper = mount(Form, {
      props: { model: { email: '' }, rules: { email: { required: true, message: 'Email required' } } },
      slots: { default: () => h(FormItem, { prop: 'email', label: 'Email' }) },
    })
    const vm = wrapper.vm as unknown as { validate: () => Promise<{ valid: boolean }> }
    const res = await vm.validate()
    await nextTick()
    expect(res.valid).toBe(false)
    expect(wrapper.find('.form-item__message--error').text()).toContain('Email required')
    expect(wrapper.find('.form-item--error').exists()).toBe(true)
  })

  it('resetFields() clears the surfaced error', async () => {
    const wrapper = mount(Form, {
      props: { model: { email: '' }, rules: { email: { required: true } } },
      slots: { default: () => h(FormItem, { prop: 'email', label: 'Email' }) },
    })
    const vm = wrapper.vm as unknown as { validate: () => Promise<unknown>; resetFields: () => void }
    await vm.validate()
    await nextTick()
    expect(wrapper.find('.form-item--error').exists()).toBe(true)
    vm.resetFields()
    await nextTick()
    expect(wrapper.find('.form-item--error').exists()).toBe(false)
  })

  it('standalone FormItem (no Form) is unchanged — explicit error prop still wins', () => {
    const wrapper = mount(FormItem, { props: { label: 'X', error: 'boom', status: 'Error' } })
    expect(wrapper.find('.form-item__message--error').text()).toContain('boom')
  })
})

describe('Form + FormItem — disabled cascade (INFRA-F120)', () => {
  it('Form disabled=true cascades to FormItem\'s default-rendered field (InputBoxFilled)', () => {
    const wrapper = mount(Form, {
      props: { model: {}, disabled: true },
      slots: { default: () => h(FormItem, { label: 'Email', type: 'Label & Input' }) },
    })
    // InputBoxFilled maps the disabled axis to its own `enable: 'on'|'off'` prop,
    // which BaseInputBox renders as data-figma-enable (see InputBoxFilled.vue).
    expect(wrapper.find('[data-figma-enable="off"]').exists()).toBe(true)
  })

  it('Form disabled=false (default) leaves the default-rendered field enabled', () => {
    const wrapper = mount(Form, {
      props: { model: {} },
      slots: { default: () => h(FormItem, { label: 'Email', type: 'Label & Input' }) },
    })
    expect(wrapper.find('[data-figma-enable="off"]').exists()).toBe(false)
  })

  it('exposes disabled via the FormItem scoped slot for consumer-supplied field content', () => {
    const wrapper = mount(Form, {
      props: { model: {}, disabled: true },
      slots: {
        default: () =>
          h(FormItem, { label: 'Custom' }, { default: (slotProps: { disabled: boolean }) => h('span', { class: 'probe' }, String(slotProps.disabled)) }),
      },
    })
    expect(wrapper.find('.probe').text()).toBe('true')
  })

  it('standalone FormItem (no Form) defaults to not disabled', () => {
    const wrapper = mount(FormItem, { props: { label: 'X', type: 'Label & Input' } })
    expect(wrapper.find('[data-figma-enable="off"]').exists()).toBe(false)
  })

  it('ButtonBridge (e.g. a Submit button) inside a disabled Form gets a real disabled button', () => {
    const wrapper = mount(Form, {
      props: { model: {}, disabled: true },
      slots: { default: () => h(ButtonBridge, {}, () => 'Submit') },
    })
    expect(wrapper.find('button').attributes('disabled')).toBeDefined()
  })

  it('ButtonBridge outside a disabled Form is unaffected', () => {
    const wrapper = mount(ButtonBridge)
    expect(wrapper.find('button').attributes('disabled')).toBeUndefined()
  })
})

describe('Form — triggers', () => {
  it('change trigger: editing the model re-validates via the watch', async () => {
    const model = reactive({ email: 'a@b.com' })
    const wrapper = mount(Form, {
      props: { model, rules: { email: { required: true, type: 'email' as const, trigger: 'change' as const } } },
      slots: { default: () => h(FormItem, { prop: 'email', label: 'Email' }) },
    })
    model.email = 'bad' // change → watch → validateField('change') → email type fails
    await nextTick()
    await nextTick()
    expect(wrapper.find('.form-item--error').exists()).toBe(true)
  })

  it('clearValidate(prop) clears the surfaced error', async () => {
    const wrapper = mount(Form, {
      props: { model: { email: '' }, rules: { email: { required: true } } },
      slots: { default: () => h(FormItem, { prop: 'email', label: 'Email' }) },
    })
    const vm = wrapper.vm as unknown as { validate: () => Promise<unknown>; clearValidate: (p?: string) => void }
    await vm.validate(); await nextTick()
    expect(wrapper.find('.form-item--error').exists()).toBe(true)
    vm.clearValidate('email'); await nextTick()
    expect(wrapper.find('.form-item--error').exists()).toBe(false)
  })
})
