/**
 * LogViewer.jsx — System log tail viewer
 *
 * Features:
 *   - Subscribes to /rosout (rcl_interfaces/Log)
 *   - Real-time scrolling log output
 *   - Severity-based color coding (DEBUG=grey, INFO=white, WARN=yellow, ERROR=red, FATAL=magenta)
 *   - Filter by severity level
 *   - Filter by node name
 *   - Auto-scroll to latest logs
 *   - Configurable max log history (default 500)
 */

import { useEffect, useRef, useState } from 'react';

const LOG_LEVELS = {
  DEBUG: 'DEBUG',
  INFO: 'INFO',
  WARN: 'WARN',
  ERROR: 'ERROR',
  FATAL: 'FATAL',
};

const LOG_LEVEL_VALUES = {
  DEBUG: 10,
  INFO: 20,
  WARN: 30,
  ERROR: 40,
  FATAL: 50,
};

const LOG_COLORS = {
  DEBUG: { bg: 'bg-gray-950', border: 'border-gray-800', text: 'text-gray-500', label: 'text-gray-500' },
  INFO: { bg: 'bg-gray-950', border: 'border-gray-800', text: 'text-gray-300', label: 'text-white' },
  WARN: { bg: 'bg-gray-950', border: 'border-yellow-900', text: 'text-yellow-400', label: 'text-yellow-500' },
  ERROR: { bg: 'bg-gray-950', border: 'border-red-900', text: 'text-red-400', label: 'text-red-500' },
  FATAL: { bg: 'bg-gray-950', border: 'border-magenta-900', text: 'text-magenta-400', label: 'text-magenta-500' },
};

const MAX_LOGS = 500;

function formatTimestamp(timestamp) {
  const date = new Date(timestamp);
  return date.toLocaleTimeString('en-US', {
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false,
  });
}

function getLevelName(level) {
  // Convert numeric level to name
  if (level <= LOG_LEVEL_VALUES.DEBUG) return LOG_LEVELS.DEBUG;
  if (level <= LOG_LEVEL_VALUES.INFO) return LOG_LEVELS.INFO;
  if (level <= LOG_LEVEL_VALUES.WARN) return LOG_LEVELS.WARN;
  if (level <= LOG_LEVEL_VALUES.ERROR) return LOG_LEVELS.ERROR;
  return LOG_LEVELS.FATAL;
}

function LogLine({ log, colors }) {
  return (
    <div className={`font-mono text-xs py-1 px-2 border-l-2 ${colors.border} ${colors.bg}`}>
      <div className="flex gap-2 items-start">
        <span className={`font-bold text-xs whitespace-nowrap flex-shrink-0 ${colors.label}`}>
          {log.level.padEnd(5)}
        </span>
        <span className="text-gray-600 whitespace-nowrap flex-shrink-0">
          {formatTimestamp(log.timestamp)}
        </span>
        <span className="text-cyan-600 whitespace-nowrap flex-shrink-0 min-w-32 truncate">
          [{log.node}]
        </span>
        <span className={`${colors.text} flex-1 break-words`}>
          {log.message}
        </span>
      </div>
    </div>
  );
}

export function LogViewer({ subscribe }) {
  const [logs, setLogs] = useState([]);
  const [selectedLevels, setSelectedLevels] = useState(new Set(['INFO', 'WARN', 'ERROR', 'FATAL']));
  const [nodeFilter, setNodeFilter] = useState('');
  const scrollRef = useRef(null);
  const logIdRef = useRef(0);

  // Auto-scroll to bottom when new logs arrive
  useEffect(() => {
    if (scrollRef.current) {
      setTimeout(() => {
        scrollRef.current?.scrollIntoView({ behavior: 'auto', block: 'end' });
      }, 0);
    }
  }, [logs.length]);

  // Subscribe to ROS logs
  useEffect(() => {
    const unsubscribe = subscribe(
      '/rosout',
      'rcl_interfaces/Log',
      (msg) => {
        try {
          const levelName = getLevelName(msg.level);
          const logEntry = {
            id: ++logIdRef.current,
            timestamp: msg.stamp ? msg.stamp.sec * 1000 + msg.stamp.nanosec / 1000000 : Date.now(),
            level: levelName,
            node: msg.name || 'unknown',
            message: msg.msg || '',
            file: msg.file || '',
            function: msg.function || '',
            line: msg.line || 0,
          };

          setLogs((prev) => [...prev, logEntry].slice(-MAX_LOGS));
        } catch (e) {
          console.error('Error parsing log message:', e);
        }
      }
    );

    return unsubscribe;
  }, [subscribe]);

  // Toggle level selection
  const toggleLevel = (level) => {
    const updated = new Set(selectedLevels);
    if (updated.has(level)) {
      updated.delete(level);
    } else {
      updated.add(level);
    }
    setSelectedLevels(updated);
  };

  // Filter logs based on selected levels and node filter
  const filteredLogs = logs.filter((log) => {
    const matchesLevel = selectedLevels.has(log.level);
    const matchesNode = nodeFilter === '' || log.node.toLowerCase().includes(nodeFilter.toLowerCase());
    return matchesLevel && matchesNode;
  });

  const clearLogs = () => {
    setLogs([]);
    logIdRef.current = 0;
  };

  return (
    <div className="flex flex-col h-full space-y-3">
      {/* Controls */}
      <div className="bg-gray-950 rounded-lg border border-cyan-950 p-3 space-y-3">
        <div className="flex justify-between items-center flex-wrap gap-2">
          <div className="text-cyan-700 text-xs font-bold tracking-widest">
            SYSTEM LOG VIEWER
          </div>
          <div className="text-gray-600 text-xs">
            {filteredLogs.length} / {logs.length} logs
          </div>
        </div>

        {/* Severity filter buttons */}
        <div className="space-y-2">
          <div className="text-gray-700 text-xs font-bold">SEVERITY FILTER:</div>
          <div className="flex gap-2 flex-wrap">
            {Object.keys(LOG_COLORS).map((level) => (
              <button
                key={level}
                onClick={() => toggleLevel(level)}
                className={`px-2 py-1 text-xs font-bold rounded border transition-colors ${
                  selectedLevels.has(level)
                    ? `${LOG_COLORS[level].border} ${LOG_COLORS[level].bg} ${LOG_COLORS[level].label}`
                    : 'border-gray-700 bg-gray-900 text-gray-600 hover:text-gray-400'
                }`}
              >
                {level}
              </button>
            ))}
          </div>
        </div>

        {/* Node filter input */}
        <div className="space-y-1">
          <div className="text-gray-700 text-xs font-bold">NODE FILTER:</div>
          <input
            type="text"
            placeholder="Filter by node name..."
            value={nodeFilter}
            onChange={(e) => setNodeFilter(e.target.value)}
            className="w-full px-2 py-1.5 text-xs bg-gray-900 border border-gray-800 rounded text-gray-300 focus:outline-none focus:border-cyan-700 placeholder-gray-700"
          />
        </div>

        {/* Action buttons */}
        <div className="flex gap-2 flex-wrap">
          <button
            onClick={clearLogs}
            className="px-3 py-1.5 text-xs font-bold tracking-widest rounded border border-gray-700 bg-gray-900 text-gray-400 hover:text-red-400 hover:border-red-700 transition-colors"
          >
            CLEAR
          </button>
          <div className="text-gray-600 text-xs flex items-center">
            Auto-scrolls to latest logs
          </div>
        </div>
      </div>

      {/* Log viewer area */}
      <div className="flex-1 bg-gray-950 rounded-lg border border-cyan-950 overflow-y-auto space-y-0">
        {filteredLogs.length === 0 ? (
          <div className="flex items-center justify-center h-full text-gray-600">
            <div className="text-center">
              <div className="text-sm mb-2">No logs to display</div>
              <div className="text-xs text-gray-700">
                Logs from /rosout will appear here
              </div>
            </div>
          </div>
        ) : (
          <>
            {filteredLogs.map((log) => (
              <LogLine
                key={log.id}
                log={log}
                colors={LOG_COLORS[log.level]}
              />
            ))}
            <div ref={scrollRef} />
          </>
        )}
      </div>

      {/* Topic info */}
      <div className="bg-gray-950 rounded border border-gray-800 p-2 text-xs text-gray-600 space-y-1">
        <div className="flex justify-between">
          <span>Topic:</span>
          <span className="text-gray-500">/rosout (rcl_interfaces/Log)</span>
        </div>
        <div className="flex justify-between">
          <span>Max History:</span>
          <span className="text-gray-500">{MAX_LOGS} entries</span>
        </div>
        <div className="flex justify-between">
          <span>Colors:</span>
          <span className="text-gray-500">DEBUG=grey | INFO=white | WARN=yellow | ERROR=red | FATAL=magenta</span>
        </div>
      </div>
    </div>
  );
}
