import { useState } from 'react'
import { describe, expect, it, vi } from 'vitest'
import { fireEvent, render, screen } from '@testing-library/react'
import type { AnyFieldDefinition, FieldDefinition, RuntimeFieldValueMap } from '@platform-modules/fields'
import { CompleteFieldValuesEditor } from './complete-editor.js'

const def = <T extends AnyFieldDefinition>(value: T): T => value

function CompleteHost({ fields, initial = {}, createRowId, resolveCloneFields }: { fields: readonly AnyFieldDefinition[]; initial?: RuntimeFieldValueMap; createRowId?: () => string; resolveCloneFields?: (field: FieldDefinition<'clone'>) => readonly AnyFieldDefinition[] }) {
  const [value, setValue] = useState<RuntimeFieldValueMap>(initial)
  return <>
    <CompleteFieldValuesEditor fields={fields} value={value} onChange={setValue} adapters={{ ...(createRowId ? { createRowId } : {}), ...(resolveCloneFields ? { resolveCloneFields } : {}) }} />
    <output data-testid="value">{JSON.stringify(value)}</output>
  </>
}

const text = def({ key:'title', name:'title', label:'Title', type:'text', settings:{} })

describe('CompleteFieldValuesEditor', () => {
  it('renders all built-in families without an unsupported-type fallback', () => {
    const fields = [
      text,
      def({ key:'textarea',name:'textarea',label:'Textarea',type:'textarea',settings:{newline:'preserve'} }),
      def({ key:'number',name:'number',label:'Number',type:'number',settings:{} }),
      def({ key:'range',name:'range',label:'Range',type:'range',settings:{min:0,max:10} }),
      def({ key:'email',name:'email',label:'Email',type:'email',settings:{} }),
      def({ key:'url',name:'url',label:'URL',type:'url',settings:{schemes:['https']} }),
      def({ key:'password',name:'password',label:'Password',type:'password',settings:{hasherKey:'test'} }),
      def({ key:'boolean',name:'boolean',label:'Boolean',type:'boolean',settings:{} }),
      def({ key:'select',name:'select',label:'Select',type:'select',settings:{choices:{a:'A'}} }),
      def({ key:'checkbox',name:'checkbox',label:'Checkbox',type:'checkbox',settings:{choices:{a:'A'}} }),
      def({ key:'radio',name:'radio',label:'Radio',type:'radio',settings:{choices:{a:'A'}} }),
      def({ key:'button_group',name:'button_group',label:'Button group',type:'buttonGroup',settings:{choices:{a:'A'}} }),
      def({ key:'rich_text',name:'rich_text',label:'Rich text',type:'richText',settings:{} }),
      def({ key:'oembed',name:'oembed',label:'Embed',type:'oembed',settings:{} }),
      def({ key:'link',name:'link',label:'Link',type:'link',settings:{schemes:['https']} }),
      def({ key:'message',name:'message',label:'Message',type:'message',settings:{text:'Helpful message'} }),
      def({ key:'image',name:'image',label:'Image',type:'image',settings:{return:'ref'} }),
      def({ key:'file',name:'file',label:'File',type:'file',settings:{return:'ref'} }),
      def({ key:'gallery',name:'gallery',label:'Gallery',type:'gallery',settings:{return:'ref'} }),
      def({ key:'entity',name:'entity',label:'Entity',type:'entity',settings:{return:'ref'} }),
      def({ key:'entity_link',name:'entity_link',label:'Entity link',type:'entityLink',settings:{return:'ref'} }),
      def({ key:'relationship',name:'relationship',label:'Relationship',type:'relationship',settings:{return:'ref'} }),
      def({ key:'taxonomy',name:'taxonomy',label:'Taxonomy',type:'taxonomy',settings:{taxonomies:['topic'],return:'ref'} }),
      def({ key:'user',name:'user',label:'User',type:'user',settings:{return:'ref'} }),
      def({ key:'date',name:'date',label:'Date',type:'date',settings:{inputFormat:'Y-m-d',displayFormat:'Y-m-d',returnFormat:'Y-m-d'} }),
      def({ key:'date_time',name:'date_time',label:'Date time',type:'dateTime',settings:{inputFormat:'c',displayFormat:'c',returnFormat:'c',timezone:'utc'} }),
      def({ key:'time',name:'time',label:'Time',type:'time',settings:{inputFormat:'H:i',displayFormat:'H:i',returnFormat:'H:i'} }),
      def({ key:'color',name:'color',label:'Color',type:'color',settings:{} }),
      def({ key:'icon',name:'icon',label:'Icon',type:'icon',settings:{} }),
      def({ key:'map',name:'map',label:'Map',type:'map',settings:{providerKey:'test'} }),
      def({ key:'accordion',name:'accordion',label:'Accordion',type:'accordion',settings:{text:'Panel'} }),
      def({ key:'tab',name:'tab',label:'Tab',type:'tab',settings:{text:'Tab divider'} }),
      def({ key:'group',name:'group',label:'Group',type:'group',settings:{fields:[text]} }),
      def({ key:'repeater',name:'repeater',label:'Repeater',type:'repeater',settings:{fields:[text]} }),
      def({ key:'flexible',name:'flexible',label:'Flexible',type:'flexible',settings:{layouts:[{key:'hero',name:'hero',label:'Hero',fields:[text]}]} }),
      def({ key:'clone',name:'clone',label:'Clone',type:'clone',settings:{sourceFieldKeys:['title']} }),
    ] satisfies readonly AnyFieldDefinition[]
    const { container } = render(<CompleteHost fields={fields} resolveCloneFields={() => [text]} />)
    expect(screen.queryByText(/Unsupported field type/)).toBeNull()
    expect(screen.getByText('Helpful message')).toBeTruthy()
    expect(container.querySelector('style')?.textContent).toContain('@container fields-react')
  })

  it('evaluates conditional logic against root values and preserves hidden values', () => {
    const fields: readonly AnyFieldDefinition[] = [
      def({ key:'enabled',name:'enabled',label:'Enabled',type:'boolean',settings:{} }),
      def({ key:'secret_note',name:'secret_note',label:'Secret note',type:'text',settings:{},conditionalLogic:[[{fieldPath:['enabled'],operator:'eq',value:true}]] }),
    ]
    render(<CompleteHost fields={fields} initial={{enabled:false,secret_note:'preserve me'}} />)
    expect(screen.queryByLabelText('Secret note')).toBeNull()
    expect(screen.getByTestId('value').textContent).toContain('preserve me')
    fireEvent.click(screen.getByLabelText('Enabled'))
    expect((screen.getByLabelText('Secret note') as HTMLInputElement).value).toBe('preserve me')
  })

  it('adds and reorders repeater rows with stable row identity', () => {
    const fields: readonly AnyFieldDefinition[] = [def({ key:'items',name:'items',label:'Items',type:'repeater',settings:{fields:[text]} })]
    const ids = ['r1','r2']
    render(<CompleteHost fields={fields} createRowId={() => ids.shift()!} />)
    fireEvent.click(screen.getByRole('button',{name:'Add Items row'}))
    fireEvent.change(screen.getByLabelText('Title'),{target:{value:'First'}})
    fireEvent.click(screen.getByRole('button',{name:'Add Items row'}))
    const titles = screen.getAllByLabelText('Title')
    fireEvent.change(titles[1]!,{target:{value:'Second'}})
    const moveUp = screen.getByRole('button',{name:'Move Items row 2 up'})
    moveUp.focus()
    fireEvent.click(moveUp)
    expect(document.activeElement).toBe(moveUp)
    const stored = JSON.parse(screen.getByTestId('value').textContent ?? '{}') as {items:Array<{rowId:string;values:{title:string}}>}
    expect(stored.items.map((row)=>row.rowId)).toEqual(['r2','r1'])
    expect(stored.items.map((row)=>row.values.title)).toEqual(['Second','First'])
  })

  it('adds flexible layouts with stable identities and edits their nested values', () => {
    const fields: readonly AnyFieldDefinition[] = [def({ key:'content',name:'content',label:'Content',type:'flexible',settings:{layouts:[{key:'hero',name:'hero',label:'Hero',fields:[text]}]} })]
    render(<CompleteHost fields={fields} createRowId={()=>'layout-1'} />)
    fireEvent.click(screen.getByRole('button',{name:'Add Hero'}))
    fireEvent.change(screen.getByLabelText('Title'),{target:{value:'Welcome'}})
    const stored = JSON.parse(screen.getByTestId('value').textContent ?? '{}') as {content:Array<{rowId:string;layoutKey:string;values:{title:string}}>}
    expect(stored.content).toEqual([{rowId:'layout-1',layoutKey:'hero',values:{title:'Welcome'}}])
  })


  it('prevents read-only boolean and range controls from mutating',()=>{
    const fields:readonly AnyFieldDefinition[]=[
      def({key:'enabled',name:'enabled',label:'Enabled',type:'boolean',settings:{}}),
      def({key:'level',name:'level',label:'Level',type:'range',settings:{min:0,max:10}}),
    ]
    function ReadOnlyHost(){const [value,setValue]=useState<RuntimeFieldValueMap>({enabled:true,level:4});return <><CompleteFieldValuesEditor fields={fields} value={value} onChange={setValue} readOnly/><output data-testid="readonly-value">{JSON.stringify(value)}</output></>}
    render(<ReadOnlyHost/>)
    expect((screen.getByLabelText('Enabled') as HTMLInputElement).disabled).toBe(true)
    expect((screen.getByLabelText('Level') as HTMLInputElement).disabled).toBe(true)
    fireEvent.click(screen.getByLabelText('Enabled'))
    expect(screen.getByTestId('readonly-value').textContent).toBe('{"enabled":true,"level":4}')
  })

  it('enforces minimum recursive row and layout counts accessibly',()=>{
    const fields:readonly AnyFieldDefinition[]=[
      def({key:'items',name:'items',label:'Items',type:'repeater',settings:{fields:[text],min:1,max:1}}),
      def({key:'content',name:'content',label:'Content',type:'flexible',settings:{layouts:[{key:'hero',name:'hero',label:'Hero',fields:[text]}],min:1,max:1}}),
    ]
    render(<CompleteHost fields={fields} initial={{items:[{rowId:'r1',values:{title:'One'}}],content:[{rowId:'l1',layoutKey:'hero',values:{title:'Hero'}}]}}/>)
    expect((screen.getByRole('button',{name:'Remove Items row 1'}) as HTMLButtonElement).disabled).toBe(true)
    expect((screen.getByRole('button',{name:'Remove Content row 1'}) as HTMLButtonElement).disabled).toBe(true)
    expect((screen.getByRole('button',{name:'Add Items row'}) as HTMLButtonElement).disabled).toBe(true)
    expect((screen.getByRole('button',{name:'Add Hero'}) as HTMLButtonElement).disabled).toBe(true)
  })

  it('uses adopter clone resolution and emits password write operations without exposing a credential', () => {
    const fields: readonly AnyFieldDefinition[] = [
      def({ key:'clone',name:'clone',label:'Clone',type:'clone',settings:{sourceFieldKeys:['title']} }),
      def({ key:'password',name:'password',label:'Password',type:'password',settings:{hasherKey:'test'} }),
    ]
    render(<CompleteHost fields={fields} initial={{password:{configured:true}}} resolveCloneFields={() => [text]} />)
    expect(screen.getByText('Credential configured')).toBeTruthy()
    fireEvent.change(screen.getByLabelText('Password'),{target:{value:'new secret'}})
    const stored = screen.getByTestId('value').textContent ?? ''
    expect(stored).toContain('"operation":"set"')
    expect(stored).not.toContain('credential')
  })

  it('renders exact validation paths in focusable alerts', () => {
    const onChange = vi.fn()
    render(<CompleteFieldValuesEditor fields={[text]} value={{}} onChange={onChange} errors={[{path:['title'],message:'Title is required',code:'required'}]} />)
    const alert = screen.getByRole('alert')
    expect(alert.textContent).toBe('Title is required')
    expect(alert.getAttribute('tabindex')).toBe('0')
    expect(screen.getByLabelText('Title').getAttribute('aria-invalid')).toBe('true')
  })
})
