import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Link } from 'react-router-dom'
import { ChevronDown, ChevronUp, GripVertical, LayoutDashboard, Plus, Search, X } from 'lucide-react'
import { api, formatFastApiDetail } from '../utils/api'
import { ensurePilotLabWidgetsRegistered } from '../widgetSystem/registerPilotLabWidgets'
import {
  BODY_CHART_DAYS_DEFAULT,
  BODY_CHART_DAYS_MAX,
  BODY_CHART_DAYS_MIN,
  normalizeBodyChartDays,
} from '../widgetSystem/bodyChartDays'
import KpiBoardConfigEditor from '../widgetSystem/KpiBoardConfigEditor'
import QuickCaptureConfigEditor from '../widgetSystem/QuickCaptureConfigEditor'
import BodyHistoryVizConfigEditor from '../widgetSystem/BodyHistoryVizConfigEditor'
import {
  moveWidget,
  moveWidgetToIndex,
  normalizeLayoutForEditor,
  toggleWidget,
} from '../widgetSystem/layoutEditor'

const CHART_DAYS_WIDGET_IDS = new Set([
  'body_overview',
  'body_history_viz',
  'activity_overview',
  'nutrition_detail_charts',
  'recovery_charts_panel',
])

const DESKTOP_DND_MQ = '(min-width: 768px)'

function catalogMetaById(catalog) {
  if (!catalog?.widgets?.length) return {}
  return Object.fromEntries(catalog.widgets.map((w) => [w.id, w]))
}

/**
 * @param {{ adminMode?: boolean }} [props]
 */
export default function DashboardConfigurePage({ adminMode = false } = {}) {
  ensurePilotLabWidgetsRegistered()

  const [bundle, setBundle] = useState(null)
  const [adminFromDatabase, setAdminFromDatabase] = useState(null)
  const [catalog, setCatalog] = useState(null)
  const [layout, setLayout] = useState(null)
  const [addPanelOpen, setAddPanelOpen] = useState(false)
  const [pickerSearch, setPickerSearch] = useState('')
  const [viewportDesktop, setViewportDesktop] = useState(() =>
    typeof window !== 'undefined' ? window.matchMedia(DESKTOP_DND_MQ).matches : false
  )
  const [busy, setBusy] = useState(false)
  const [msg, setMsg] = useState(null)
  const [err, setErr] = useState(null)
  const [chartDaysDraftByWidgetId, setChartDaysDraftByWidgetId] = useState({})
  const [dragOverFullIndex, setDragOverFullIndex] = useState(null)

  const pickerSearchRef = useRef(null)
   
  const dndEnabled = viewportDesktop

  useEffect(() => {
    const mq = window.matchMedia(DESKTOP_DND_MQ)
    const fn = () => setViewportDesktop(mq.matches)
    fn()
    mq.addEventListener('change', fn)
    return () => mq.removeEventListener('change', fn)
  }, [])

  useEffect(() => {
    if (!addPanelOpen) return
    const t = window.setTimeout(() => pickerSearchRef.current?.focus(), 50)
    const prev = document.body.style.overflow
    document.body.style.overflow = 'hidden'
    const onKey = (e) => {
      if (e.key === 'Escape') setAddPanelOpen(false)
    }
    window.addEventListener('keydown', onKey)
    return () => {
      window.clearTimeout(t)
      document.body.style.overflow = prev
      window.removeEventListener('keydown', onKey)
    }
  }, [addPanelOpen])

  const metaById = useMemo(() => catalogMetaById(catalog), [catalog])

  const isWidgetCatalogAllowed = useCallback(
    (widgetId) => {
      const m = metaById[widgetId]
      if (m == null) return true
      return m.allowed !== false
    },
    [metaById],
  )

  const commitChartDaysDraftToLayout = useCallback((draftStr, baseLayout, widgetId) => {
    const clamped = normalizeBodyChartDays(
      draftStr === '' || draftStr == null ? BODY_CHART_DAYS_DEFAULT : draftStr
    )
    return {
      ...baseLayout,
      widgets: baseLayout.widgets.map((x) =>
        x.id !== widgetId ? x : { ...x, config: { ...x.config, chart_days: clamped } }
      ),
    }
  }, [])

  const load = useCallback(async () => {
    setErr(null)
    try {
      if (adminMode) {
        const [cat, d] = await Promise.all([
          api.adminGetWidgetsCatalogFull(),
          api.adminGetDashboardProductDefault(),
        ])
        setCatalog(cat)
        setBundle({ custom: false, product_default_layout: d.layout })
        setAdminFromDatabase(!!d.from_database)
        setChartDaysDraftByWidgetId({})
        setLayout(normalizeLayoutForEditor(structuredClone(d.layout)))
      } else {
        const [cat, b] = await Promise.all([api.getAppWidgetsCatalog(), api.getAppDashboardLayout()])
        setCatalog(cat)
        setBundle(b)
        setAdminFromDatabase(null)
        setChartDaysDraftByWidgetId({})
        const base = b.custom ? b.layout : structuredClone(b.product_default_layout)
        setLayout(normalizeLayoutForEditor(base))
      }
    } catch (e) {
      setErr(formatFastApiDetail(null, e.message))
    }
  }, [adminMode])

  useEffect(() => {
    load()
  }, [load])

  const openAddPanel = () => {
    setPickerSearch('')
    setAddPanelOpen(true)
  }

  const save = async () => {
    if (!layout) return
    let toSave = layout
    const draftEntries = Object.entries(chartDaysDraftByWidgetId)
    if (draftEntries.length) {
      for (const [wid, val] of draftEntries) {
        toSave = normalizeLayoutForEditor(commitChartDaysDraftToLayout(val, toSave, wid))
      }
      setLayout(toSave)
      setChartDaysDraftByWidgetId({})
    }
    setBusy(true)
    setMsg(null)
    setErr(null)
    try {
      if (adminMode) {
        await api.adminPutDashboardProductDefault(toSave)
        setMsg('System-Standard für die Übersicht wurde gespeichert.')
      } else {
        await api.putAppDashboardLayout(toSave)
        setMsg('Dein Dashboard wurde gespeichert.')
      }
      await load()
    } catch (e) {
      setErr(formatFastApiDetail(null, e.message))
    } finally {
      setBusy(false)
    }
  }

  const resetToSystem = async () => {
    const ok = adminMode
      ? window.confirm(
          'Eintrag in der Datenbank löschen und Layout aus dem Code (widget_catalog) wiederherstellen?'
        )
      : window.confirm('Dein individuelles Layout löschen und System-Standard wiederherstellen?')
    if (!ok) return
    setBusy(true)
    setMsg(null)
    setErr(null)
    try {
      if (adminMode) {
        const r = await api.adminDeleteDashboardProductDefault()
        setChartDaysDraftByWidgetId({})
        setLayout(normalizeLayoutForEditor(r.layout))
        setMsg('Code-Standard wiederhergestellt (kein DB-Override mehr).')
      } else {
        const r = await api.resetAppDashboardLayout()
        setChartDaysDraftByWidgetId({})
        setLayout(normalizeLayoutForEditor(r.layout))
        setMsg('Auf System-Standard zurückgesetzt.')
      }
      await load()
    } catch (e) {
      setErr(formatFastApiDetail(null, e.message))
    } finally {
      setBusy(false)
    }
  }

  const pickerLower = pickerSearch.trim().toLowerCase()

  const libraryIndices = useMemo(() => {
    if (!layout?.widgets) return []
    return layout.widgets
      .map((w, i) => i)
      .filter((i) => {
        const w = layout.widgets[i]
        if (w.enabled || !isWidgetCatalogAllowed(w.id)) return false
        if (!pickerLower) return true
        const m = metaById[w.id]
        const hay = `${m?.title || ''} ${m?.description || ''} ${w.id}`.toLowerCase()
        return hay.includes(pickerLower)
      })
  }, [layout, pickerLower, metaById, isWidgetCatalogAllowed])

  const activeIndices = useMemo(() => {
    if (!layout?.widgets) return []
    return layout.widgets
      .map((w, i) => i)
      .filter((i) => layout.widgets[i].enabled && isWidgetCatalogAllowed(layout.widgets[i].id))
  }, [layout, isWidgetCatalogAllowed])

  const addableCount = useMemo(() => {
    if (!layout?.widgets) return 0
    return layout.widgets.filter((w) => !w.enabled && isWidgetCatalogAllowed(w.id)).length
  }, [layout, isWidgetCatalogAllowed])

  const onDragStartRow = (e, fullIndex) => {
    if (!dndEnabled) return
    e.dataTransfer.effectAllowed = 'move'
    e.dataTransfer.setData('text/plain', String(fullIndex))
    try {
      e.dataTransfer.setDragImage(e.currentTarget, 0, 0)
    } catch {
      /* ok */
    }
  }

  const onDragOverRow = (e, fullIndex) => {
    if (!dndEnabled) return
    e.preventDefault()
    e.dataTransfer.dropEffect = 'move'
    setDragOverFullIndex(fullIndex)
  }

  const onDragLeaveRow = () => {
    setDragOverFullIndex(null)
  }

  const onDropRow = (e, dropFullIndex) => {
    if (!dndEnabled) return
    e.preventDefault()
    setDragOverFullIndex(null)
    const raw = e.dataTransfer.getData('text/plain')
    const from = Number.parseInt(raw, 10)
    if (!Number.isFinite(from)) return
    setLayout((L) => normalizeLayoutForEditor(moveWidgetToIndex(L, from, dropFullIndex)))
  }

  const onDragEndRow = () => {
    setDragOverFullIndex(null)
  }

  if (err && !layout) {
    return (
      <div style={{ padding: 24, maxWidth: 640, margin: '0 auto' }}>
        <p style={{ color: '#D85A30' }}>{err}</p>
        <button type="button" className="btn btn-secondary" onClick={load}>
          Erneut laden
        </button>
      </div>
    )
  }

  if (!layout) {
    return (
      <div style={{ padding: 48, textAlign: 'center' }}>
        <div className="spinner" style={{ width: 32, height: 32, margin: '0 auto' }} />
      </div>
    )
  }

  return (
    <div style={{ paddingBottom: 96, textAlign: 'left', maxWidth: 720, margin: '0 auto' }}>
      <div style={{ marginBottom: 20 }}>
        <Link
          to={adminMode ? '/admin/g/system' : '/settings'}
          className="btn btn-secondary"
          style={{ display: 'inline-flex', marginBottom: 12, textDecoration: 'none' }}
        >
          {adminMode ? '← Basiseinstellungen (Admin)' : '← Einstellungen'}
        </Link>
        <h1 className="page-title" style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <LayoutDashboard size={26} color="var(--accent)" />
          {adminMode ? 'Produkt-Übersicht: Systemstandard' : 'Übersicht anpassen'}
        </h1>
        <p style={{ fontSize: 13, color: 'var(--text2)', lineHeight: 1.6, marginTop: 8 }}>
          {adminMode
            ? 'Globales Standard-Dashboard für alle Nutzer ohne eigenes Layout. Gespeichert in der Datenbank; mit „Code-Standard wiederherstellen“ wird der Eintrag entfernt und der Fallback aus dem Code genutzt.'
            : 'Kacheln für die Startseite sortieren und entfernen. Neue Kacheln über „Kachel hinzufügen“ – mit Suche direkt im eigenen Fenster, ohne langes Scrollen.'}
        </p>
        {adminMode && adminFromDatabase != null && (
          <p style={{ fontSize: 12, color: 'var(--accent)', marginTop: 10, lineHeight: 1.5 }}>
            {adminFromDatabase ? (
              <>
                Aktuell gilt ein <strong>gespeicherter Systemstandard</strong> (Datenbank).
              </>
            ) : (
              <>
                Es liegt <strong>kein DB-Override</strong> vor — es wird der Code-Standard aus dem Widget-Katalog
                verwendet.
              </>
            )}
          </p>
        )}
        {!adminMode && !bundle?.custom && (
          <p style={{ fontSize: 12, color: 'var(--accent)', marginTop: 10, lineHeight: 1.5 }}>
            Du bearbeitest gerade das <strong>System-Standardlayout</strong>. Mit „Speichern“ legst du deine persönliche
            Version ab.
          </p>
        )}
      </div>

      <div className="card section-gap" style={{ marginBottom: 16 }}>
        <div className="card-title" style={{ fontSize: 15, display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
          <span>
            Aktive Kacheln · {activeIndices.length}
            {dndEnabled && (
              <span style={{ fontSize: 12, fontWeight: 400, color: 'var(--text3)', marginLeft: 8 }}>
                (ab Desktop: ziehen oder Pfeile)
              </span>
            )}
          </span>
          <button type="button" className="btn btn-primary" style={{ fontSize: 13 }} onClick={openAddPanel} disabled={addableCount === 0}>
            <Plus size={16} style={{ marginRight: 6, verticalAlign: 'middle' }} />
            Kachel hinzufügen{addableCount > 0 ? ` (${addableCount})` : ''}
          </button>
        </div>
        {addableCount === 0 && (
          <p style={{ fontSize: 12, color: 'var(--text3)', marginTop: 8, marginBottom: 0 }}>
            Alle freigeschalteten Kacheln sind aktiv.
          </p>
        )}
        {err && <p style={{ fontSize: 12, color: '#D85A30', marginTop: 12, marginBottom: 0 }}>{err}</p>}
        {msg && <p style={{ fontSize: 12, color: 'var(--accent)', marginTop: 12, marginBottom: 0 }}>{msg}</p>}
        <ul style={{ listStyle: 'none', padding: 0, margin: '12px 0 0' }}>
          {activeIndices.map((i) => {
            const w = layout.widgets[i]
            const label = metaById[w.id]?.title || w.id
            const chartDaysVal =
              w.config?.chart_days != null
                ? normalizeBodyChartDays(w.config.chart_days)
                : BODY_CHART_DAYS_DEFAULT
            const dragOver = dragOverFullIndex === i
            return (
              <li
                key={w.id}
                onDragOver={(e) => onDragOverRow(e, i)}
                onDragLeave={onDragLeaveRow}
                onDrop={(e) => onDropRow(e, i)}
                style={{
                  padding: '10px 0',
                  borderBottom: '1px solid var(--border)',
                  background: dragOver ? 'var(--surface2)' : undefined,
                  borderRadius: dragOver ? 8 : undefined,
                }}
              >
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
                  {dndEnabled && (
                    <span
                      draggable
                      role="button"
                      tabIndex={0}
                      aria-label={`${label} verschieben`}
                      style={{ cursor: 'grab', color: 'var(--text3)', display: 'flex', touchAction: 'none' }}
                      onDragStart={(e) => onDragStartRow(e, i)}
                      onDragEnd={onDragEndRow}
                    >
                      <GripVertical size={18} />
                    </span>
                  )}
                  <label style={{ display: 'flex', alignItems: 'center', gap: 8, flex: '1 1 160px' }}>
                    <input type="checkbox" checked={w.enabled} onChange={() => setLayout((L) => toggleWidget(L, i))} />
                    <span style={{ fontSize: 14 }}>{label}</span>
                  </label>
                  <div style={{ display: 'flex', gap: 6 }}>
                    <button
                      type="button"
                      className="btn btn-secondary"
                      style={{ padding: '6px 10px' }}
                      aria-label="Nach oben"
                      onClick={() => setLayout((L) => moveWidget(L, i, -1))}
                    >
                      <ChevronUp size={18} />
                    </button>
                    <button
                      type="button"
                      className="btn btn-secondary"
                      style={{ padding: '6px 10px' }}
                      aria-label="Nach unten"
                      onClick={() => setLayout((L) => moveWidget(L, i, 1))}
                    >
                      <ChevronDown size={18} />
                    </button>
                  </div>
                </div>
                {w.id === 'quick_capture' && (
                  <QuickCaptureConfigEditor
                    config={w.config || {}}
                    onChange={(next) =>
                      setLayout((L) =>
                        normalizeLayoutForEditor({
                          ...L,
                          widgets: L.widgets.map((x, j) => {
                            if (j !== i) return x
                            const cfg = { ...(x.config || {}) }
                            for (const k of ['show_weight', 'show_resting_hr', 'show_hrv', 'show_vo2_max']) {
                              delete cfg[k]
                            }
                            Object.assign(cfg, next)
                            return { ...x, config: cfg }
                          }),
                        })
                      )
                    }
                  />
                )}
                {w.id === 'kpi_board' && (
                  <KpiBoardConfigEditor
                    tiles={Object.prototype.hasOwnProperty.call(w.config || {}, 'tiles') ? w.config.tiles : undefined}
                    onChange={(next) =>
                      setLayout((L) =>
                        normalizeLayoutForEditor({
                          ...L,
                          widgets: L.widgets.map((x, j) => {
                            if (j !== i) return x
                            const cfg = { ...(x.config || {}) }
                            if (next === undefined) {
                              delete cfg.tiles
                            } else {
                              cfg.tiles = next
                            }
                            return { ...x, config: cfg }
                          }),
                        })
                      )
                    }
                  />
                )}
                {CHART_DAYS_WIDGET_IDS.has(w.id) && (
                  <div style={{ marginTop: 10, marginLeft: 28 }}>
                    <label style={{ fontSize: 12, color: 'var(--text2)', display: 'block', marginBottom: 4 }}>
                      Zeitraum (Tage): {BODY_CHART_DAYS_MIN}–{BODY_CHART_DAYS_MAX}
                    </label>
                    <input
                      type="text"
                      inputMode="numeric"
                      autoComplete="off"
                      className="form-input"
                      style={{ maxWidth: 120 }}
                      value={
                        chartDaysDraftByWidgetId[w.id] !== undefined
                          ? chartDaysDraftByWidgetId[w.id]
                          : String(chartDaysVal)
                      }
                      onFocus={() =>
                        setChartDaysDraftByWidgetId((prev) => ({
                          ...prev,
                          [w.id]: String(chartDaysVal),
                        }))
                      }
                      onChange={(e) =>
                        setChartDaysDraftByWidgetId((prev) => ({
                          ...prev,
                          [w.id]: e.target.value,
                        }))
                      }
                      onBlur={(e) => {
                        const raw = e.target.value
                        setLayout((L) =>
                          normalizeLayoutForEditor(commitChartDaysDraftToLayout(raw, L, w.id))
                        )
                        setChartDaysDraftByWidgetId((prev) => {
                          const next = { ...prev }
                          delete next[w.id]
                          return next
                        })
                      }}
                      onKeyDown={(e) => {
                        if (e.key === 'Enter') e.currentTarget.blur()
                      }}
                    />
                  </div>
                )}
                {w.id === 'body_history_viz' && (
                  <BodyHistoryVizConfigEditor
                    config={w.config || {}}
                    onChange={(next) =>
                      setLayout((L) =>
                        normalizeLayoutForEditor({
                          ...L,
                          widgets: L.widgets.map((x, j) => {
                            if (j !== i) return x
                            if (Object.keys(next).length === 0) return { ...x, config: {} }
                            return { ...x, config: { ...(x.config || {}), ...next } }
                          }),
                        })
                      )
                    }
                  />
                )}
              </li>
            )
          })}
        </ul>
      </div>

      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
        <button type="button" className="btn btn-primary" disabled={busy} onClick={save}>
          Speichern
        </button>
        <button type="button" className="btn btn-secondary" disabled={busy} onClick={resetToSystem}>
          {adminMode ? 'Code-Standard wiederherstellen' : 'System-Standard wiederherstellen'}
        </button>
        <Link
          to={adminMode ? '/admin' : '/'}
          className="btn btn-secondary"
          style={{ textDecoration: 'none', textAlign: 'center' }}
        >
          {adminMode ? 'Admin-Übersicht' : 'Zur Übersicht'}
        </Link>
      </div>

      {addPanelOpen && (
        <div
          style={{
            position: 'fixed',
            inset: 0,
            zIndex: 300,
            display: 'flex',
            alignItems: viewportDesktop ? 'center' : 'flex-end',
            justifyContent: 'center',
            padding: viewportDesktop ? 16 : 0,
            paddingBottom: viewportDesktop ? 16 : undefined,
          }}
        >
          <button
            type="button"
            aria-label="Schließen"
            style={{
              position: 'absolute',
              inset: 0,
              border: 'none',
              padding: 0,
              margin: 0,
              background: 'rgba(0,0,0,0.45)',
              cursor: 'pointer',
            }}
            onClick={() => setAddPanelOpen(false)}
          />
          <div
            role="dialog"
            aria-modal="true"
            aria-labelledby="dashboard-add-widget-title"
            onClick={(e) => e.stopPropagation()}
            style={{
              position: 'relative',
              width: '100%',
              maxWidth: viewportDesktop ? 520 : '100%',
              maxHeight: viewportDesktop ? 'min(85vh, 640px)' : 'min(92vh, 100%)',
              background: 'var(--surface)',
              borderRadius: viewportDesktop ? 12 : '16px 16px 0 0',
              boxShadow: viewportDesktop ? '0 12px 40px rgba(0,0,0,0.2)' : '0 -4px 24px rgba(0,0,0,0.12)',
              display: 'flex',
              flexDirection: 'column',
              overflow: 'hidden',
              paddingBottom: 'max(12px, env(safe-area-inset-bottom))',
            }}
          >
            <div
              style={{
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'space-between',
                gap: 12,
                padding: '14px 16px',
                borderBottom: '1px solid var(--border)',
                flexShrink: 0,
              }}
            >
              <h2 id="dashboard-add-widget-title" className="card-title" style={{ fontSize: 16, margin: 0 }}>
                Kachel hinzufügen
              </h2>
              <button
                type="button"
                className="btn btn-secondary"
                style={{ padding: '8px 10px' }}
                aria-label="Schließen"
                onClick={() => setAddPanelOpen(false)}
              >
                <X size={20} />
              </button>
            </div>
            <div style={{ padding: '12px 16px', flexShrink: 0, borderBottom: '1px solid var(--border)' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <Search size={18} color="var(--text3)" />
                <input
                  ref={pickerSearchRef}
                  type="search"
                  className="form-input"
                  placeholder="Suchen nach Titel, Beschreibung, ID …"
                  value={pickerSearch}
                  onChange={(e) => setPickerSearch(e.target.value)}
                  aria-label="Widgets durchsuchen"
                  style={{ flex: 1 }}
                />
              </div>
            </div>
            <div style={{ overflowY: 'auto', flex: 1, padding: '8px 16px 16px' }}>
              {libraryIndices.length === 0 ? (
                <p style={{ fontSize: 13, color: 'var(--text3)', margin: 12 }}>
                  {pickerLower ? 'Keine Treffer.' : 'Keine weiteren Kacheln verfügbar.'}
                </p>
              ) : (
                <ul style={{ listStyle: 'none', padding: 0, margin: 0 }}>
                  {libraryIndices.map((i) => {
                    const w = layout.widgets[i]
                    const m = metaById[w.id]
                    return (
                      <li
                        key={w.id}
                        style={{
                          display: 'flex',
                          alignItems: 'center',
                          justifyContent: 'space-between',
                          gap: 12,
                          padding: '12px 0',
                          borderBottom: '1px solid var(--border)',
                        }}
                      >
                        <div style={{ minWidth: 0 }}>
                          <div style={{ fontSize: 14, fontWeight: 600 }}>{m?.title || w.id}</div>
                          <div style={{ fontSize: 12, color: 'var(--text3)', marginTop: 2 }}>{m?.description || ''}</div>
                        </div>
                        <button
                          type="button"
                          className="btn btn-primary"
                          style={{ flexShrink: 0, fontSize: 12, padding: '8px 14px' }}
                          onClick={() => {
                            setLayout((L) =>
                              normalizeLayoutForEditor({
                                ...L,
                                widgets: L.widgets.map((x, j) => (j === i ? { ...x, enabled: true } : x)),
                              })
                            )
                          }}
                        >
                          Hinzufügen
                        </button>
                      </li>
                    )
                  })}
                </ul>
              )}
            </div>
          </div>
        </div>
      )}
    </div>
  )
}
