import { useEffect, useId, useMemo, useRef, useState, type ChangeEvent } from 'react'
import {
  createFieldTypeRegistry,
  parseFieldDefinition,
  parseFieldGroupDefinition,
  type AnyFieldDefinition,
  type ConditionalRule,
  type ConditionalRuleGroups,
  type FieldGroup,
  type FieldPath,
  type FieldType,
  type FlexibleLayout,
  type DefinitionLocationRule,
  type LocationRuleGroups,
} from '@platform-modules/fields'

export const COMPLETE_FIELD_TYPES: readonly FieldType[] = Object.freeze(
  [...createFieldTypeRegistry().builtInTypes],
)

function defaultSettings(type: FieldType): Record<string, unknown> {
  if (type === 'textarea') return { newline: 'preserve' }
  if (type === 'url' || type === 'link') return { schemes: [] }
  if (type === 'password') return { hasherKey: '' }
  if (['select', 'checkbox', 'radio', 'buttonGroup'].includes(type)) return { choices: {} }
  if (type === 'image' || type === 'file' || type === 'gallery') return { return: 'ref' }
  if (type === 'entity' || type === 'entityLink' || type === 'relationship') return { return: 'ref' }
  if (type === 'taxonomy') return { taxonomies: [], return: 'ref' }
  if (type === 'user') return { return: 'ref' }
  if (type === 'date') return { inputFormat: '', displayFormat: '', returnFormat: '' }
  if (type === 'dateTime') return { inputFormat: '', displayFormat: '', returnFormat: '', timezone: 'utc' }
  if (type === 'time') return { inputFormat: '', displayFormat: '', returnFormat: '' }
  if (type === 'map') return { providerKey: '' }
  if (type === 'group' || type === 'repeater') return { fields: [] }
  if (type === 'flexible') return { layouts: [] }
  if (type === 'clone') return { sourceFieldKeys: [] }
  return {}
}

function defaultField(index = 0): AnyFieldDefinition {
  const suffix = index + 1
  return {
    key: `field_${suffix}`,
    name: `field_${suffix}`,
    label: `Field ${suffix}`,
    type: 'text',
    settings: {},
  }
}

function parsePath(input: string): FieldPath {
  const trimmed = input.trim()
  if (!trimmed) return Object.freeze(['field'])
  return Object.freeze(trimmed.split('.').filter(Boolean).map((segment) => /^\d+$/.test(segment) ? Number(segment) : segment))
}

function formatPath(path: FieldPath): string {
  return path.join('.')
}

function JsonObjectEditor({ label, value, onChange, disabled }: { label: string; value: unknown; onChange: (value: Record<string, unknown>) => void; disabled?: boolean }) {
  const serialized = useMemo(() => JSON.stringify(value ?? {}, null, 2), [value])
  const [draft, setDraft] = useState(serialized)
  const [error, setError] = useState<string | null>(null)
  useEffect(() => setDraft(serialized), [serialized])
  return <div className="fields-definition-builder__json">
    <label>
      {label}
      <textarea
        rows={7}
        value={draft}
        disabled={disabled}
        onChange={(event) => {
          const next = event.currentTarget.value
          setDraft(next)
          try {
            const parsed = JSON.parse(next) as unknown
            if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) throw new Error('Settings must be a JSON object')
            setError(null)
            onChange(parsed as Record<string, unknown>)
          } catch (cause) {
            setError(cause instanceof Error ? cause.message : String(cause))
          }
        }}
      />
    </label>
    {error ? <div role="alert" tabIndex={0}>{error}</div> : null}
  </div>
}

function ConditionalLogicBuilder({ value, onChange, disabled }: { value: ConditionalRuleGroups | undefined; onChange: (value: ConditionalRuleGroups | undefined) => void; disabled?: boolean }) {
  const groups = value ? value.map((group) => [...group]) : []
  const replace = (next: ConditionalRule[][]) => onChange(next.length ? Object.freeze(next.map((group) => Object.freeze(group))) : undefined)
  const updateRule = (groupIndex: number, ruleIndex: number, patch: Partial<ConditionalRule>) => {
    const next = groups.map((group) => [...group])
    const current = next[groupIndex]?.[ruleIndex]
    if (!current) return
    next[groupIndex]![ruleIndex] = { ...current, ...patch } as ConditionalRule
    replace(next)
  }
  const addRule = (groupIndex: number) => {
    const next = groups.map((group) => [...group])
    next[groupIndex] = [...(next[groupIndex] ?? []), { fieldPath: ['field'], operator: 'eq', value: '' }]
    replace(next)
  }
  return <fieldset className="fields-definition-builder__conditions" disabled={disabled}>
    <legend>Conditional logic</legend>
    {groups.map((group, groupIndex) => <div key={groupIndex} className="fields-definition-builder__condition-group" aria-label={`Conditional OR group ${groupIndex + 1}`}>
      <strong>{groupIndex === 0 ? 'Show when' : 'OR'}</strong>
      {group.map((rule, ruleIndex) => <div key={ruleIndex} className="fields-definition-builder__condition-rule">
        <input aria-label={`Condition ${groupIndex + 1}.${ruleIndex + 1} field path`} value={formatPath(rule.fieldPath)} onChange={(event) => updateRule(groupIndex, ruleIndex, { fieldPath: parsePath(event.currentTarget.value) })} />
        <select aria-label={`Condition ${groupIndex + 1}.${ruleIndex + 1} operator`} value={rule.operator} onChange={(event) => {
          const operator = event.currentTarget.value as ConditionalRule['operator']
          const next = ['empty', 'notEmpty'].includes(operator)
            ? { fieldPath: rule.fieldPath, operator, ...(rule.quantifier ? { quantifier: rule.quantifier } : {}) }
            : operator === 'matches'
              ? { fieldPath: rule.fieldPath, operator, value: { source: '' }, ...(rule.quantifier ? { quantifier: rule.quantifier } : {}) }
              : { fieldPath: rule.fieldPath, operator, value: '', ...(rule.quantifier ? { quantifier: rule.quantifier } : {}) }
          updateRule(groupIndex, ruleIndex, next as ConditionalRule)
        }}>
          {['eq','neq','contains','notContains','gt','gte','lt','lte','empty','notEmpty','matches'].map((operator) => <option key={operator} value={operator}>{operator}</option>)}
        </select>
        {!['empty', 'notEmpty'].includes(rule.operator) ? <input
          aria-label={`Condition ${groupIndex + 1}.${ruleIndex + 1} value`}
          type={['gt','gte','lt','lte'].includes(rule.operator) ? 'number' : 'text'}
          value={'value' in rule ? (rule.operator === 'matches' ? rule.value.source : String(rule.value ?? '')) : ''}
          onChange={(event) => {
            if (rule.operator === 'matches') updateRule(groupIndex, ruleIndex, { value: { source: event.currentTarget.value } } as Partial<ConditionalRule>)
            else if (['gt','gte','lt','lte'].includes(rule.operator)) updateRule(groupIndex, ruleIndex, { value: Number(event.currentTarget.value) } as Partial<ConditionalRule>)
            else updateRule(groupIndex, ruleIndex, { value: event.currentTarget.value } as Partial<ConditionalRule>)
          }}
        /> : null}
        <select aria-label={`Condition ${groupIndex + 1}.${ruleIndex + 1} quantifier`} value={rule.quantifier ?? 'any'} onChange={(event) => updateRule(groupIndex, ruleIndex, { quantifier: event.currentTarget.value as 'any' | 'all' })}>
          <option value="any">any</option><option value="all">all</option>
        </select>
        <button type="button" onClick={() => { const next=groups.map((candidate)=>[...candidate]);next[groupIndex]=next[groupIndex]!.filter((_,index)=>index!==ruleIndex);replace(next.filter((candidate)=>candidate.length)) }}>Remove condition</button>
      </div>)}
      <button type="button" onClick={() => addRule(groupIndex)}>Add AND condition</button>
    </div>)}
    <button type="button" onClick={() => replace([...groups,[{ fieldPath:['field'], operator:'eq', value:'' }]])}>Add OR group</button>
  </fieldset>
}

function LocationRulesBuilder({ value, onChange, disabled }: { value: LocationRuleGroups; onChange: (value: LocationRuleGroups) => void; disabled?: boolean }) {
  const groups=value.map((group)=>[...group])
  const replace=(next:DefinitionLocationRule[][])=>onChange(Object.freeze(next.map((group)=>Object.freeze(group))))
  const update=(groupIndex:number,ruleIndex:number,patch:Partial<DefinitionLocationRule>)=>{const next=groups.map((group)=>[...group]);next[groupIndex]![ruleIndex]={...next[groupIndex]![ruleIndex]!,...patch};replace(next)}
  return <fieldset disabled={disabled}><legend>Location rules</legend>
    {groups.map((group,groupIndex)=><div key={groupIndex} aria-label={`Location OR group ${groupIndex+1}`}>
      <strong>{groupIndex===0?'Match':'OR'}</strong>
      {group.map((rule,ruleIndex)=><div key={ruleIndex}>
        <input aria-label={`Location ${groupIndex+1}.${ruleIndex+1} parameter`} value={rule.parameter} onChange={(event)=>update(groupIndex,ruleIndex,{parameter:event.currentTarget.value})}/>
        <select aria-label={`Location ${groupIndex+1}.${ruleIndex+1} operator`} value={rule.operator} onChange={(event)=>update(groupIndex,ruleIndex,{operator:event.currentTarget.value as DefinitionLocationRule['operator']})}>
          {['eq','neq','in','notIn','contains'].map((operator)=><option key={operator} value={operator}>{operator}</option>)}
        </select>
        <input aria-label={`Location ${groupIndex+1}.${ruleIndex+1} value`} value={Array.isArray(rule.value)?rule.value.join(','):String(rule.value)} onChange={(event)=>update(groupIndex,ruleIndex,{value:rule.operator==='in'||rule.operator==='notIn'?event.currentTarget.value.split(',').map((item)=>item.trim()).filter(Boolean):event.currentTarget.value})}/>
        <button type="button" onClick={()=>{const next=groups.map((candidate)=>[...candidate]);next[groupIndex]=next[groupIndex]!.filter((_,index)=>index!==ruleIndex);replace(next.filter((candidate)=>candidate.length))}}>Remove location rule</button>
      </div>)}
      <button type="button" onClick={()=>{const next=groups.map((candidate)=>[...candidate]);next[groupIndex]=[...(next[groupIndex]??[]),{parameter:'entityType',operator:'eq',value:'content'}];replace(next)}}>Add AND location rule</button>
    </div>)}
    <button type="button" onClick={()=>replace([...groups,[{parameter:'entityType',operator:'eq',value:'content'}]])}>Add OR location group</button>
  </fieldset>
}

function FieldDefinitionRow({ value, onChange, onMove, onRemove, index, length, disabled }: { value: AnyFieldDefinition; onChange: (value: AnyFieldDefinition) => void; onMove: (direction: -1 | 1) => void; onRemove: () => void; index: number; length: number; disabled?: boolean }) {
  let validationError: string | null = null
  try { parseFieldDefinition(value) } catch (cause) { validationError = cause instanceof Error ? cause.message : String(cause) }
  const updateBase=(patch:Record<string,unknown>)=>onChange({...value,...patch} as AnyFieldDefinition)
  const setType=(type:FieldType)=>onChange({...value,type,settings:defaultSettings(type),...(type==='password'||type==='message'||type==='accordion'||type==='tab'?{defaultValue:undefined}:{})} as AnyFieldDefinition)
  const settings=value.settings as Record<string,unknown>
  const nestedFields=(value.type==='group'||value.type==='repeater')?(settings.fields as readonly AnyFieldDefinition[]??[]):null
  const layouts=value.type==='flexible'?(settings.layouts as readonly FlexibleLayout[]??[]):null
  return <li className="fields-definition-builder__field-row" style={{containerType:'inline-size'}}>
    <fieldset disabled={disabled}><legend>{value.label || value.key || `Field ${index+1}`}</legend>
      <div className="fields-definition-builder__grid">
        <label>Label<input aria-label={`Field ${index+1} label`} value={value.label} onChange={(event)=>updateBase({label:event.currentTarget.value})}/></label>
        <label>Key<input aria-label={`Field ${index+1} key`} value={value.key} onChange={(event)=>updateBase({key:event.currentTarget.value})}/></label>
        <label>Name<input aria-label={`Field ${index+1} name`} value={value.name} onChange={(event)=>updateBase({name:event.currentTarget.value})}/></label>
        <label>Type<select aria-label={`Field ${index+1} type`} value={value.type} onChange={(event)=>setType(event.currentTarget.value as FieldType)}>{COMPLETE_FIELD_TYPES.map((type)=><option key={type} value={type}>{type}</option>)}</select></label>
        <label><input type="checkbox" checked={value.required===true} onChange={(event)=>updateBase({required:event.currentTarget.checked||undefined})}/> Required</label>
        <label><input type="checkbox" checked={value.readOnly===true} onChange={(event)=>updateBase({readOnly:event.currentTarget.checked||undefined})}/> Read-only</label>
        <label className="fields-definition-builder__wide">Instructions<textarea aria-label={`Field ${index+1} instructions`} value={value.instructions??''} onChange={(event)=>updateBase({instructions:event.currentTarget.value||undefined})}/></label>
      </div>
      <JsonObjectEditor label="Settings JSON" value={value.settings} disabled={disabled} onChange={(next)=>updateBase({settings:next})}/>
      {nestedFields ? <FieldDefinitionListBuilder value={nestedFields} onChange={(fields)=>updateBase({settings:{...settings,fields}})} disabled={disabled} label={`${value.label} child fields`}/> : null}
      {layouts ? <div className="fields-definition-builder__layouts"><h4>Flexible layouts</h4>{layouts.map((layout,layoutIndex)=><fieldset key={layoutIndex}><legend>{layout.label}</legend>
        <input aria-label={`Layout ${layoutIndex+1} key`} value={layout.key} onChange={(event)=>{const next=layouts.map((candidate,candidateIndex)=>candidateIndex===layoutIndex?{...candidate,key:event.currentTarget.value}:candidate);updateBase({settings:{...settings,layouts:next}})}}/>
        <input aria-label={`Layout ${layoutIndex+1} name`} value={layout.name} onChange={(event)=>{const next=layouts.map((candidate,candidateIndex)=>candidateIndex===layoutIndex?{...candidate,name:event.currentTarget.value}:candidate);updateBase({settings:{...settings,layouts:next}})}}/>
        <input aria-label={`Layout ${layoutIndex+1} label`} value={layout.label} onChange={(event)=>{const next=layouts.map((candidate,candidateIndex)=>candidateIndex===layoutIndex?{...candidate,label:event.currentTarget.value}:candidate);updateBase({settings:{...settings,layouts:next}})}}/>
        <FieldDefinitionListBuilder value={layout.fields} onChange={(fields)=>{const next=layouts.map((candidate,candidateIndex)=>candidateIndex===layoutIndex?{...candidate,fields}:candidate);updateBase({settings:{...settings,layouts:next}})}} disabled={disabled} label={`${layout.label} fields`}/>
        <button type="button" onClick={()=>updateBase({settings:{...settings,layouts:layouts.filter((_,candidateIndex)=>candidateIndex!==layoutIndex)}})}>Remove layout</button>
      </fieldset>)}<button type="button" onClick={()=>{const suffix=layouts.length+1;updateBase({settings:{...settings,layouts:[...layouts,{key:`layout_${suffix}`,name:`layout_${suffix}`,label:`Layout ${suffix}`,fields:[]}]}})}}>Add layout</button></div> : null}
      <ConditionalLogicBuilder value={value.conditionalLogic} onChange={(conditionalLogic)=>updateBase({conditionalLogic})} disabled={disabled}/>
      {validationError?<div role="alert" tabIndex={0}>{validationError}</div>:null}
      <div className="fields-definition-builder__actions">
        <button type="button" aria-label={`Move ${value.label} up`} aria-disabled={index===0} onClick={()=>{if(index>0)onMove(-1)}}>Move up</button>
        <button type="button" aria-label={`Move ${value.label} down`} aria-disabled={index===length-1} onClick={()=>{if(index<length-1)onMove(1)}}>Move down</button>
        <button type="button" aria-label={`Remove ${value.label}`} onClick={onRemove}>Remove field</button>
      </div>
    </fieldset>
  </li>
}

export function FieldDefinitionListBuilder({ value, onChange, disabled, label='Fields' }: { value: readonly AnyFieldDefinition[]; onChange: (value: readonly AnyFieldDefinition[]) => void; disabled?: boolean; label?: string }) {
  const listId=useId(),idsRef=useRef<string[]>([]),serialRef=useRef(0)
  while(idsRef.current.length<value.length)idsRef.current.push(`${listId}-field-${serialRef.current++}`)
  if(idsRef.current.length>value.length)idsRef.current.length=value.length
  const replace=(index:number,next:AnyFieldDefinition)=>onChange(value.map((field,candidateIndex)=>candidateIndex===index?next:field))
  const move=(index:number,direction:-1|1)=>{const target=index+direction;if(target<0||target>=value.length)return;const next=[...value];[next[index],next[target]]=[next[target]!,next[index]!];[idsRef.current[index],idsRef.current[target]]=[idsRef.current[target]!,idsRef.current[index]!];onChange(next)}
  const remove=(index:number)=>{idsRef.current.splice(index,1);onChange(value.filter((_,candidateIndex)=>candidateIndex!==index))}
  const add=()=>{idsRef.current.push(`${listId}-field-${serialRef.current++}`);onChange([...value,defaultField(value.length)])}
  return <section className="fields-definition-builder__list" aria-label={label} style={{containerType:'inline-size'}}>
    <ol>{value.map((field,index)=><FieldDefinitionRow key={idsRef.current[index]} value={field} index={index} length={value.length} disabled={disabled} onChange={(next)=>replace(index,next)} onMove={(direction)=>move(index,direction)} onRemove={()=>remove(index)}/>)}</ol>
    <button type="button" disabled={disabled} onClick={add}>Add field</button>
  </section>
}

export interface FieldGroupDefinitionBuilderProps { readonly value: FieldGroup; readonly onChange: (value: FieldGroup) => void; readonly disabled?: boolean }
export function FieldGroupDefinitionBuilder({ value, onChange, disabled }: FieldGroupDefinitionBuilderProps) {
  let validationError:string|null=null
  try{parseFieldGroupDefinition(value)}catch(cause){validationError=cause instanceof Error?cause.message:String(cause)}
  const update=(patch:Partial<FieldGroup>)=>onChange({...value,...patch})
  return <section className="fields-definition-builder" style={{containerType:'inline-size',containerName:'fields-definition-builder'}} aria-label={`Field group ${value.title}`}>
    <style>{`.fields-definition-builder__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.65rem}.fields-definition-builder__wide{grid-column:1/-1}.fields-definition-builder__field-row{display:grid;gap:.5rem}.fields-definition-builder__actions{display:flex;gap:.4rem;flex-wrap:wrap}@container fields-definition-builder (max-width:36rem){.fields-definition-builder__grid{grid-template-columns:minmax(0,1fr)}.fields-definition-builder__wide{grid-column:1}}`}</style>
    <fieldset disabled={disabled}><legend>Group definition</legend>
      <div className="fields-definition-builder__grid">
        <label>Title<input aria-label="Group title" value={value.title} onChange={(event)=>update({title:event.currentTarget.value})}/></label>
        <label>Key<input aria-label="Group key" value={value.key} onChange={(event)=>update({key:event.currentTarget.value})}/></label>
        <label>Position<select aria-label="Group position" value={value.position??'normal'} onChange={(event)=>update({position:event.currentTarget.value as FieldGroup['position']})}><option value="normal">normal</option><option value="side">side</option><option value="afterTitle">afterTitle</option></select></label>
        <label>Style<select aria-label="Group style" value={value.style??'default'} onChange={(event)=>update({style:event.currentTarget.value as FieldGroup['style']})}><option value="default">default</option><option value="seamless">seamless</option></select></label>
        <label><input type="checkbox" checked={value.active} onChange={(event)=>update({active:event.currentTarget.checked})}/> Active</label>
        <label>Order<input aria-label="Group order" type="number" value={value.order??''} onChange={(event)=>update({order:event.currentTarget.value===''?undefined:Number(event.currentTarget.value)})}/></label>
        <label className="fields-definition-builder__wide">Description<textarea aria-label="Group description" value={value.description??''} onChange={(event)=>update({description:event.currentTarget.value||undefined})}/></label>
      </div>
      <LocationRulesBuilder value={value.location} onChange={(location)=>update({location})} disabled={disabled}/>
      <FieldDefinitionListBuilder value={value.fields} onChange={(fields)=>update({fields})} disabled={disabled}/>
      {validationError?<div role="alert" tabIndex={0}>{validationError}</div>:null}
    </fieldset>
  </section>
}
