import { useState, useEffect } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { X, Loader2, Palette, Layers, EyeOff } from 'lucide-react'
import { toast } from 'sonner'
import api from '../../api/client'
import { savePartMaterials, saveManualOverrides, type PartMaterialMap, type PartMaterialEntry } from '../../api/cad'
import { fetchMaterialPBR, type MaterialPBRMap } from '../../api/assetLibraries'
import { previewColorForEntry, pbrColorHex } from './cadUtils'

// ---------------------------------------------------------------------------
// MaterialOut — matches GET /api/materials response
// ---------------------------------------------------------------------------

export interface MaterialOut {
  id: string
  name: string
  description: string | null
  hartomat_code: number | null
  source: string
}

// ---------------------------------------------------------------------------
// MaterialPanel — floating panel for assigning a material/color to a part
// ---------------------------------------------------------------------------

export type IsolateMode = 'none' | 'ghost' | 'hide'

export interface MaterialPanelProps {
  partName: string
  cadFileId: string
  currentEntry: PartMaterialEntry | undefined
  partMaterials: PartMaterialMap
  onClose: () => void
  isolateMode?: IsolateMode
  onIsolateModeChange?: (mode: IsolateMode) => void
  /** Source part name from XCAF (shown alongside partKey slug) */
  sourcePartName?: string
  /** How the current assignment was derived */
  assignmentProvenance?: 'manual' | 'auto' | 'source' | 'default'
  /** True when GLB has partKeyMap — saves via /manual-material-overrides endpoint */
  isPartKeyMode?: boolean
  /** Current manual overrides map (needed to merge when saving in partKey mode) */
  manualOverrides?: Record<string, string>
  /** PBR material map from Blender asset library */
  pbrMap?: MaterialPBRMap
}

export default function MaterialPanel({
  partName,
  cadFileId,
  currentEntry,
  partMaterials,
  onClose,
  isolateMode = 'none',
  onIsolateModeChange,
  sourcePartName,
  assignmentProvenance,
  isPartKeyMode = false,
  manualOverrides = {},
  pbrMap: pbrMapProp,
}: MaterialPanelProps) {
  const queryClient = useQueryClient()

  // Fetch PBR data if not passed via props
  const { data: pbrMapFetched } = useQuery({
    queryKey: ['material-pbr'],
    queryFn: fetchMaterialPBR,
    staleTime: 300_000,
    enabled: !pbrMapProp,
  })
  const pbrMap = pbrMapProp ?? pbrMapFetched ?? {}

  // Fetch all tenant materials (no filter — user sees their full library)
  const { data: allMaterials = [] } = useQuery({
    queryKey: ['materials'],
    queryFn: async () => {
      const res = await api.get<MaterialOut[]>('/materials')
      return res.data
    },
    staleTime: 60_000,
  })

  const [assignType, setAssignType] = useState<'library' | 'hex'>(
    currentEntry?.type ?? 'library',
  )
  const [hexValue, setHexValue] = useState(
    currentEntry?.type === 'hex' ? currentEntry.value : '#888888',
  )
  const [libValue, setLibValue] = useState(
    currentEntry?.type === 'library'
      ? currentEntry.value
      : (allMaterials[0]?.name ?? ''),
  )

  // Set default library value once materials load
  useEffect(() => {
    if (!libValue && allMaterials.length > 0) setLibValue(allMaterials[0].name)
  }, [allMaterials]) // eslint-disable-line react-hooks/exhaustive-deps

  // Legacy save (part_materials, keyed by normalized mesh name)
  const saveMut = useMutation({
    mutationFn: (updated: PartMaterialMap) => savePartMaterials(cadFileId, updated),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['part-materials', cadFileId] })
      toast.success(`Material assigned to "${partName}"`)
      onClose()
    },
    onError: () => toast.error('Failed to save material assignment'),
  })

  const removeMut = useMutation({
    mutationFn: (updated: PartMaterialMap) => savePartMaterials(cadFileId, updated),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['part-materials', cadFileId] })
      toast.success(`Assignment removed from "${partName}"`)
      onClose()
    },
    onError: () => toast.error('Failed to remove assignment'),
  })

  // partKey mode save (manual_material_overrides, keyed by partKey slug)
  const manualSaveMut = useMutation({
    mutationFn: (updated: Record<string, string>) => saveManualOverrides(cadFileId, updated),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['manual-overrides', cadFileId] })
      toast.success(`Material assigned to "${partName}"`)
      onClose()
    },
    onError: () => toast.error('Failed to save material assignment'),
  })

  const manualRemoveMut = useMutation({
    mutationFn: (updated: Record<string, string>) => saveManualOverrides(cadFileId, updated),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['manual-overrides', cadFileId] })
      toast.success(`Assignment removed from "${partName}"`)
      onClose()
    },
    onError: () => toast.error('Failed to remove assignment'),
  })

  function handleAssign() {
    const materialValue = assignType === 'hex' ? hexValue : libValue
    if (isPartKeyMode) {
      manualSaveMut.mutate({ ...manualOverrides, [partName]: materialValue })
    } else {
      const entry: PartMaterialEntry =
        assignType === 'hex'
          ? { type: 'hex', value: hexValue }
          : { type: 'library', value: libValue }
      saveMut.mutate({ ...partMaterials, [partName]: entry })
    }
  }

  function handleRemove() {
    if (isPartKeyMode) {
      const updated = { ...manualOverrides }
      delete updated[partName]
      manualRemoveMut.mutate(updated)
    } else {
      const updated = { ...partMaterials }
      delete updated[partName]
      removeMut.mutate(updated)
    }
  }

  const isBusy = saveMut.isPending || removeMut.isPending || manualSaveMut.isPending || manualRemoveMut.isPending

  // Preview color for selected material
  const selectedPbr = pbrMap[libValue]
  const previewHex = assignType === 'hex'
    ? hexValue
    : (selectedPbr ? pbrColorHex(selectedPbr) : '#888888')

  return (
    <div
      className="absolute top-2 left-2 z-30 w-72 bg-gray-900 border border-gray-700 rounded-lg shadow-2xl"
      onClick={(e) => e.stopPropagation()}
    >
      {/* Header */}
      <div className="flex items-center justify-between px-3 py-2 border-b border-gray-700">
        <div className="flex items-center gap-2 min-w-0 flex-1">
          <Palette size={13} className="text-accent shrink-0" />
          <div className="min-w-0">
            <span className="text-white text-xs font-semibold truncate block" title={partName}>
              {partName}
            </span>
            {sourcePartName && sourcePartName !== partName && (
              <span className="text-gray-500 text-[10px] truncate block" title={sourcePartName}>
                {sourcePartName}
              </span>
            )}
          </div>
          {assignmentProvenance && assignmentProvenance !== 'default' && (
            <span className={`shrink-0 text-[9px] font-medium px-1 py-0.5 rounded ${
              assignmentProvenance === 'manual' ? 'bg-accent/20 text-accent' :
              assignmentProvenance === 'auto'   ? 'bg-green-900/40 text-green-400' :
                                                  'bg-yellow-900/40 text-yellow-400'
            }`}>
              {assignmentProvenance}
            </span>
          )}
        </div>
        <button onClick={onClose} className="text-gray-400 hover:text-white p-0.5 shrink-0 ml-1">
          <X size={14} />
        </button>
      </div>

      <div className="p-3 space-y-3">
        {/* Isolation toggles — ghost or hide all other parts */}
        {onIsolateModeChange && (
          <div className="flex gap-1.5">
            <button
              onClick={() => onIsolateModeChange(isolateMode === 'ghost' ? 'none' : 'ghost')}
              title="Ghost other parts (semi-transparent)"
              className={`flex-1 flex items-center justify-center gap-1 py-1 rounded text-[11px] font-medium transition-colors ${
                isolateMode === 'ghost'
                  ? 'bg-accent text-white'
                  : 'bg-gray-800 text-gray-300 hover:bg-gray-700'
              }`}
            >
              <Layers size={11} /> Ghost
            </button>
            <button
              onClick={() => onIsolateModeChange(isolateMode === 'hide' ? 'none' : 'hide')}
              title="Hide other parts"
              className={`flex-1 flex items-center justify-center gap-1 py-1 rounded text-[11px] font-medium transition-colors ${
                isolateMode === 'hide'
                  ? 'bg-accent text-white'
                  : 'bg-gray-800 text-gray-300 hover:bg-gray-700'
              }`}
            >
              <EyeOff size={11} /> Hide
            </button>
          </div>
        )}

        {/* Type tabs */}
        <div className="flex rounded-md overflow-hidden border border-gray-700 text-xs">
          {(['library', 'hex'] as const).map((t) => (
            <button
              key={t}
              onClick={() => setAssignType(t)}
              className={`flex-1 py-1.5 font-medium transition-colors ${
                assignType === t
                  ? 'bg-accent text-white'
                  : 'bg-gray-800 text-gray-300 hover:bg-gray-700'
              }`}
            >
              {t === 'library' ? 'Library Material' : 'Hex Color'}
            </button>
          ))}
        </div>

        {assignType === 'library' ? (
          <div>
            <label className="block text-gray-400 text-[11px] mb-1">Material</label>
            <select
              value={libValue}
              onChange={(e) => setLibValue(e.target.value)}
              className="w-full bg-gray-800 border border-gray-600 text-white text-xs rounded px-2 py-1.5 focus:outline-none focus:border-accent"
            >
              {allMaterials.map((m) => (
                <option key={m.id} value={m.name}>
                  {m.name}
                  {m.description ? ` — ${m.description}` : ''}
                </option>
              ))}
              {allMaterials.length === 0 && (
                <option value="">No materials found</option>
              )}
            </select>
          </div>
        ) : (
          <div>
            <label className="block text-gray-400 text-[11px] mb-1">Hex Color</label>
            <div className="flex items-center gap-2">
              <input
                type="color"
                value={hexValue}
                onChange={(e) => setHexValue(e.target.value)}
                className="w-10 h-8 rounded border border-gray-600 bg-gray-800 cursor-pointer p-0.5"
              />
              <input
                type="text"
                value={hexValue}
                onChange={(e) => setHexValue(e.target.value)}
                className="flex-1 bg-gray-800 border border-gray-600 text-white text-xs rounded px-2 py-1.5 font-mono focus:outline-none focus:border-accent"
                placeholder="#888888"
              />
            </div>
          </div>
        )}

        {/* Preview swatch with PBR info */}
        <div className="flex items-center gap-2 text-[11px] text-gray-400">
          <div
            className="w-4 h-4 rounded-sm border border-gray-600 shrink-0"
            style={{ backgroundColor: previewHex }}
          />
          <span className="flex-1">Preview</span>
          {assignType === 'library' && selectedPbr && (
            <span className="text-[10px] text-gray-500 font-mono">
              M:{selectedPbr.metallic.toFixed(1)} R:{selectedPbr.roughness.toFixed(1)}
            </span>
          )}
        </div>

        {/* Current assignment */}
        {currentEntry && (
          <div className="flex items-center gap-2 text-[11px] text-gray-400 bg-gray-800/60 rounded px-2 py-1.5">
            <div
              className="w-3 h-3 rounded-sm shrink-0 border border-gray-600"
              style={{ backgroundColor: previewColorForEntry(currentEntry, pbrMap) }}
            />
            <span className="truncate">Current: {currentEntry.value}</span>
          </div>
        )}

        {/* Actions */}
        <div className="flex gap-2 pt-1">
          <button
            onClick={handleAssign}
            disabled={isBusy || (assignType === 'library' && !libValue)}
            className="flex-1 px-3 py-1.5 rounded bg-accent hover:bg-accent-hover disabled:opacity-40 disabled:cursor-not-allowed text-white text-xs font-medium transition-colors flex items-center justify-center gap-1"
          >
            {(saveMut.isPending || manualSaveMut.isPending) && <Loader2 size={11} className="animate-spin" />}
            Assign
          </button>
          {currentEntry && (
            <button
              onClick={handleRemove}
              disabled={isBusy}
              className="px-3 py-1.5 rounded bg-gray-700 hover:bg-red-900 disabled:opacity-40 disabled:cursor-not-allowed text-gray-300 hover:text-white text-xs font-medium transition-colors flex items-center gap-1"
            >
              {(removeMut.isPending || manualRemoveMut.isPending) && <Loader2 size={11} className="animate-spin" />}
              Remove
            </button>
          )}
        </div>
      </div>
    </div>
  )
}
