import React, { useEffect, useRef, useState } from 'react';
import { TabView, TabPanel } from 'primereact/tabview';
import { Accordion, AccordionTab } from 'primereact/accordion';
import { InputText } from 'primereact/inputtext';
import { InputNumber } from 'primereact/inputnumber';
import { InputSwitch } from 'primereact/inputswitch';
import { Dropdown } from 'primereact/dropdown';
import { Checkbox } from 'primereact/checkbox';
import { Tag } from 'primereact/tag';
import { Button } from 'primereact/button';
import { api } from '../api/client';

function normalizeText(value) {
  return String(value || '').trim().toLowerCase();
}

function normalizeSettingKey(value) {
  return String(value || '').trim().toLowerCase();
}

const GENERAL_TOOL_KEYS = new Set([
  'makemkv_command',
  'makemkv_registration_key',
  'makemkv_min_length_minutes',
  'mediainfo_command',
  'handbrake_command',
  'ffmpeg_command',
  'ffprobe_command',
  'handbrake_restart_delete_incomplete_output',
  'script_test_timeout_ms'
]);

const HANDBRAKE_PRESET_SETTING_KEYS = new Set([
  'handbrake_preset',
  'handbrake_preset_bluray',
  'handbrake_preset_dvd'
]);

const NOTIFICATION_EVENT_TOGGLE_KEYS = new Set([
  'pushover_notify_metadata_ready',
  'pushover_notify_rip_started',
  'pushover_notify_encoding_started',
  'pushover_notify_job_finished',
  'pushover_notify_job_error',
  'pushover_notify_job_cancelled',
  'pushover_notify_reencode_started',
  'pushover_notify_reencode_finished'
]);

const PUSHOVER_ENABLED_SETTING_KEY = 'pushover_enabled';
const EXPERT_MODE_SETTING_KEY = 'ui_expert_mode';
const EXTERNAL_STORAGE_PATHS_KEY = 'external_storage_paths';
const ALWAYS_HIDDEN_SETTING_KEYS = new Set([
  'drive_device',
  'makemkv_source_index', // auto-derived from drive path — shown as read-only in drive info
  'makemkv_rip_mode',
  'makemkv_rip_mode_bluray',
  'makemkv_rip_mode_dvd',
  'makemkv_backup_mode'
]);
const EXPERT_ONLY_SETTING_KEYS = new Set([
  'pushover_device',
  'pushover_priority',
  'pushover_timeout_ms',
  'disc_poll_interval_ms',
  'hardware_monitoring_interval_ms',
  'makemkv_command',
  'mediainfo_command',
  'handbrake_command',
  'mediainfo_extra_args_bluray',
  'mediainfo_extra_args_dvd',
  'makemkv_analyze_extra_args_bluray',
  'makemkv_analyze_extra_args_dvd',
  'makemkv_rip_extra_args_bluray',
  'makemkv_rip_extra_args_dvd',
  'cdparanoia_command',
  'script_test_timeout_ms'
]);

function toBoolean(value) {
  if (typeof value === 'boolean') {
    return value;
  }
  if (typeof value === 'number') {
    return value !== 0;
  }
  const normalized = normalizeText(value);
  if (!normalized) {
    return false;
  }
  return normalized === 'true' || normalized === '1' || normalized === 'yes' || normalized === 'on';
}

function DriveDevicesEditor({ value, onChange, settingKey }) {
  const parseDevices = (val) => {
    try {
      const parsed = JSON.parse(val || '[]');
      if (!Array.isArray(parsed)) return [];
      const normalized = parsed.map((e) => {
        if (typeof e === 'string') {
          const p = e.trim();
          if (!p) return null;
          return { path: p, makemkvIndex: null };
        }
        if (e && typeof e === 'object') {
          const p = String(e.path || '').trim();
          if (!p) return null;
          const idxRaw = Number(e.makemkvIndex);
          return {
            path: p,
            makemkvIndex: Number.isFinite(idxRaw) && idxRaw >= 0 ? Math.trunc(idxRaw) : null
          };
        }
        return null;
      }).filter(Boolean);
      const used = new Set();
      let nextAutoIndex = 0;
      return normalized.map((entry) => {
        if (entry.makemkvIndex != null) {
          used.add(entry.makemkvIndex);
          if (entry.makemkvIndex >= nextAutoIndex) {
            nextAutoIndex = entry.makemkvIndex + 1;
          }
          return entry;
        }
        while (used.has(nextAutoIndex)) {
          nextAutoIndex += 1;
        }
        const resolved = { path: entry.path, makemkvIndex: nextAutoIndex };
        used.add(nextAutoIndex);
        nextAutoIndex += 1;
        return resolved;
      });
    } catch (_e) {
      return [];
    }
  };

  const devices = parseDevices(value);

  const updateDevices = (newDevices) => {
    onChange?.(settingKey, JSON.stringify(newDevices));
  };

  return (
    <div className="drive-devices-editor">
      {devices.length === 0 && (
        <p className="drive-devices-empty">Keine expliziten Laufwerke konfiguriert.</p>
      )}
      {devices.map((dev, idx) => (
        <div key={idx} className="drive-device-row">
          <InputText
            value={dev.path}
            placeholder="/dev/sr0"
            onChange={(e) => {
              const next = [...devices];
              const p = e.target.value;
              next[idx] = { ...next[idx], path: p };
              updateDevices(next);
            }}
            className="drive-device-input"
          />
          <InputNumber
            value={dev.makemkvIndex}
            min={0}
            max={9}
            showButtons={false}
            placeholder="Idx"
            title="MakeMKV disc index (disc:N)"
            style={{ width: '4rem' }}
            inputStyle={{ textAlign: 'center' }}
            onChange={(e) => {
              const next = [...devices];
              next[idx] = { ...next[idx], makemkvIndex: e.value ?? 0 };
              updateDevices(next);
            }}
          />
          <Button
            icon="pi pi-trash"
            severity="danger"
            text
            rounded
            size="small"
            onClick={() => updateDevices(devices.filter((_, i) => i !== idx))}
            type="button"
            aria-label="Laufwerk entfernen"
          />
        </div>
      ))}
      <Button
        icon="pi pi-plus"
        label="Laufwerk hinzufügen"
        severity="secondary"
        text
        size="small"
        onClick={() => {
          const nextIdx = devices.length;
          updateDevices([...devices, { path: '', makemkvIndex: nextIdx }]);
        }}
        type="button"
      />
    </div>
  );
}

function ExternalStoragePathsEditor({ value, onChange, settingKey }) {
  const parsePaths = (rawValue) => {
    try {
      const parsed = JSON.parse(rawValue || '[]');
      if (!Array.isArray(parsed)) return [];
      const normalized = [];
      for (const entry of parsed) {
        if (typeof entry === 'string') {
          normalized.push({
            name: '',
            path: String(entry || '')
          });
          continue;
        }
        if (entry && typeof entry === 'object') {
          normalized.push({
            name: String(entry.name || entry.label || ''),
            path: String(entry.path || '')
          });
        }
      }
      return normalized;
    } catch (_error) {
      return [];
    }
  };

  const entries = parsePaths(value);

  const updatePaths = (nextPaths) => {
    const normalized = (Array.isArray(nextPaths) ? nextPaths : [])
      .map((entry) => ({
        name: String(entry?.name || ''),
        path: String(entry?.path || '')
      }));
    onChange?.(settingKey, JSON.stringify(normalized));
  };

  return (
    <div className="external-storage-editor">
      {entries.length === 0 && (
        <p className="external-storage-empty">Keine externen Speicherpfade konfiguriert.</p>
      )}
      {entries.map((entry, idx) => (
        <div key={`external-storage-${idx}`} className="external-storage-row">
          <InputText
            value={entry.name}
            placeholder="Name (z.B. USB HDD)"
            onChange={(event) => {
              const next = [...entries];
              next[idx] = { ...next[idx], name: event.target.value };
              updatePaths(next);
            }}
            className="external-storage-name-input"
          />
          <InputText
            value={entry.path}
            placeholder="/mnt/external-disk"
            onChange={(event) => {
              const next = [...entries];
              next[idx] = { ...next[idx], path: event.target.value };
              updatePaths(next);
            }}
            className="external-storage-input"
          />
          <Button
            icon="pi pi-trash"
            severity="danger"
            text
            rounded
            size="small"
            onClick={() => updatePaths(entries.filter((_, entryIndex) => entryIndex !== idx))}
            type="button"
            aria-label="Externen Speicherpfad entfernen"
          />
        </div>
      ))}
      <Button
        icon="pi pi-plus"
        label="Pfad hinzufügen"
        severity="secondary"
        text
        size="small"
        onClick={() => updatePaths([...entries, { name: '', path: '' }])}
        type="button"
      />
    </div>
  );
}

function DetectedDrivesInfo({ expertModeEnabled = false, onNotify = null }) {
  const [drives, setDrives] = useState(null);
  const [loading, setLoading] = useState(false);
  const [unlocking, setUnlocking] = useState(false);

  const load = () => {
    setLoading(true);
    api.getDetectedDrives()
      .then((res) => setDrives(Array.isArray(res?.drives) ? res.drives : []))
      .catch(() => setDrives([]))
      .finally(() => setLoading(false));
  };

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

  const notify = (severity, summary, detail) => {
    if (typeof onNotify === 'function') {
      onNotify({ severity, summary, detail });
    }
  };

  const handleUnlockOne = async (devicePath) => {
    setUnlocking(true);
    try {
      await api.forceUnlockDrive({ devicePath });
      notify('success', 'Laufwerk freigegeben', `${devicePath} wurde entsperrt.`);
      load();
    } catch (error) {
      notify('error', 'Freigabe fehlgeschlagen', error?.message || 'Unbekannter Fehler');
    } finally {
      setUnlocking(false);
    }
  };

  const handleUnlockAll = async () => {
    setUnlocking(true);
    try {
      await api.forceUnlockDrive({ all: true });
      notify('success', 'Laufwerke freigegeben', 'Alle erkannten Laufwerke wurden entsperrt.');
      load();
    } catch (error) {
      notify('error', 'Freigabe fehlgeschlagen', error?.message || 'Unbekannter Fehler');
    } finally {
      setUnlocking(false);
    }
  };

  return (
    <div className="detected-drives-info">
      <div className="detected-drives-header">
        <span className="detected-drives-label">Erkannte optische Laufwerke</span>
        <div style={{ display: 'flex', alignItems: 'center', gap: '0.3rem' }}>
          {expertModeEnabled ? (
            <Button
              icon="pi pi-unlock"
              text
              rounded
              size="small"
              loading={unlocking}
              disabled={loading}
              onClick={handleUnlockAll}
              type="button"
              aria-label="Alle Laufwerke freigeben"
              title="Alle Laufwerke freigeben"
            />
          ) : null}
          <Button
            icon="pi pi-refresh"
            text
            rounded
            size="small"
            loading={loading}
            disabled={unlocking}
            onClick={load}
            type="button"
            aria-label="Laufwerke neu einlesen"
            title="Laufwerke neu einlesen"
          />
        </div>
      </div>
      {drives === null || loading
        ? <span className="detected-drives-empty">Wird geladen…</span>
        : drives.length === 0
          ? <span className="detected-drives-empty">Keine optischen Laufwerke erkannt.</span>
          : (
            <table className="detected-drives-table">
              <thead>
                <tr>
                  <th>Gerät</th>
                  <th>Modell</th>
                  <th>MakeMKV Index</th>
                  {expertModeEnabled ? <th>Freigabe</th> : null}
                </tr>
              </thead>
              <tbody>
                {drives.map((d) => (
                  <tr key={d.path}>
                    <td><code>{d.path}</code></td>
                    <td>{d.model || '—'}</td>
                    <td><Tag value={d.discArg ?? '—'} severity="info" /></td>
                    {expertModeEnabled ? (
                      <td>
                        <Button
                          label="Freigeben"
                          icon="pi pi-unlock"
                          size="small"
                          text
                          onClick={() => handleUnlockOne(d.path)}
                          loading={unlocking}
                          disabled={loading}
                        />
                      </td>
                    ) : null}
                  </tr>
                ))}
              </tbody>
            </table>
          )}
    </div>
  );
}

function shouldHideSettingByExpertMode(settingKey, expertModeEnabled) {
  const key = normalizeSettingKey(settingKey);
  if (!key) {
    return false;
  }
  if (ALWAYS_HIDDEN_SETTING_KEYS.has(key)) {
    return true;
  }
  if (key === EXPERT_MODE_SETTING_KEY) {
    return true;
  }
  return !expertModeEnabled && EXPERT_ONLY_SETTING_KEYS.has(key);
}

function filterSettingsByVisibility(settings, expertModeEnabled) {
  const list = Array.isArray(settings) ? settings : [];
  return list.filter((setting) => !shouldHideSettingByExpertMode(setting?.key, expertModeEnabled));
}

function buildToolSections(settings) {
  const list = Array.isArray(settings) ? settings : [];
  const generalBucket = {
    id: 'general',
    title: 'General',
    description: 'Gemeinsame Tool-Settings für alle Medien.',
    settings: []
  };
  const blurayBucket = {
    id: 'bluray',
    title: 'BluRay',
    description: 'Profil-spezifische Settings für Blu-ray.',
    settings: []
  };
  const dvdBucket = {
    id: 'dvd',
    title: 'DVD',
    description: 'Profil-spezifische Settings für DVD.',
    settings: []
  };
  const audiobookBucket = {
    id: 'audiobook',
    title: 'Audiobook',
    description: 'Profil-spezifische Settings für Audiobooks.',
    settings: []
  };
  const fallbackBucket = {
    id: 'other',
    title: 'Weitere Tool-Settings',
    description: null,
    settings: []
  };

  for (const setting of list) {
    const key = normalizeSettingKey(setting?.key);
    if (GENERAL_TOOL_KEYS.has(key)) {
      generalBucket.settings.push(setting);
      continue;
    }
    if (key.endsWith('_bluray')) {
      blurayBucket.settings.push(setting);
      continue;
    }
    if (key.endsWith('_dvd')) {
      dvdBucket.settings.push(setting);
      continue;
    }
    if (key.endsWith('_audiobook')) {
      audiobookBucket.settings.push(setting);
      continue;
    }
    fallbackBucket.settings.push(setting);
  }

  const sections = [
    generalBucket,
    blurayBucket,
    dvdBucket,
    audiobookBucket
  ].filter((item) => item.settings.length > 0);
  if (fallbackBucket.settings.length > 0) {
    sections.push(fallbackBucket);
  }
  return sections;
}

// Path keys per medium — _owner keys are rendered inline
const BLURAY_PATH_KEYS = ['raw_dir_bluray', 'movie_dir_bluray', 'output_template_bluray'];
const DVD_PATH_KEYS = [
  'raw_dir_dvd',
  'raw_dir_dvd_series',
  'movie_dir_dvd',
  'series_dir_dvd',
  'output_template_dvd',
  'output_template_dvd_series_episode',
  'output_template_dvd_series_multi_episode'
];
const CD_PATH_KEYS = ['raw_dir_cd', 'movie_dir_cd', 'cd_output_template'];
const AUDIOBOOK_PATH_KEYS = ['raw_dir_audiobook', 'movie_dir_audiobook', 'output_template_audiobook', 'output_chapter_template_audiobook', 'audiobook_raw_template'];
const DOWNLOAD_PATH_KEYS = ['download_dir'];
const LOG_PATH_KEYS = ['log_dir'];
const EXTERNAL_STORAGE_PATH_KEYS = [EXTERNAL_STORAGE_PATHS_KEY];
const CONVERTER_PATH_KEYS = ['converter_raw_dir', 'converter_movie_dir', 'converter_audio_dir', 'converter_output_template_video', 'converter_output_template_audio'];
const CONVERTER_SCAN_EXTENSION_KEY = 'converter_scan_extensions';
const CONVERTER_SCAN_EXTENSION_OPTIONS = [
  'mkv', 'mp4', 'm2ts', 'iso', 'avi', 'mov',
  'flac', 'mp3', 'wav', 'm4a', 'ogg', 'opus'
];

function parseConverterScanExtensions(value) {
  const tokens = String(value || '')
    .split(',')
    .map((item) => item.trim().toLowerCase())
    .filter(Boolean);
  const known = new Set(CONVERTER_SCAN_EXTENSION_OPTIONS);
  const seen = new Set();
  const selected = [];
  for (const token of tokens) {
    if (!known.has(token) || seen.has(token)) {
      continue;
    }
    seen.add(token);
    selected.push(token);
  }
  return selected;
}

function serializeConverterScanExtensions(values) {
  const list = Array.isArray(values) ? values : [];
  const known = new Set(CONVERTER_SCAN_EXTENSION_OPTIONS);
  const seen = new Set();
  const normalized = [];
  for (const item of list) {
    const ext = String(item || '').trim().toLowerCase();
    if (!known.has(ext) || seen.has(ext)) {
      continue;
    }
    seen.add(ext);
    normalized.push(ext);
  }
  return normalized.join(',');
}

function ConverterScanExtensionsEditor({ value, onChange, settingKey }) {
  const selected = parseConverterScanExtensions(value);
  const selectedSet = new Set(selected);

  const handleToggle = (extension) => {
    const ext = String(extension || '').trim().toLowerCase();
    if (!ext) {
      return;
    }
    let next = selected.filter((item) => item !== ext);
    if (!selectedSet.has(ext)) {
      next = [...selected, ext];
    }
    // Setting ist required/minLength: mindestens eine Endung aktiv lassen.
    if (next.length === 0) {
      return;
    }
    onChange?.(settingKey, serializeConverterScanExtensions(next));
  };

  return (
    <div className="converter-scan-extensions-grid">
      {CONVERTER_SCAN_EXTENSION_OPTIONS.map((extension) => (
        <label key={extension} htmlFor={`${settingKey}-${extension}`} className="converter-scan-extension-option">
          <Checkbox
            inputId={`${settingKey}-${extension}`}
            checked={selectedSet.has(extension)}
            onChange={() => handleToggle(extension)}
          />
          <span>.{extension}</span>
        </label>
      ))}
    </div>
  );
}

function buildSectionsForCategory(categoryName, settings) {
  const list = Array.isArray(settings) ? settings : [];
  const normalizedCategory = normalizeText(categoryName);
  if (normalizedCategory === 'tools') {
    const sections = buildToolSections(list);
    if (sections.length > 0) {
      return sections;
    }
  }
  return [
    {
      id: 'all',
      title: null,
      description: null,
      settings: list
    }
  ];
}

function isHandBrakePresetSetting(setting) {
  const key = String(setting?.key || '').trim().toLowerCase();
  return HANDBRAKE_PRESET_SETTING_KEYS.has(key);
}

function isNotificationEventToggleSetting(setting) {
  return setting?.type === 'boolean' && NOTIFICATION_EVENT_TOGGLE_KEYS.has(normalizeSettingKey(setting?.key));
}

function isReadonlySetting(setting) {
  try {
    // API gibt validation als parsed object zurück, nicht validation_json als String
    const v = setting?.validation;
    if (v && typeof v === 'object') return v.readonly === true;
    return JSON.parse(setting?.validation_json || '{}')?.readonly === true;
  } catch {
    return false;
  }
}

function SettingField({
  setting,
  value,
  error,
  dirty,
  ownerSetting,
  ownerValue,
  ownerError,
  ownerDirty,
  onChange,
  variant = 'default',
  disabled = false
}) {
  const ownerKey = ownerSetting?.key;
  const pathHasValue = Boolean(String(value ?? '').trim());
  const isNotificationToggleBox = variant === 'notification-toggle' && setting?.type === 'boolean';

  return (
    <div className={`setting-row${isNotificationToggleBox ? ' notification-toggle-box' : ''}`}>
      {isNotificationToggleBox ? (
        <div className="notification-toggle-head">
          <label htmlFor={setting.key}>
            {setting.label}
            {setting.required && <span className="required">*</span>}
          </label>
          <InputSwitch
            id={setting.key}
            checked={Boolean(value)}
            disabled={disabled}
            onChange={disabled ? undefined : (event) => onChange?.(setting.key, event.value)}
          />
        </div>
      ) : (
        <label htmlFor={setting.key} className={disabled ? 'setting-label--readonly' : undefined}>
          {setting.label}
          {setting.required && <span className="required">*</span>}
          {disabled ? <span className="setting-badge--coming-soon"> (bald)</span> : null}
        </label>
      )}

      {(setting.type === 'string' || setting.type === 'path')
      && setting.key !== 'drive_devices'
      && setting.key !== EXTERNAL_STORAGE_PATHS_KEY
      && setting.key !== CONVERTER_SCAN_EXTENSION_KEY ? (
        <InputText
          id={setting.key}
          value={value ?? ''}
          onChange={(event) => onChange?.(setting.key, event.target.value)}
        />
      ) : null}

      {setting.key === 'drive_devices' ? (
        <DriveDevicesEditor
          value={value}
          onChange={onChange}
          settingKey={setting.key}
        />
      ) : null}

      {setting.key === EXTERNAL_STORAGE_PATHS_KEY ? (
        <ExternalStoragePathsEditor
          value={value}
          onChange={onChange}
          settingKey={setting.key}
        />
      ) : null}

      {setting.key === CONVERTER_SCAN_EXTENSION_KEY ? (
        <ConverterScanExtensionsEditor
          value={value}
          onChange={onChange}
          settingKey={setting.key}
        />
      ) : null}

      {setting.type === 'number' ? (
        <InputNumber
          id={setting.key}
          value={value ?? 0}
          onValueChange={(event) => onChange?.(setting.key, event.value)}
          mode="decimal"
          useGrouping={false}
        />
      ) : null}

      {setting.type === 'boolean' && !isNotificationToggleBox ? (
        <InputSwitch
          id={setting.key}
          checked={Boolean(value)}
          disabled={disabled}
          onChange={disabled ? undefined : (event) => onChange?.(setting.key, event.value)}
        />
      ) : null}

      {setting.type === 'select' ? (
        <Dropdown
          id={setting.key}
          value={value}
          options={setting.options}
          optionLabel="label"
          optionValue="value"
          optionDisabled="disabled"
          onChange={(event) => onChange?.(setting.key, event.value)}
        />
      ) : null}

      <small className="setting-description">{setting.description || ''}</small>
      {isHandBrakePresetSetting(setting) ? (
        <small>
          Preset-Erklärung:{' '}
          <a
            href="https://handbrake.fr/docs/en/latest/technical/official-presets.html"
            target="_blank"
            rel="noreferrer"
          >
            HandBrake Official Presets
          </a>
        </small>
      ) : null}
      {error ? (
        <small className="error-text">{error}</small>
      ) : (
        <Tag
          value={dirty ? 'Ungespeichert' : 'Gespeichert'}
          severity={dirty ? 'warning' : 'success'}
          className="saved-tag"
        />
      )}

      {ownerSetting ? (
        <div className="setting-owner-row">
          <label htmlFor={ownerKey} className="setting-owner-label">
            Eigentümer (user:gruppe)
          </label>
          <InputText
            id={ownerKey}
            value={ownerValue ?? ''}
            placeholder="z.B. michael:ripster"
            disabled={!pathHasValue}
            onChange={(event) => onChange?.(ownerKey, event.target.value)}
          />
          {ownerError ? (
            <small className="error-text">{ownerError}</small>
          ) : (
            <Tag
              value={ownerDirty ? 'Ungespeichert' : 'Gespeichert'}
              severity={ownerDirty ? 'warning' : 'success'}
              className="saved-tag"
            />
          )}
        </div>
      ) : null}
    </div>
  );
}


function PathCategoryTab({ settings, values, errors, dirtyKeys, onChange, effectivePaths }) {
  const list = Array.isArray(settings) ? settings : [];
  const settingsByKey = new Map(list.map((s) => [s.key, s]));

  const bluraySettings = list.filter((s) => BLURAY_PATH_KEYS.includes(s.key) || (s.key.endsWith('_owner') && BLURAY_PATH_KEYS.includes(s.key.replace('_owner', ''))));
  const dvdSettings = list.filter((s) => DVD_PATH_KEYS.includes(s.key) || (s.key.endsWith('_owner') && DVD_PATH_KEYS.includes(s.key.replace('_owner', ''))));
  const cdSettings = list.filter((s) => CD_PATH_KEYS.includes(s.key) || (s.key.endsWith('_owner') && CD_PATH_KEYS.includes(s.key.replace('_owner', ''))));
  const audiobookSettings = list.filter((s) => AUDIOBOOK_PATH_KEYS.includes(s.key) || (s.key.endsWith('_owner') && AUDIOBOOK_PATH_KEYS.includes(s.key.replace('_owner', ''))));
  const downloadSettings = list.filter((s) => DOWNLOAD_PATH_KEYS.includes(s.key) || (s.key.endsWith('_owner') && DOWNLOAD_PATH_KEYS.includes(s.key.replace('_owner', ''))));
  const logSettings = list.filter((s) => LOG_PATH_KEYS.includes(s.key));
  const externalStorageSettings = list.filter((s) => EXTERNAL_STORAGE_PATH_KEYS.includes(s.key));
  const converterSettings = list.filter((s) => CONVERTER_PATH_KEYS.includes(s.key));

  const defaultRaw = effectivePaths?.defaults?.raw || 'data/output/raw';
  const defaultMovies = effectivePaths?.defaults?.movies || 'data/output/movies';
  const defaultSeries = effectivePaths?.defaults?.series || 'data/output/series';
  const defaultCd = effectivePaths?.defaults?.cd || 'data/output/cd';
  const defaultAudiobookRaw = effectivePaths?.defaults?.audiobookRaw || 'data/output/audiobook-raw';
  const defaultAudiobookMovies = effectivePaths?.defaults?.audiobookMovies || 'data/output/audiobooks';
  const defaultDownloads = effectivePaths?.defaults?.downloads || 'data/downloads';

  const defaultConverterRaw = effectivePaths?.defaults?.converterRaw || 'data/output/converter-raw';
  const defaultConverterMovies = effectivePaths?.defaults?.converterMovies || 'data/output/converted-movies';
  const defaultConverterAudio = effectivePaths?.defaults?.converterAudio || 'data/output/converted-audio';

  const ep = effectivePaths || {};
  const blurayRaw = ep.bluray?.raw || defaultRaw;
  const blurayMovies = ep.bluray?.movies || defaultMovies;
  const dvdRaw = ep.dvd?.raw || defaultRaw;
  const dvdSeriesRaw = ep.dvd?.seriesRaw || dvdRaw;
  const dvdMovies = ep.dvd?.movies || defaultMovies;
  const dvdSeries = ep.dvd?.series || defaultSeries;
  const cdRaw = ep.cd?.raw || defaultCd;
  const cdMovies = ep.cd?.movies || cdRaw;
  const audiobookRaw = ep.audiobook?.raw || defaultAudiobookRaw;
  const audiobookMovies = ep.audiobook?.movies || defaultAudiobookMovies;
  const downloadPath = ep.downloads?.path || defaultDownloads;
  const converterRaw = ep.converter?.raw || defaultConverterRaw;
  const converterMovies = ep.converter?.movies || defaultConverterMovies;
  const converterAudio = ep.converter?.audio || defaultConverterAudio;

  const isDefault = (path, def) => path === def;

  return (
    <div className="path-category-tab">
      {/* Effektive Pfade Übersicht */}
      <div className="path-overview-card">
        <div className="path-overview-header">
          <h4>Effektive Pfade</h4>
          <small>Zeigt die tatsächlich verwendeten Pfade entsprechend der aktuellen Konfiguration.</small>
        </div>
        <table className="path-overview-table">
          <thead>
            <tr>
              <th>Medium</th>
              <th>RAW / Eingang</th>
              <th>Encode / Ausgang</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td><strong>Blu-ray</strong></td>
              <td>
                <code>{blurayRaw}</code>
                {isDefault(blurayRaw, defaultRaw) && <span className="path-default-badge">Standard</span>}
              </td>
              <td>
                <code>{blurayMovies}</code>
                {isDefault(blurayMovies, defaultMovies) && <span className="path-default-badge">Standard</span>}
              </td>
            </tr>
            <tr>
              <td><strong>DVD</strong></td>
              <td>
                <code>{dvdRaw}</code>
                {isDefault(dvdRaw, defaultRaw) && <span className="path-default-badge">Standard</span>}
              </td>
              <td>
                <code>{dvdMovies}</code>
                {isDefault(dvdMovies, defaultMovies) && <span className="path-default-badge">Standard</span>}
              </td>
            </tr>
            <tr>
              <td><strong>DVD Serie</strong></td>
              <td>
                <code>{dvdSeriesRaw}</code>
                {isDefault(dvdSeriesRaw, dvdRaw) && <span className="path-default-badge">wie DVD RAW</span>}
              </td>
              <td>
                <code>{dvdSeries}</code>
                {isDefault(dvdSeries, defaultSeries) && <span className="path-default-badge">Standard</span>}
              </td>
            </tr>
            <tr>
              <td><strong>CD / Audio</strong></td>
              <td>
                <code>{cdRaw}</code>
                {isDefault(cdRaw, defaultCd) && <span className="path-default-badge">Standard</span>}
              </td>
              <td>
                <code>{cdMovies}</code>
                {isDefault(cdMovies, cdRaw) && <span className="path-default-badge">Standard</span>}
              </td>
            </tr>
            <tr>
              <td><strong>Audiobook</strong></td>
              <td>
                <code>{audiobookRaw}</code>
                {isDefault(audiobookRaw, defaultAudiobookRaw) && <span className="path-default-badge">Standard</span>}
              </td>
              <td>
                <code>{audiobookMovies}</code>
                {isDefault(audiobookMovies, defaultAudiobookMovies) && <span className="path-default-badge">Standard</span>}
              </td>
            </tr>
            <tr>
              <td><strong>Zip-Downloads</strong></td>
              <td>
                <code>{downloadPath}</code>
                {isDefault(downloadPath, defaultDownloads) && <span className="path-default-badge">Standard</span>}
              </td>
              <td>---</td>
            </tr>
            <tr>
              <td><strong>Converter Raw</strong></td>
              <td>
                <code>{converterRaw}</code>
                {isDefault(converterRaw, defaultConverterRaw) && <span className="path-default-badge">Standard</span>}
              </td>
              <td>---</td>
            </tr>
            <tr>
              <td><strong>Converter Video</strong></td>
              <td>---</td>
              <td>
                <code>{converterMovies}</code>
                {isDefault(converterMovies, defaultConverterMovies) && <span className="path-default-badge">Standard</span>}
              </td>
            </tr>
            <tr>
              <td><strong>Converter Audio</strong></td>
              <td>---</td>
              <td>
                <code>{converterAudio}</code>
                {isDefault(converterAudio, defaultConverterAudio) && <span className="path-default-badge">Standard</span>}
              </td>
            </tr>
          </tbody>
        </table>
      </div>

      {/* Medium-Karten als Accordion */}
      <Accordion className="path-medium-accordion">
        {[
          { title: 'Blu-ray', pathSettings: bluraySettings },
          { title: 'DVD', pathSettings: dvdSettings },
          { title: 'CD / Audio', pathSettings: cdSettings },
          { title: 'Audiobook', pathSettings: audiobookSettings },
          { title: 'Converter', pathSettings: converterSettings },
          { title: 'Downloads', pathSettings: downloadSettings },
          ...(externalStorageSettings.length > 0 ? [{ title: 'Externe Speicher', pathSettings: externalStorageSettings }] : []),
          ...(logSettings.length > 0 ? [{ title: 'Logs', pathSettings: logSettings }] : [])
        ]
          .filter(({ pathSettings }) =>
            pathSettings.filter((s) => !String(s?.key || '').endsWith('_owner')).length > 0
          )
          .map(({ title, pathSettings }) => {
            const visibleSettings = pathSettings.filter(
              (s) => !String(s?.key || '').endsWith('_owner')
            );
            return (
              <AccordionTab key={title} header={title}>
                <div className="settings-grid">
                  {visibleSettings.map((setting) => {
                    const value = values?.[setting.key];
                    const error = errors?.[setting.key] || null;
                    const dirty = Boolean(dirtyKeys?.has?.(setting.key));
                    const ownerKey = `${setting.key}_owner`;
                    const ownerSetting = settingsByKey.get(ownerKey) || null;
                    const ownerValue = values?.[ownerKey];
                    const ownerError = errors?.[ownerKey] || null;
                    const ownerDirty = Boolean(dirtyKeys?.has?.(ownerKey));
                    return (
                      <SettingField
                        key={setting.key}
                        setting={setting}
                        value={value}
                        error={error}
                        dirty={dirty}
                        ownerSetting={ownerSetting}
                        ownerValue={ownerValue}
                        ownerError={ownerError}
                        ownerDirty={ownerDirty}
                        onChange={onChange}
                      />
                    );
                  })}
                </div>
              </AccordionTab>
            );
          })}
      </Accordion>
    </div>
  );
}

export default function DynamicSettingsForm({
  categories,
  values,
  errors,
  dirtyKeys,
  onChange,
  effectivePaths,
  onNotify
}) {
  const safeCategories = Array.isArray(categories) ? categories : [];
  const expertModeEnabled = toBoolean(values?.[EXPERT_MODE_SETTING_KEY]);
  const visibleCategories = safeCategories
    .map((category) => ({
      ...category,
      settings: filterSettingsByVisibility(category?.settings, expertModeEnabled)
    }))
    .filter((category) => Array.isArray(category?.settings) && category.settings.length > 0);
  const [activeIndex, setActiveIndex] = useState(0);
  const rootRef = useRef(null);

  useEffect(() => {
    if (visibleCategories.length === 0) {
      setActiveIndex(0);
      return;
    }
    if (activeIndex < 0 || activeIndex >= visibleCategories.length) {
      setActiveIndex(0);
    }
  }, [activeIndex, visibleCategories.length]);

  useEffect(() => {
    if (typeof window === 'undefined') {
      return undefined;
    }
    const syncToggleHeights = () => {
      const root = rootRef.current;
      if (!root) {
        return;
      }
      const grids = root.querySelectorAll('.notification-toggle-grid');
      for (const grid of grids) {
        const cards = Array.from(grid.querySelectorAll('.notification-toggle-box'));
        if (cards.length === 0) {
          continue;
        }
        for (const card of cards) {
          card.style.minHeight = '0px';
        }
        const maxHeight = cards.reduce((acc, card) => Math.max(acc, Number(card.offsetHeight || 0)), 0);
        if (maxHeight <= 0) {
          continue;
        }
        for (const card of cards) {
          card.style.minHeight = `${maxHeight}px`;
        }
      }
    };

    const frameId = window.requestAnimationFrame(syncToggleHeights);
    window.addEventListener('resize', syncToggleHeights);
    return () => {
      window.cancelAnimationFrame(frameId);
      window.removeEventListener('resize', syncToggleHeights);
    };
  }, [activeIndex, visibleCategories, values]);

  if (visibleCategories.length === 0) {
    return <p>Keine Kategorien vorhanden.</p>;
  }

  return (
    <div className="dynamic-settings-form" ref={rootRef}>
      <TabView
        className="settings-tabview"
        activeIndex={activeIndex}
        onTabChange={(event) => setActiveIndex(Number(event.index || 0))}
        scrollable
      >
        {visibleCategories.map((category, categoryIndex) => (
          <TabPanel
            key={`${category.category || 'category'}-${categoryIndex}`}
            header={category.category || `Kategorie ${categoryIndex + 1}`}
          >
            {normalizeText(category?.category) === 'pfade' ? (
              <PathCategoryTab
                settings={category?.settings || []}
                values={values}
                errors={errors}
                dirtyKeys={dirtyKeys}
                onChange={onChange}
                effectivePaths={effectivePaths}
              />
            ) : (() => {
              const sections = buildSectionsForCategory(category?.category, category?.settings || []);
              const grouped = sections.length > 1;
              const isNotificationCategory = normalizeText(category?.category) === 'benachrichtigungen';
              const pushoverEnabled = toBoolean(values?.[PUSHOVER_ENABLED_SETTING_KEY]);

              const isDriveCategory = normalizeText(category?.category) === 'laufwerk';
              return (
                <div className="settings-sections">
                  {isDriveCategory && <DetectedDrivesInfo expertModeEnabled={expertModeEnabled} onNotify={onNotify} />}
                  {sections.map((section) => (
                    <section
                      key={`${category?.category || 'category'}-${section.id}`}
                      className={`settings-section${grouped ? ' grouped' : ''}`}
                    >
                      {section.title ? (
                        <div className="settings-section-head">
                          <h4>{section.title}</h4>
                          {section.description ? <small>{section.description}</small> : null}
                        </div>
                      ) : null}
                      {(() => {
                        const ownerKeySet = new Set(
                          (section.settings || [])
                            .filter((s) => String(s.key || '').endsWith('_owner'))
                            .map((s) => s.key)
                        );
                        const settingsByKey = new Map(
                          (section.settings || []).map((s) => [s.key, s])
                        );
                        const baseSettings = (section.settings || []).filter(
                          (s) => !ownerKeySet.has(s.key)
                        );
                        const notificationToggleSettings = isNotificationCategory
                          ? baseSettings.filter((setting) => isNotificationEventToggleSetting(setting))
                          : [];
                        const notificationToggleKeys = new Set(
                          notificationToggleSettings.map((setting) => normalizeSettingKey(setting?.key))
                        );

                        // Abhängigkeitsbaum aufbauen: depends_on → Kinder
                        const dependentsByParent = new Map();
                        const rootSettings = [];
                        for (const setting of baseSettings) {
                          if (notificationToggleKeys.has(normalizeSettingKey(setting?.key))) continue;
                          const dep = setting?.depends_on;
                          if (dep) {
                            if (!dependentsByParent.has(dep)) dependentsByParent.set(dep, []);
                            dependentsByParent.get(dep).push(setting);
                          } else {
                            rootSettings.push(setting);
                          }
                        }

                        const renderSetting = (setting, variant = 'default', disabled = false, onChangeFn = onChange) => {
                          const value = values?.[setting.key];
                          const error = errors?.[setting.key] || null;
                          const dirty = Boolean(dirtyKeys?.has?.(setting.key));

                          const ownerKey = `${setting.key}_owner`;
                          const ownerSetting = settingsByKey.get(ownerKey) || null;
                          const ownerValue = values?.[ownerKey];
                          const ownerError = errors?.[ownerKey] || null;
                          const ownerDirty = Boolean(dirtyKeys?.has?.(ownerKey));

                          return (
                            <SettingField
                              key={setting.key}
                              setting={setting}
                              value={value}
                              error={error}
                              dirty={dirty}
                              ownerSetting={ownerSetting}
                              ownerValue={ownerValue}
                              ownerError={ownerError}
                              ownerDirty={ownerDirty}
                              onChange={onChangeFn}
                              variant={variant}
                              disabled={disabled}
                            />
                          );
                        };

                        // Alle Nachkommen eines Keys rekursiv auf false setzen
                        const cascadeOff = (key) => {
                          const children = dependentsByParent.get(key) || [];
                          for (const child of children) {
                            onChange?.(child.key, false);
                            cascadeOff(child.key);
                          }
                        };

                        // Rekursiv: Setting + seine Kinder rendern
                        // Wenn ein Parent auf false geht → alle Kinder werden auf false gesetzt
                        const renderWithChildren = (setting, depth = 0) => {
                          const children = dependentsByParent.get(setting.key) || [];
                          const readonly = depth > 0 && isReadonlySetting(setting);
                          const active = toBoolean(values?.[setting.key]);

                          const effectiveOnChange = children.length > 0
                            ? (key, value) => {
                                onChange?.(key, value);
                                if (!value) cascadeOff(key);
                              }
                            : onChange;

                          return (
                            <React.Fragment key={setting.key}>
                              {renderSetting(setting, 'default', readonly, effectiveOnChange)}
                              {children.length > 0 && active && !readonly ? (
                                <div className={`settings-grid settings-grid--depth-${depth + 1}`}>
                                  {children.map((child) => renderWithChildren(child, depth + 1))}
                                </div>
                              ) : null}
                            </React.Fragment>
                          );
                        };

                        return (
                          <>
                            {rootSettings.length > 0 ? (
                              <div className="settings-grid">
                                {rootSettings.map((setting) => renderWithChildren(setting, 0))}
                              </div>
                            ) : null}
                            {pushoverEnabled && notificationToggleSettings.length > 0 ? (
                              <div className="notification-toggle-grid">
                                {notificationToggleSettings.map((setting) => renderSetting(setting, 'notification-toggle'))}
                              </div>
                            ) : null}
                          </>
                        );
                      })()}
                    </section>
                  ))}
                </div>
              );
            })()}
          </TabPanel>
        ))}
      </TabView>
    </div>
  );
}
