'use client'
/**
 * Master-data domain widget.
 *
 * Lives in the master-data module (not under `components/ui/`) because it
 * directly knows the master-data schema (`master_data_types`,
 * `master_data_values`, `master_data_suggestions`) and runs Supabase queries
 * against it. UI primitives in `components/ui/` are domain-free. See ADR 015
 * layer map.
 */

import { useState, useEffect, useRef, useMemo, useId } from'react'
import { ChevronDown, X } from'lucide-react'
import { nextHighlightIndex } from'@/lib/ui/list-keyboard-nav'
import { createClient } from'@/lib/supabase/client'
import type { MasterDataValue, ComboboxValue } from'@/lib/master-data/types'

interface Props {
 typeCode: string
 value: ComboboxValue
 onChange: (v: ComboboxValue) => void
 required?: boolean
 allowFreeText?: boolean
 placeholder?: string
 disabled?: boolean
}

const INPUT_CLS ='w-full h-11 px-3 pr-8 rounded-lg border border-border bg-background text-sm focus:outline-none focus:ring-2 focus:ring-primary'

export function resolveEnterTarget(
 highlighted: number,
 filteredCount: number,
 showNewOption: boolean,
): 'value' | 'new' | null {
 if (highlighted < 0) return null
 if (highlighted < filteredCount) return 'value'
 if (showNewOption && highlighted === filteredCount) return 'new'
 return null
}

export default function MasterDataCombobox({
 typeCode,
 value,
 onChange,
 required = false,
 allowFreeText = false,
 placeholder ='Bitte wählen…',
 disabled = false,
}: Props) {
 const [values, setValues] = useState<MasterDataValue[]>([])
 const [typeId, setTypeId] = useState<string | null>(null)
 const [inputText, setInputText] = useState('')
 const [open, setOpen] = useState(false)
 const [loading, setLoading] = useState(true)
 const [highlighted, setHighlighted] = useState(-1)
 const listboxId = useId()

 const containerRef = useRef<HTMLDivElement>(null)
 const inputRef = useRef<HTMLInputElement>(null)
 const optionRefs = useRef<(HTMLButtonElement | null)[]>([])
 const supabase = useMemo(() => createClient(), [])

 // Load values on mount
 useEffect(() => {
 async function load() {
 const { data: typeRow } = await supabase
 .from('master_data_types')
 .select('id')
 .eq('code', typeCode)
 .single()
 if (!typeRow) { setLoading(false); return }

 setTypeId(typeRow.id)

 const { data } = await supabase
 .from('master_data_values')
 .select('id, type_id, code, label, normalized_label, sort_order, is_active, is_hidden, valid_from, valid_to, created_at, updated_at')
 .eq('type_id', typeRow.id)
 .eq('is_active', true)
 .eq('is_hidden', false)
 .order('sort_order')
 setValues((data as MasterDataValue[]) ?? [])
 setLoading(false)
 }
 void load()
 }, [typeCode])

 // Sync display text when value changes externally
 useEffect(() => {
 if (value.valueId) {
 const found = values.find(v => v.id === value.valueId)
 if (found) setInputText(found.label)
 } else if (value.freeText) {
 setInputText(value.freeText)
 } else {
 setInputText('')
 }
 }, [value.valueId, value.freeText, values])

 // Close dropdown on outside click
 useEffect(() => {
 function handleClick(e: MouseEvent) {
 if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
 setOpen(false)
 // Revert input text to current committed value
 if (value.valueId) {
 const found = values.find(v => v.id === value.valueId)
 setInputText(found?.label ??'')
 } else if (value.freeText) {
 setInputText(value.freeText)
 } else {
 setInputText('')
 }
 }
 }
 document.addEventListener('pointerdown', handleClick)
 return () => document.removeEventListener('pointerdown', handleClick)
 }, [value, values])

 useEffect(() => {
  if (highlighted >= 0) optionRefs.current[highlighted]?.scrollIntoView({ block: 'nearest' })
 }, [highlighted])

 const normalized = inputText.toLowerCase().trim()

 const filtered = values.filter(v =>
 v.label.toLowerCase().includes(normalized) ||
 v.code.toLowerCase().includes(normalized)
 )

 // Exact match check for duplicate detection
 const exactMatch = values.find(v => v.normalized_label === normalized || v.code.toLowerCase() === normalized)

 // Show"Neuer Wert"only if typing something not in list and free text allowed
 const showNewOption =
 allowFreeText &&
 normalized.length > 0 &&
 !exactMatch &&
 // Only if it doesn't match an already-selected value
 !(value.freeText?.toLowerCase().trim() === normalized && !value.valueId)

 function selectValue(v: MasterDataValue) {
 onChange({ valueId: v.id, freeText: null })
 setInputText(v.label)
 setOpen(false)
 setHighlighted(-1)
 }

 async function selectNewValue() {
 const text = inputText.trim()
 if (!text) return
 onChange({ valueId: null, freeText: text })
 setOpen(false)
 setHighlighted(-1)

 // Submit suggestion asynchronously (fire-and-forget in UI)
 if (typeId) {
 const { data: { user } } = await supabase.auth.getUser()
 await supabase.from('master_data_suggestions').insert({
 type_id: typeId,
 entered_value: text,
 normalized_value: text.toLowerCase().trim(),
 status:'pending',
 created_by: user?.id ?? null,
 })
 }
 }

 function handleClear(e: React.MouseEvent) {
 e.stopPropagation()
 onChange({ valueId: null, freeText: null })
 setInputText('')
 setOpen(false)
 setHighlighted(-1)
 }

 function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
  if (disabled || loading) return
  if (e.key === 'Escape') {
   setOpen(false)
   setHighlighted(-1)
   return
  }
  if (e.key === 'Enter') {
   const target = resolveEnterTarget(highlighted, filtered.length, showNewOption)
   if (!target) return
   e.preventDefault()
   if (target === 'value') selectValue(filtered[highlighted])
   else void selectNewValue()
   return
  }
  const itemCount = filtered.length + (showNewOption ? 1 : 0)
  const next = nextHighlightIndex(e.key, highlighted, itemCount)
  if (next === null) return
  e.preventDefault()
  if (!open) setOpen(true)
  setHighlighted(next)
 }

 const hasValue = !!(value.valueId || value.freeText)
 const showFreeTextBadge = !!(value.freeText && !value.valueId)

 return (
 <div ref={containerRef} className="relative">
 <div className="relative">
 <input
 ref={inputRef}
 type="text"
 value={inputText}
 disabled={disabled || loading}
 placeholder={loading ?'Lädt…': placeholder}
 required={required && !hasValue}
 onChange={e => {
 setInputText(e.target.value)
 setOpen(true)
 setHighlighted(-1)
 // If user is typing, clear the committed valueId so we don't lock to old selection
 if (value.valueId) onChange({ valueId: null, freeText: e.target.value })
 }}
 onFocus={() => { setOpen(true); setHighlighted(-1) }}
 onKeyDown={handleKeyDown}
 role="combobox"
 aria-haspopup="listbox"
 aria-expanded={open}
 aria-controls={listboxId}
 className={`${INPUT_CLS} ${disabled || loading ?'opacity-60 cursor-not-allowed':''}`}
 autoComplete="off"
 />
 <div className="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-1">
 {hasValue && !disabled && (
 <button
 type="button"
 tabIndex={-1}
 onClick={handleClear}
 className="text-muted-foreground hover:text-foreground transition-colors"
 >
 <X size={13} />
 </button>
 )}
 <ChevronDown size={14} className="text-muted-foreground pointer-events-none"/>
 </div>
 </div>

 {/* Free-text badge */}
 {showFreeTextBadge && (
 <span className="mt-1 inline-flex items-center gap-1 text-[10px] bg-amber-100 text-amber-700 border border-amber-200 px-1.5 py-0.5 rounded font-medium">
 Neu — wird geprüft
 </span>
 )}

 {/* Dropdown */}
 {open && !disabled && (
 <div id={listboxId} role="listbox" className="absolute z-50 top-full mt-1 w-full bg-card text-foreground border border-border rounded-lg shadow-lg max-h-52 overflow-y-auto">
 {loading && (
 <p className="px-3 py-2 text-sm text-muted-foreground">Lädt…</p>
 )}

 {!loading && filtered.length === 0 && !showNewOption && (
 <p className="px-3 py-2 text-sm text-muted-foreground italic">Keine Einträge gefunden.</p>
 )}

 {/* Suggest exact match if user typed something similar */}
 {!loading && exactMatch && value.valueId !== exactMatch.id && (
 <div className="px-3 py-1.5 bg-secondary border-b border-border">
 <p className="text-[10px] text-primary font-medium">Meinten Sie:</p>
 <button
 type="button"
 onClick={() => selectValue(exactMatch)}
 className="text-sm font-medium text-primary hover:text-primary-dark"
 >
 {exactMatch.label}
 </button>
 </div>
 )}

 {!loading && filtered.map((v, i) => (
 <button
 key={v.id}
 type="button"
 role="option"
 aria-selected={i === highlighted}
 ref={el => { optionRefs.current[i] = el }}
 onClick={() => selectValue(v)}
 onMouseEnter={() => setHighlighted(i)}
 className={`w-full text-left px-3 py-2 text-sm transition-colors flex items-center justify-between gap-2 ${
 value.valueId === v.id ?'bg-secondary font-medium text-primary': i === highlighted ?'bg-background text-foreground':'text-foreground hover:bg-background'
 }`}
 >
 <span>{v.label}</span>
 {v.code !== v.label && (
 <span className="text-xs text-muted-foreground">{v.code}</span>
 )}
 </button>
 ))}

 {showNewOption && (
 <button
 type="button"
 role="option"
 aria-selected={highlighted === filtered.length}
 ref={el => { optionRefs.current[filtered.length] = el }}
 onClick={() => void selectNewValue()}
 onMouseEnter={() => setHighlighted(filtered.length)}
 className={`w-full text-left px-3 py-2 text-sm text-amber-700 transition-colors border-t border-border flex items-center gap-2 ${highlighted === filtered.length ?'bg-amber-50':'hover:bg-amber-50'}`}
 >
 <span className="text-[10px] bg-amber-100 text-amber-700 px-1.5 py-0.5 rounded font-medium shrink-0">Neu</span>
 <span className="font-medium">{inputText.trim()}</span>
 <span className="text-muted-foreground text-xs">→ Zur Prüfung einreichen</span>
 </button>
 )}
 </div>
 )}
 </div>
 )
}
