import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Activity, Settings, Radio, Layers, Wifi, Send, FileText, Filter, Trash2 } from 'lucide-react';
import { TopicTree } from '../components/TopicTree';
import { TopicDetails } from '../components/TopicDetails';
import { PublishPanel } from '../components/PublishPanel';
import { SettingsPanel } from '../components/SettingsPanel';
import { GiteaIcon } from '../components/GiteaIcon';
import { TopicNode, MQTTMessage } from '../types';
import { INITIAL_SETTINGS } from '../constants';
import { clearAllHistory, getHistory, getMetrics, getSettings, getStats, getTopics, getSysinfo, saveSettings, setFilters } from '../utils/api';
import { initTheme, setTheme } from '../utils/theme';
import { formatBytes } from '../utils/format';

type NumericField = { path: string; label: string };

const RAW_FIELD = '__raw__';

const isNumericString = (value: string) => value.trim() !== '' && !Number.isNaN(Number(value));

const parsePath = (path: string): (string | number)[] => {
  const tokens: (string | number)[] = [];
  path.replace(/([^[.\]]+)|\[(\d+)\]/g, (_match, key, index) => {
    if (index !== undefined) {
      tokens.push(Number(index));
    } else {
      tokens.push(key);
    }
    return '';
  });
  return tokens;
};

const extractNumericFields = (payload: string): NumericField[] => {
  const out: NumericField[] = [];
  try {
    const parsed = JSON.parse(payload);
    if (typeof parsed === 'number' || (typeof parsed === 'string' && isNumericString(parsed))) {
      return [{ path: RAW_FIELD, label: 'value' }];
    }
    const walk = (value: unknown, path: string) => {
      if (typeof value === 'number' && Number.isFinite(value)) {
        out.push({ path, label: path });
        return;
      }
      if (typeof value === 'string' && isNumericString(value)) {
        out.push({ path, label: path });
        return;
      }
      if (Array.isArray(value)) {
        value.forEach((entry, index) => {
          const nextPath = `${path}[${index}]`;
          walk(entry, nextPath);
        });
        return;
      }
      if (value && typeof value === 'object') {
        Object.entries(value as Record<string, unknown>).forEach(([key, entry]) => {
          const nextPath = path ? `${path}.${key}` : key;
          walk(entry, nextPath);
        });
      }
    };
    if (parsed && typeof parsed === 'object') {
      walk(parsed, '');
    }
  } catch {
    if (isNumericString(payload)) {
      return [{ path: RAW_FIELD, label: 'value' }];
    }
  }
  const deduped = new Map<string, NumericField>();
  out.forEach((field) => {
    if (field.path) deduped.set(field.path, field);
  });
  return Array.from(deduped.values());
};

const extractNumericValue = (payload: string, path: string): number | undefined => {
  if (path === RAW_FIELD) {
    if (!isNumericString(payload)) return undefined;
    return Number(payload);
  }
  try {
    const parsed = JSON.parse(payload);
    const tokens = parsePath(path);
    let current: unknown = parsed;
    for (const token of tokens) {
      if (current === null || current === undefined) {
        return undefined;
      }
      current = (current as Record<string, unknown>)[token as keyof Record<string, unknown>];
    }
    if (typeof current === 'number' && Number.isFinite(current)) {
      return current;
    }
    if (typeof current === 'string' && isNumericString(current)) {
      return Number(current);
    }
  } catch {
    return undefined;
  }
  return undefined;
};

const hydrateTree = (node: TopicNode): TopicNode => ({
  ...node,
  isExpanded: node.isExpanded ?? false,
  children: node.children ? node.children.map(hydrateTree) : []
});

const applyInitialExpansion = (node: TopicNode, expandAll: boolean): TopicNode => ({
  ...node,
  isExpanded: node.fullName === '' ? true : expandAll,
  children: node.children ? node.children.map((child) => applyInitialExpansion(child, expandAll)) : []
});

const cloneTree = (node: TopicNode): TopicNode => ({
  ...node,
  children: node.children.map(cloneTree)
});

const insertMessage = (root: TopicNode, msg: MQTTMessage): TopicNode => {
  const nextRoot = cloneTree(root);
  const parts = msg.topic.split('/');
  let current = nextRoot;

  parts.forEach((part, index) => {
    let child = current.children.find((entry) => entry.name === part);
    if (!child) {
      const fullName = parts.slice(0, index + 1).join('/');
      child = {
        name: part,
        fullName,
        children: [],
        messageCount: 0,
        isExpanded: false
      };
      current.children.push(child);
    }
    child.messageCount += 1;
    if (index === parts.length - 1) {
      child.lastMessage = msg;
    }
    current = child;
  });

  return nextRoot;
};

const App: React.FC = () => {
  const [root, setRoot] = useState<TopicNode>({
    name: 'root',
    fullName: '',
    children: [],
    messageCount: 0,
    isExpanded: true
  });
  const [treeInitialized, setTreeInitialized] = useState(false);

  const [selectedTopic, setSelectedTopic] = useState<string | null>(null);
  const [lastMessages, setLastMessages] = useState<Map<string, MQTTMessage>>(new Map());
  const [previousMessages, setPreviousMessages] = useState<Map<string, MQTTMessage>>(new Map());
  const [connected, setConnected] = useState(false);
  const [dbStats, setDbStats] = useState({ count: 0, size: '0 MB' });
  const [settings, setSettings] = useState(INITIAL_SETTINGS);
  const [activeView, setActiveView] = useState<'topics' | 'details' | 'publish' | 'settings'>('topics');
  const [seriesData, setSeriesData] = useState<Record<string, { times: number[]; values: number[] }>>({});
  const [dbSeriesData, setDbSeriesData] = useState<Record<string, { times: number[]; values: number[] }>>({});
  const [chartSource, setChartSource] = useState<'live' | 'db'>('live');
  const [recentTopics, setRecentTopics] = useState<Record<string, number>>({});
  const [sidebarWidth, setSidebarWidth] = useState(320);
  const [resizing, setResizing] = useState(false);
  const [settingsWidth, setSettingsWidth] = useState(360);
  const [resizingSettings, setResizingSettings] = useState(false);
  const [publishWidth, setPublishWidth] = useState(360);
  const [resizingPublish, setResizingPublish] = useState(false);
  const [publishOpen, setPublishOpen] = useState(false);
  const [metrics, setMetrics] = useState({ cpuPercent: 0, memBytes: 0, memLimit: 0, dbBytes: 0, dbSize: '0 B' });
  const [sysinfo, setSysinfo] = useState({ version: '', clients: '-', msgReceived: '-', msgSent: '-', msgStored: '-', subscriptions: '-' });
  const [publishDraft, setPublishDraft] = useState({ topic: '', payload: '' });
  const [chartFieldsByTopic, setChartFieldsByTopic] = useState<Record<string, NumericField[]>>({});
  const [chartFieldByTopic, setChartFieldByTopic] = useState<Record<string, string>>({});
  const chartFieldRef = useRef<Record<string, string>>({});
  const defaultProfile = settings.mqttProfiles.find((profile) => profile.isDefault);
  const activeProfile = settings.mqttProfiles.find((profile) => profile.id === settings.activeProfileId) || defaultProfile;
  useEffect(() => {
    const currentTheme = initTheme();
    getSettings()
      .then((remote) => {
        const merged = { ...INITIAL_SETTINGS, ...remote, theme: remote.theme || currentTheme };
        setSettings(merged);
        setTheme(merged.theme);
      })
      .catch(() => {
        setSettings((prev) => ({ ...prev, theme: currentTheme }));
      });
  }, []);

  useEffect(() => {
    setTheme(settings.theme);
  }, [settings.theme]);

  useEffect(() => {
    chartFieldRef.current = chartFieldByTopic;
  }, [chartFieldByTopic]);

  const updateStats = useCallback((stats: { count: number; size: string }) => {
    setDbStats(stats);
  }, []);

  const refreshStats = useCallback(async () => {
    try {
      const stats = await getStats();
      setDbStats(stats);
    } catch {
      // stats optionnelles
    }
  }, []);

  useEffect(() => {
    if (settings.statsRefreshMs <= 0) return;
    const interval = setInterval(refreshStats, settings.statsRefreshMs);
    return () => clearInterval(interval);
  }, [refreshStats, settings.statsRefreshMs]);

  useEffect(() => {
    getTopics()
      .then((snapshot) => setRoot(hydrateTree(snapshot)))
      .catch(() => null);
    refreshStats();
  }, [refreshStats]);

  useEffect(() => {
    if (treeInitialized) return;
    if (root.children.length === 0) return;
    setRoot((prev) => applyInitialExpansion(prev, settings.expandTreeOnStart));
    setTreeInitialized(true);
  }, [root.children.length, settings.expandTreeOnStart, treeInitialized]);

  useEffect(() => {
    setFilters(settings.topicFilters).catch(() => null);
  }, [settings.topicFilters]);

  useEffect(() => {
    const handle = setTimeout(() => {
      saveSettings(settings).catch(() => null);
    }, 400);
    return () => clearTimeout(handle);
  }, [settings]);

  useEffect(() => {
    let active = true;
    const load = async () => {
      try {
        const next = await getMetrics();
        if (active) setMetrics(next);
      } catch {
        // métriques optionnelles
      }
    };
    load();
    const interval = setInterval(load, settings.statsRefreshMs);
    return () => {
      active = false;
      clearInterval(interval);
    };
  }, [settings.statsRefreshMs]);

  useEffect(() => {
    let active = true;
    const load = async () => {
      try {
        const next = await getSysinfo();
        if (active) setSysinfo(next);
      } catch {
        // sysinfo optionnelles
      }
    };
    load();
    const interval = setInterval(load, settings.statsRefreshMs);
    return () => {
      active = false;
      clearInterval(interval);
    };
  }, [settings.statsRefreshMs]);

  useEffect(() => {
    const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
    const wsUrl = `${protocol}://${window.location.host}/ws/events`;
    const socket = new WebSocket(wsUrl);

    socket.onopen = () => setConnected(true);
    socket.onclose = () => setConnected(false);
    socket.onerror = () => setConnected(false);

    socket.onmessage = (event) => {
      const data = JSON.parse(event.data);
      if (data.type === 'message') {
        const msg = data.data as MQTTMessage;
        const now = Date.now();
        setRoot((prevRoot) => insertMessage(prevRoot, msg));
        setLastMessages((prev) => {
          const next = new Map(prev);
          const previous = next.get(msg.topic);
          if (previous) {
            setPreviousMessages((prevPrev) => {
              const nextPrev = new Map(prevPrev);
              nextPrev.set(msg.topic, previous);
              return nextPrev;
            });
          }
          next.set(msg.topic, msg);
          return next;
        });
        const fields = extractNumericFields(msg.payload);
        setChartFieldsByTopic((prev) => ({ ...prev, [msg.topic]: fields }));
        let selectedPath = chartFieldRef.current[msg.topic];
        if (!selectedPath || !fields.some((field) => field.path === selectedPath)) {
          if (fields.length === 1) {
            selectedPath = fields[0].path;
            setChartFieldByTopic((prev) => ({ ...prev, [msg.topic]: selectedPath as string }));
          } else {
            selectedPath = undefined;
          }
        }
        if (selectedPath) {
          const numeric = extractNumericValue(msg.payload, selectedPath);
          if (numeric !== undefined) {
            const seriesKey = `${msg.topic}::${selectedPath}`;
            setSeriesData((prevSeries) => {
              const next = { ...prevSeries };
              const current = next[seriesKey] || { times: [], values: [] };
              const nextTimes = [...current.times, Date.now() / 1000];
              const nextValues = [...current.values, numeric];
              const cap = 200;
              next[seriesKey] = {
                times: nextTimes.slice(-cap),
                values: nextValues.slice(-cap)
              };
              return next;
            });
          }
        }
        setRecentTopics((prev) => {
          const next = { ...prev };
          const parts = msg.topic.split('/');
          let current = '';
          parts.forEach((part) => {
            current = current ? `${current}/${part}` : part;
            next[current] = now;
          });
          return next;
        });
      }
      if (data.type === 'stats') {
        updateStats(data.data);
      }
    };

    return () => socket.close();
  }, [updateStats]);

  useEffect(() => {
    if (Object.keys(recentTopics).length === 0) return;
    const timeout = setTimeout(() => {
      const cutoff = Date.now() - settings.highlightMs;
      setRecentTopics((prev) => {
        const next: Record<string, number> = {};
        Object.entries(prev).forEach(([topic, ts]) => {
          if (ts >= cutoff) next[topic] = ts;
        });
        return next;
      });
    }, Math.max(30, Math.min(settings.highlightMs / 2, 120)));
    return () => clearTimeout(timeout);
  }, [recentTopics, settings.highlightMs]);

  useEffect(() => {
    if (!resizing) return;
    const handleMove = (event: MouseEvent) => {
      const nextWidth = Math.min(520, Math.max(220, event.clientX));
      setSidebarWidth(nextWidth);
    };
    const stopResize = () => setResizing(false);
    window.addEventListener('mousemove', handleMove);
    window.addEventListener('mouseup', stopResize);
    return () => {
      window.removeEventListener('mousemove', handleMove);
      window.removeEventListener('mouseup', stopResize);
    };
  }, [resizing]);

  useEffect(() => {
    if (!resizingSettings) return;
    const handleMove = (event: MouseEvent) => {
      const viewport = window.innerWidth;
      const nextWidth = Math.min(520, Math.max(260, viewport - event.clientX));
      setSettingsWidth(nextWidth);
    };
    const stopResize = () => setResizingSettings(false);
    window.addEventListener('mousemove', handleMove);
    window.addEventListener('mouseup', stopResize);
    return () => {
      window.removeEventListener('mousemove', handleMove);
      window.removeEventListener('mouseup', stopResize);
    };
  }, [resizingSettings]);

  useEffect(() => {
    if (!resizingPublish) return;
    const handleMove = (event: MouseEvent) => {
      const viewport = window.innerWidth;
      const nextWidth = Math.min(520, Math.max(260, viewport - event.clientX));
      setPublishWidth(nextWidth);
    };
    const stopResize = () => setResizingPublish(false);
    window.addEventListener('mousemove', handleMove);
    window.addEventListener('mouseup', stopResize);
    return () => {
      window.removeEventListener('mousemove', handleMove);
      window.removeEventListener('mouseup', stopResize);
    };
  }, [resizingPublish]);

  const handleClearHistory = async () => {
    await clearAllHistory();
    setLastMessages(new Map());
    setPreviousMessages(new Map());
    setRoot({
      name: 'root',
      fullName: '',
      children: [],
      messageCount: 0,
      isExpanded: true
    });
    setSelectedTopic(null);
    refreshStats();
  };

  const activeMessage = selectedTopic ? lastMessages.get(selectedTopic) || null : null;
  const previousMessage = selectedTopic ? previousMessages.get(selectedTopic) || null : null;
  const chartFields = selectedTopic ? chartFieldsByTopic[selectedTopic] || [] : [];
  const selectedChartField = selectedTopic ? chartFieldByTopic[selectedTopic] || '' : '';
  const effectiveChartField = selectedTopic
    ? selectedChartField || (chartFields.length === 1 ? chartFields[0].path : '')
    : '';
  const chartSeriesKey = selectedTopic && effectiveChartField
    ? `${selectedTopic}::${effectiveChartField}`
    : '';
  const chartSeries = selectedTopic && effectiveChartField
    ? (chartSource === 'db'
      ? dbSeriesData[chartSeriesKey] || null
      : seriesData[chartSeriesKey] || null)
    : null;

  useEffect(() => {
    if (chartSource !== 'db' || !selectedTopic || !effectiveChartField) {
      return;
    }
    let active = true;
    const seriesKey = `${selectedTopic}::${effectiveChartField}`;
    const loadHistory = async () => {
      try {
        const history = await getHistory(selectedTopic, 200);
        const points = history
          .map((msg) => {
            const value = extractNumericValue(msg.payload, effectiveChartField);
            if (value === undefined) return null;
            const timestamp = Date.parse(msg.timestamp);
            if (Number.isNaN(timestamp)) return null;
            return { time: timestamp / 1000, value };
          })
          .filter((entry): entry is { time: number; value: number } => entry !== null)
          .reverse();
        if (!active) return;
        setDbSeriesData((prev) => ({
          ...prev,
          [seriesKey]: { times: points.map((p) => p.time), values: points.map((p) => p.value) }
        }));
      } catch {
        if (!active) return;
        setDbSeriesData((prev) => ({ ...prev, [seriesKey]: { times: [], values: [] } }));
      }
    };
    loadHistory();
    return () => {
      active = false;
    };
  }, [chartSource, effectiveChartField, selectedTopic]);

  return (
    <div
      className="flex flex-col h-screen overflow-hidden bg-[color:var(--bg-main)] text-[color:var(--text-main)] relative"
      style={{
        ['--flash-duration' as string]: `${settings.highlightMs}ms`,
        ['--ui-font-size' as string]: `${settings.uiFontSize}px`,
        ['--topic-font-size' as string]: `${settings.topicFontSize}px`,
        ['--payload-font-size' as string]: `${settings.payloadFontSize}px`
      }}
    >
      <header className="h-12 border-b border-[color:var(--border)] bg-[color:var(--bg-panel)] flex items-center justify-between px-4 shrink-0 ui-font">
        <div className="flex items-center gap-3">
          <div className="bg-[color:var(--accent-green)] p-1.5 rounded-lg text-black">
            <Radio size={18} />
          </div>
          <h1 className="font-bold text-sm tracking-tight flex items-center gap-2">
            MQTT EXPLORER
            <span className="text-[10px] font-normal opacity-50 bg-white/5 px-1.5 py-0.5 rounded">v1.2.0-monokai</span>
          </h1>
        </div>

          <div className="flex items-center gap-6">
            <div className="flex items-center gap-2 text-xs">
              <span className="opacity-40 uppercase tracking-widest text-[9px]">Status</span>
              <div className="flex items-center gap-1.5 bg-[color:var(--bg-main)] px-2 py-1 rounded border border-[color:var(--border)]">
                <div className={`w-2 h-2 rounded-full ${connected ? 'bg-[color:var(--accent-green)]' : 'bg-[color:var(--accent-red)]'} animate-pulse`} />
                <span className={connected ? 'text-[color:var(--accent-green)]' : 'text-[color:var(--accent-red)]'}>{connected ? 'Connected' : 'Offline'}</span>
              </div>
            </div>
            <div className="hidden md:flex items-center gap-4 text-[10px] opacity-70">
              <span>CPU: {metrics.cpuPercent.toFixed(1)}%</span>
              <span>MEM: {formatBytes(metrics.memBytes)}{metrics.memLimit ? ` / ${formatBytes(metrics.memLimit)}` : ''}</span>
              <span>DB: {metrics.dbSize}</span>
              <button
                className="flex items-center gap-1 px-2 py-1 rounded border text-[10px] border-[color:var(--border)] hover:border-[color:var(--accent-red)] hover:text-[color:var(--accent-red)]"
                title="Supprimer toute la base SQLite"
                onClick={handleClearHistory}
              >
                <Trash2 size={12} /> Clear DB
              </button>
            </div>
            <div className="hidden md:flex items-center gap-2 text-xs">
              <select
                value={activeProfile?.id || ''}
                onChange={(e) => setSettings((prev) => ({ ...prev, activeProfileId: e.target.value }))}
                className="bg-[color:var(--bg-main)] border border-[color:var(--border)] rounded px-2 py-1 text-[10px]"
              >
                {settings.mqttProfiles.map((profile) => (
                  <option key={profile.id} value={profile.id}>
                    {profile.name}
                  </option>
                ))}
              </select>
              <button
                className={`flex items-center gap-1 px-2 py-1 rounded border text-[10px] ${settings.applyViewFilter ? 'border-[color:var(--accent-green)] text-[color:var(--accent-green)]' : 'border-[color:var(--border)] opacity-60'}`}
                onClick={() => setSettings((prev) => ({ ...prev, applyViewFilter: !prev.applyViewFilter }))}
                title="Filtrer les topics masqués"
              >
                <Filter size={12} />
                Masqués
              </button>
            </div>
            <nav className="hidden md:flex gap-4 opacity-70 hover:opacity-100 transition-opacity">
            <button
              className={`hover:text-[color:var(--accent-blue)] ${publishOpen ? 'text-[color:var(--accent-green)]' : ''}`}
              title="Publish"
              onClick={() => setPublishOpen((prev) => !prev)}
            >
              <Send size={20}/>
            </button>
            <button
              className={`hover:text-[color:var(--accent-blue)] ${activeView === 'settings' ? 'text-[color:var(--accent-green)]' : ''}`}
              title="Settings"
              onClick={() => setActiveView(activeView === 'settings' ? 'details' : 'settings')}
            >
              <Settings size={20}/>
            </button>
            <button className="hover:text-[color:var(--accent-blue)]" title="Refresh Stats" onClick={refreshStats}><Layers size={20}/></button>
            {settings.repoUrl && (
              <a
                href={settings.repoUrl}
                className="hover:text-[color:var(--accent-blue)]"
                title="Source Code"
                target="_blank"
                rel="noreferrer"
              >
                <GiteaIcon size={20} />
              </a>
            )}
          </nav>
        </div>
      </header>

      <main className="flex-1 flex overflow-hidden">
        <aside
          className={`shrink-0 ${activeView === 'topics' ? 'block' : 'hidden'} md:block`}
          style={{ width: `${sidebarWidth}px` }}
        >
          <TopicTree
            root={root}
            selectedTopic={selectedTopic}
            recentTopics={recentTopics}
            topicFilters={settings.topicFilters}
            applyViewFilter={settings.applyViewFilter}
            onSelect={(topic) => {
              setSelectedTopic(topic);
              if (window.matchMedia('(max-width: 767px)').matches) {
                setActiveView('details');
              }
            }}
          />
        </aside>
        <div
          className="hidden md:block cursor-col-resize bg-[color:var(--border)]/60 hover:bg-[color:var(--accent-blue)]/40 transition-colors"
          style={{ width: `${settings.resizeHandlePx}px` }}
          onMouseDown={() => setResizing(true)}
        />

        <section className={`flex-1 overflow-hidden flex flex-col ${activeView === 'details' ? 'block' : 'hidden'} md:flex`}>
          <TopicDetails
            topic={selectedTopic || ''}
            lastMessage={activeMessage}
            previousMessage={previousMessage}
            isRecent={selectedTopic ? Boolean(recentTopics[selectedTopic]) : false}
            maxPayloadBytes={settings.maxPayloadBytes}
            chartTopic={selectedTopic}
            chartSeries={chartSeries}
            chartFields={chartFields}
            chartField={selectedChartField}
            onChartFieldChange={(path) => {
              if (!selectedTopic) return;
              setChartFieldByTopic((prev) => ({ ...prev, [selectedTopic]: path }));
            }}
            chartSource={chartSource}
            onChartSourceChange={setChartSource}
          />
        </section>

        <section className={`flex-1 overflow-hidden flex flex-col ${activeView === 'publish' ? 'block' : 'hidden'} md:hidden`}>
          <PublishPanel
            draft={publishDraft}
            onDraftChange={setPublishDraft}
            onPasteTopic={() => {
              if (selectedTopic) setPublishDraft((prev) => ({ ...prev, topic: selectedTopic }));
            }}
            onPastePayload={() => {
              if (activeMessage) setPublishDraft((prev) => ({ ...prev, payload: activeMessage.payload }));
            }}
          />
        </section>

        <section className={`flex-1 overflow-hidden flex flex-col ${activeView === 'settings' ? 'block' : 'hidden'} md:hidden`}>
          <SettingsPanel settings={settings} onSettingsChange={setSettings} />
        </section>
        <div className={`${publishOpen ? 'hidden md:flex' : 'hidden'}`}>
          <div
            className="cursor-col-resize bg-[color:var(--border)]/60 hover:bg-[color:var(--accent-blue)]/40 transition-colors"
            style={{ width: `${settings.resizeHandlePx}px` }}
            onMouseDown={() => setResizingPublish(true)}
          />
          <section className="border-l border-[color:var(--border)] bg-[color:var(--bg-main)]" style={{ width: `${publishWidth}px` }}>
            <PublishPanel
              draft={publishDraft}
              onDraftChange={setPublishDraft}
              onPasteTopic={() => {
                if (selectedTopic) setPublishDraft((prev) => ({ ...prev, topic: selectedTopic }));
              }}
              onPastePayload={() => {
                if (activeMessage) setPublishDraft((prev) => ({ ...prev, payload: activeMessage.payload }));
              }}
            />
          </section>
        </div>

        <div className={`${activeView === 'settings' ? 'hidden md:flex' : 'hidden'}`}>
          <div
            className="cursor-col-resize bg-[color:var(--border)]/60 hover:bg-[color:var(--accent-blue)]/40 transition-colors"
            style={{ width: `${settings.resizeHandlePx}px` }}
            onMouseDown={() => setResizingSettings(true)}
          />
          <section className="border-l border-[color:var(--border)]" style={{ width: `${settingsWidth}px` }}>
            <SettingsPanel settings={settings} onSettingsChange={setSettings} />
          </section>
        </div>
      </main>

      <footer className="h-8 border-t border-[color:var(--border)] bg-[color:var(--bg-panel)] flex items-center justify-between px-4 opacity-60 shrink-0 ui-font">
        <div className="flex gap-4 items-center">
          <span className="flex items-center gap-1"><Activity size={10}/> 2.4 msg/sec</span>
          <span className="flex items-center gap-1">SQLite: {dbStats.size} ({dbStats.count} msgs)</span>
          <span className="flex items-center gap-1">TTL: {settings.ttlDays} jours</span>
        </div>
        <div className="hidden md:flex items-center gap-3">
          <span>MQTT {sysinfo.version || '—'}</span>
          <span>Clients {sysinfo.clients}</span>
          <span>Rx {sysinfo.msgReceived}</span>
          <span>Tx {sysinfo.msgSent}</span>
          <span>Stored {sysinfo.msgStored}</span>
          <span>Subs {sysinfo.subscriptions}</span>
        </div>
        <div className="hidden md:block">
          Broker: {activeProfile ? `${activeProfile.host}:${activeProfile.port}` : 'non défini'}
        </div>
      </footer>

      <nav className="md:hidden h-12 border-t border-[color:var(--border)] bg-[color:var(--bg-panel)] flex items-center justify-around text-[10px]">
        <button onClick={() => setActiveView('topics')} className={`flex flex-col items-center ${activeView === 'topics' ? 'text-[color:var(--accent-green)]' : 'opacity-60'}`}>
          <Wifi size={14} /> Topics
        </button>
        <button onClick={() => setActiveView('details')} className={`flex flex-col items-center ${activeView === 'details' ? 'text-[color:var(--accent-green)]' : 'opacity-60'}`}>
          <FileText size={14} /> Details
        </button>
        <button onClick={() => setActiveView('publish')} className={`flex flex-col items-center ${activeView === 'publish' ? 'text-[color:var(--accent-green)]' : 'opacity-60'}`}>
          <Send size={14} /> Publish
        </button>
        <button onClick={() => setActiveView('settings')} className={`flex flex-col items-center ${activeView === 'settings' ? 'text-[color:var(--accent-green)]' : 'opacity-60'}`}>
          <Settings size={14} /> Settings
        </button>
      </nav>
    </div>
  );
};

export default App;
