// components/tools.jsx — ToolsPanel: tool registry, dispatch, job queue.
// Depends on: React (useState, useCallback, useEffect), Card, StatusBadge, get, post from api.js

// ── Table Export Component ─────────────────────────────────────

function TableExport({ data, filename, title }) {
  const [showMenu, setShowMenu] = useState(false);

  const convertToCSV = (data) => {
    if (!data || data.length === 0) return '';
    
    const headers = Object.keys(data[0]);
    const csvHeaders = headers.join(',');
    
    const csvRows = data.map(row => {
      return headers.map(header => {
        const value = row[header];
        // Handle nested objects and arrays
        if (typeof value === 'object' && value !== null) {
          return `"${JSON.stringify(value).replaceAll('"', '""')}"`;
        }
        // Escape commas, quotes, and newlines in strings
        if (typeof value === 'string' && (value.includes(',') || value.includes('"') || value.includes('\n') || value.includes('\r'))) {
          return `"${value.replaceAll('"', '""')}"`;
        }
        return value ?? '';
      }).join(',');
    });
    
    return [csvHeaders, ...csvRows].join('\n');
  };

  const downloadCSV = () => {
    const csv = convertToCSV(data);
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
    const link = document.createElement('a');
    const url = URL.createObjectURL(blob);
    link.setAttribute('href', url);
    link.setAttribute('download', `${filename}.csv`);
    link.style.visibility = 'hidden';
    document.body.appendChild(link);
    link.click();
    link.remove();
  };

  const downloadJSON = () => {
    const json = JSON.stringify(data, null, 2);
    const blob = new Blob([json], { type: 'application/json;charset=utf-8;' });
    const link = document.createElement('a');
    const url = URL.createObjectURL(blob);
    link.setAttribute('href', url);
    link.setAttribute('download', `${filename}.json`);
    link.style.visibility = 'hidden';
    document.body.appendChild(link);
    link.click();
    link.remove();
  };

  const copyToClipboard = (format, event) => {
    let content;
    if (format === 'csv') {
      content = convertToCSV(data);
    } else {
      content = JSON.stringify(data, null, 2);
    }
    
    navigator.clipboard.writeText(content).then(() => {
      // Show brief success feedback
      const button = event.target;
      const originalText = button.textContent;
      button.textContent = 'Copied!';
      button.className = button.className.replace('bg-gray-100', 'bg-green-100').replace('text-gray-600', 'text-green-600');
      setTimeout(() => {
        button.textContent = originalText;
        button.className = button.className.replace('bg-green-100', 'bg-gray-100').replace('text-green-600', 'text-gray-600');
      }, 2000);
    });
  };

  if (!data || data.length === 0) {
    return null;
  }

  return (
    <div className="relative">
      <button
        onClick={() => setShowMenu(!showMenu)}
        className="text-xs px-2 py-1 rounded bg-gray-100 text-gray-600 hover:bg-gray-200 flex items-center gap-1"
        title="Export table data"
      >
        📊 Export ▼
      </button>
      
      {showMenu && (
        <div className="absolute right-0 mt-1 w-48 bg-white rounded-md shadow-lg border border-gray-200 z-10">
          <div className="py-1">
            <button
              onClick={() => { downloadCSV(); setShowMenu(false); }}
              className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
            >
              📄 Download CSV
            </button>
            <button
              onClick={() => { downloadJSON(); setShowMenu(false); }}
              className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
            >
              📋 Download JSON
            </button>
            <button
              onClick={(e) => { copyToClipboard('csv', e); setShowMenu(false); }}
              className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
            >
              📋 Copy CSV
            </button>
            <button
              onClick={(e) => { copyToClipboard('json', e); setShowMenu(false); }}
              className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
            >
              📋 Copy JSON
            </button>
          </div>
        </div>
      )}
      
      {/* Close menu when clicking outside */}
      {showMenu && (
        <button
          type="button"
          className="fixed inset-0 z-0"
          aria-label="Close export menu"
          onClick={() => setShowMenu(false)}
        />
      )}
    </div>
  );
}

const CATEGORY_LABELS = {
  ide: "IDE",
  agent_cli: "CLI Agent",
  browser: "Browser",
  custom: "Custom",
  human: "Human",
};

const CATEGORY_COLORS = {
  ide: "bg-blue-100 text-blue-700",
  agent_cli: "bg-purple-100 text-purple-700",
  browser: "bg-amber-100 text-amber-700",
  custom: "bg-gray-100 text-gray-600",
  human: "bg-gray-100 text-gray-600",
};

const JOB_STATUS_COLORS = {
  queued: "bg-gray-100 text-gray-600",
  running: "bg-amber-100 text-amber-700",
  done: "bg-emerald-100 text-emerald-700",
  failed: "bg-red-100 text-red-600",
  cancelled: "bg-gray-200 text-gray-500",
};

// ── Hook: useTools ──────────────────────────────────────────

function useTools() {
  const [tools, setTools] = useState([]);
  const [queue, setQueue] = useState(null);
  const [jobs, setJobs] = useState([]);
  const [loading, setLoading] = useState(true);

  const refreshTools = useCallback(async () => {
    const data = await get("/dashboard/tools/");
    setTools(data?.tools || []);
  }, []);

  const refreshQueue = useCallback(async () => {
    const data = await get("/dashboard/tools/queue");
    setQueue(data);
    setJobs(data?.recent_jobs || []);
  }, []);

  const refresh = useCallback(async () => {
    setLoading(true);
    await Promise.all([refreshTools(), refreshQueue()]);
    setLoading(false);
  }, [refreshTools, refreshQueue]);

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

  return { tools, queue, jobs, loading, refresh };
}

// ── ToolCard ────────────────────────────────────────────────

function ToolCard({ tool, isSelected, onSelect }) {
  const catClass = CATEGORY_COLORS[tool.category] || CATEGORY_COLORS.custom;
  
  const handleCardClick = () => {
    onSelect(tool.name);
  };

  return (
    <button
      type="button"
      onClick={handleCardClick}
      className={`bg-white border rounded-lg p-4 hover:shadow-sm transition-shadow text-left w-full ${
        isSelected ? 'border-blue-400 bg-blue-50' : 'border-gray-200'
      }`}
      aria-pressed={isSelected}
      aria-label={`Tool ${tool.name}${isSelected ? ' (selected)' : ''}: ${tool.description}`}
    >
      <div className="flex items-center justify-between mb-2">
        <div className="flex items-center gap-2">
          <span className="font-semibold text-gray-900">{tool.name}</span>
          <span className={`text-xs px-2 py-0.5 rounded-full ${catClass}`}>
            {CATEGORY_LABELS[tool.category] || tool.category}
          </span>
        </div>
        <StatusBadge ok={tool.available} label={tool.available ? "Available" : "Not found"} />
      </div>
      <p className="text-sm text-gray-500 mb-2">{tool.description}</p>
      <div className="flex flex-wrap gap-1">
        {tool.capabilities?.map(cap => (
          <span key={cap} className="text-xs bg-gray-100 text-gray-600 px-2 py-0.5 rounded">
            {cap.replaceAll("_", " ")}
          </span>
        ))}
      </div>
      {tool.modes?.length > 0 && (
        <div className="mt-2 flex gap-1">
          <span className="text-xs text-gray-400">Modes:</span>
          {tool.modes.map(m => (
            <span key={m} className="text-xs bg-indigo-50 text-indigo-600 px-1.5 py-0.5 rounded">{m}</span>
          ))}
        </div>
      )}
      {tool.environment && (
        <div className="mt-2 space-y-1">
          <div className="flex flex-wrap gap-1">
            <span className="text-xs bg-emerald-50 text-emerald-700 px-2 py-0.5 rounded border border-emerald-100">
              env: {tool.environment.name}
            </span>
            <span className="text-xs bg-emerald-50 text-emerald-600 px-2 py-0.5 rounded border border-emerald-100">
              {tool.environment.kind?.replace("_", " ")}
            </span>
          </div>
          {tool.environment.target && (
            <div className="text-[11px] text-emerald-600 truncate" title={tool.environment.target}>
              {tool.environment.target}
            </div>
          )}
        </div>
      )}
      {tool.binary && (
        <div className="mt-1 text-xs text-gray-400">binary: <code className="bg-gray-50 px-1 rounded">{tool.binary}</code></div>
      )}
    </button>
  );
}

// ── JobRow ──────────────────────────────────────────────────

function JobRow({ job, onClick, isSelected, onRetry, onRepair }) {
  const statusClass = JOB_STATUS_COLORS[job.status] || JOB_STATUS_COLORS.queued;
  const hasLogs = (job.result?.stdout_lines || 0) > 0 || (job.result?.stderr_lines || 0) > 0 || !!job.error;
  const isFailed = job.status === "failed";
  return (
    <tr className={`border-b border-gray-100 hover:bg-gray-50 ${isSelected ? "bg-blue-50" : ""}`}>
      <td className="py-2 px-3 text-xs font-mono text-gray-500">
        <button
          type="button"
          onClick={() => onClick?.(job)}
          className="text-left hover:text-blue-600"
          title="Kliknij, aby zobaczyć logi joba"
        >
          {job.id}
        </button>
      </td>
      <td className="py-2 px-3 text-sm">{job.ticket_id}</td>
      <td className="py-2 px-3 text-sm font-medium">
        <div className="flex flex-col gap-1">
          <span>{job.tool}</span>
          {job.environment && (
            <span className="text-[11px] text-emerald-600 truncate" title={job.environment.target || job.environment.name}>
              {job.environment.name}{job.environment.kind ? ` · ${job.environment.kind.replace("_", " ")}` : ""}
            </span>
          )}
        </div>
      </td>
      <td className="py-2 px-3 text-xs">{job.mode}</td>
      <td className="py-2 px-3">
        <span className={`text-xs px-2 py-0.5 rounded-full ${statusClass}`}>{job.status}</span>
      </td>
      <td className="py-2 px-3 text-xs text-gray-500">
        {job.duration_s > 0 ? `${job.duration_s}s` : "—"}
      </td>
      <td className="py-2 px-3 text-xs text-red-500 truncate max-w-[200px]">{job.error || ""}</td>
      <td className="py-2 px-3">
        <button
          type="button"
          onClick={() => onClick?.(job)}
          className={`text-xs px-2 py-0.5 rounded ${
            hasLogs ? "bg-blue-50 text-blue-600 hover:bg-blue-100" : "bg-gray-50 text-gray-400"
          }`}
          title={hasLogs ? "View stdout/stderr logs" : "No logs available"}
        >
          {hasLogs ? "View Logs" : "No Logs"}
        </button>
      </td>
      <td className="py-2 px-3">
        <div className="flex items-center gap-1">
          {isFailed && (
            <button
              type="button"
              onClick={(e) => { e.stopPropagation(); onRetry?.(job); }}
              className="text-xs px-2 py-0.5 rounded bg-amber-50 text-amber-600 hover:bg-amber-100"
              title="Retry this job"
            >
              Retry
            </button>
          )}
          {isFailed && (
            <button
              type="button"
              onClick={(e) => { e.stopPropagation(); onRepair?.(job); }}
              className="text-xs px-2 py-0.5 rounded bg-purple-50 text-purple-600 hover:bg-purple-100"
              title="Diagnose and suggest fix"
            >
              Repair
            </button>
          )}
          {job.status === "queued" && (
            <span className="text-xs text-gray-400">Waiting...</span>
          )}
          {job.status === "running" && (
            <span className="text-xs text-blue-500 animate-pulse">Running</span>
          )}
          {job.status === "done" && (
            <span className="text-xs text-emerald-500">OK</span>
          )}
        </div>
      </td>
    </tr>
  );
}

function JobDetailsPanel({ job, loading, error, onClose, onRetry, onRepair, repairResult }) {
  if (!job) return null;

  const result = job.result || null;
  const stdout = result?.stdout || "";
  const stderr = result?.stderr || "";
  const filesChanged = result?.files_changed || [];
  const isFailed = job.status === "failed";
  const ticketTitle = job.ticket?.title || job.ticket?.task || job.ticket_id || "—";
  const ticketStatus = job.ticket?.status || "—";
  const repairActionClass = {
    install: "bg-emerald-100 text-emerald-700",
    retry: "bg-amber-100 text-amber-700",
    start_container: "bg-blue-100 text-blue-700",
  };
  const execution = result?.execution || result?.execution_metadata || {};
  const promptExcerpt = job.prompt_excerpt || "";
  const projectDir = job.project_dir || "";
  const resultSuccess = result?.success;
  const isActiveJob = job.status === "queued" || job.status === "running";
  let resultLabel = "unknown";
  if (typeof resultSuccess === "boolean") {
    if (resultSuccess) {
      resultLabel = "success";
    } else if (isActiveJob) {
      resultLabel = "pending";
    } else {
      resultLabel = "failed";
    }
  } else if (isActiveJob) {
    resultLabel = "pending";
  }
  let resultLabelClass = "text-gray-500";
  if (resultLabel === "success") {
    resultLabelClass = "text-emerald-600";
  } else if (resultLabel === "failed") {
    resultLabelClass = "text-red-600";
  }
  const stdoutLines = result?.stdout_lines ?? 0;
  const stderrLines = result?.stderr_lines ?? 0;

  return (
    <div className="mt-4 rounded-xl border border-gray-200 bg-gray-50 p-4 space-y-4">
      <div className="flex items-start justify-between gap-4">
        <div>
          <div className="text-sm font-semibold text-gray-800">Job details</div>
          <div className="text-xs text-gray-500 font-mono mt-1">{job.id} · {job.tool} · {job.status}</div>
        </div>
        <div className="flex items-center gap-2">
          {isFailed && (
            <button
              type="button"
              onClick={() => onRetry?.(job)}
              className="text-xs px-3 py-1 rounded bg-amber-100 text-amber-700 hover:bg-amber-200 font-medium"
            >
              Retry Job
            </button>
          )}
          {isFailed && (
            <button
              type="button"
              onClick={() => onRepair?.(job)}
              className="text-xs px-3 py-1 rounded bg-purple-100 text-purple-700 hover:bg-purple-200 font-medium"
            >
              Auto-Repair
            </button>
          )}
          <button
            type="button"
            onClick={onClose}
            className="text-xs px-2 py-1 rounded bg-white border border-gray-200 text-gray-600 hover:bg-gray-100"
          >
            Close
          </button>
        </div>
      </div>

      <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-3 text-xs">
        <div className="rounded-lg bg-white border border-gray-200 p-3">
          <div className="text-gray-400 uppercase">Ticket</div>
          <div className="font-medium text-gray-800 mt-1">{ticketTitle}</div>
          <div className="text-gray-500 mt-1 font-mono">{job.ticket_id}</div>
          <div className="text-gray-400 mt-1">{ticketStatus}</div>
        </div>
        <div className="rounded-lg bg-white border border-gray-200 p-3">
          <div className="text-gray-400 uppercase">Execution context</div>
          <div className="font-medium text-gray-800 mt-1 truncate" title={projectDir || ""}>
            {projectDir || "—"}
          </div>
          <div className="text-gray-400 mt-1">
            {job.mode || "—"}{job.model ? ` · ${job.model}` : ""}
          </div>
          {job.environment && (
            <div className="mt-2 space-y-1">
              <div className="text-gray-500">Environment: <span className="font-medium text-gray-800">{job.environment.name}</span></div>
              <div className="text-gray-400 truncate" title={job.environment.target || ""}>
                {job.environment.target || job.environment.kind || ""}
              </div>
            </div>
          )}
          <div className="text-gray-400 mt-1">Prompt: {job.prompt_length || 0} chars</div>
        </div>
        <div className="rounded-lg bg-white border border-gray-200 p-3">
          <div className="text-gray-400 uppercase">Execution result</div>
          <div className={`font-medium mt-1 ${resultLabelClass}`}>
            {resultLabel}
          </div>
          <div className="text-gray-400 mt-1">Exit code: {result?.exit_code ?? "—"}</div>
          <div className="text-gray-400 mt-1">stdout {stdoutLines} / stderr {stderrLines}</div>
        </div>
        <div className="rounded-lg bg-white border border-gray-200 p-3">
          <div className="text-gray-400 uppercase">Files changed</div>
          <div className="font-medium text-gray-800 mt-1">{filesChanged.length || 0}</div>
        </div>
      </div>

      <div className="grid grid-cols-1 lg:grid-cols-2 gap-3 text-xs">
        <div className="rounded-lg bg-white border border-gray-200 p-3">
          <div className="text-gray-400 uppercase">Prompt excerpt</div>
          <p className="mt-2 max-h-40 overflow-auto whitespace-pre-wrap font-mono text-[11px] leading-snug text-gray-700">
            {promptExcerpt || "—"}
          </p>
        </div>
        <div className="rounded-lg bg-white border border-gray-200 p-3">
          <div className="text-gray-400 uppercase">Execution metadata</div>
          {Object.keys(execution).length > 0 ? (
            <pre className="mt-2 max-h-40 overflow-auto rounded bg-gray-50 p-2 text-[11px] leading-snug text-gray-700 whitespace-pre-wrap">
              {JSON.stringify(execution, null, 2)}
            </pre>
          ) : (
            <div className="mt-2 text-gray-400">No execution metadata captured.</div>
          )}
        </div>
      </div>

      {loading && <p className="text-xs text-gray-500">Loading job logs...</p>}
      {error && <p className="text-xs text-red-600">{error}</p>}

      {/* Repair results panel */}
      {repairResult && (
        <div className="rounded-lg border border-purple-200 bg-purple-50 p-4 space-y-3">
          <div className="text-sm font-semibold text-purple-800 flex items-center gap-2">
            Auto-Repair Results
            {repairResult.error && <span className="text-xs text-red-500 font-normal">(error)</span>}
          </div>

          {repairResult.error && (
            <p className="text-xs text-red-600">{repairResult.error}</p>
          )}

          {repairResult.diagnosis && (
            <div>
              <div className="text-xs font-medium text-purple-700 mb-1">Diagnosis</div>
              <p className="text-xs text-gray-700 bg-white rounded p-2 border border-purple-100">
                {repairResult.diagnosis}
              </p>
            </div>
          )}

          {repairResult.suggestion && (
            <div>
              <div className="text-xs font-medium text-purple-700 mb-1">Suggestion</div>
              <p className="text-xs text-gray-700 bg-white rounded p-2 border border-purple-100">
                {repairResult.suggestion}
              </p>
            </div>
          )}

          {repairResult.quick_fixes && repairResult.quick_fixes.length > 0 && (
            <div>
              <div className="text-xs font-medium text-purple-700 mb-1">Quick Fixes</div>
              <div className="space-y-2">
                {repairResult.quick_fixes.map((fix) => (
                  <div key={fix.label} className="flex items-start gap-2 bg-white rounded p-2 border border-purple-100">
                    <span className={`text-xs px-1.5 py-0.5 rounded font-medium ${repairActionClass[fix.action] || "bg-gray-100 text-gray-600"}`}>
                      {fix.action}
                    </span>
                    <div className="flex-1">
                      <div className="text-xs font-medium text-gray-800">{fix.label}</div>
                      <div className="text-xs text-gray-500">{fix.description}</div>
                      {fix.command && (
                        <pre className="mt-1 text-[11px] bg-gray-900 text-green-300 rounded px-2 py-1 font-mono overflow-x-auto">
                          {fix.command}
                        </pre>
                      )}
                    </div>
                  </div>
                ))}
              </div>
            </div>
          )}
        </div>
      )}

      <div className="space-y-3">
        <div>
          <div className="text-xs font-medium text-gray-600 mb-1">stdout</div>
          <pre className="max-h-72 overflow-auto rounded-lg bg-gray-900 p-3 text-[11px] leading-tight text-green-300 whitespace-pre-wrap">
            {stdout || "No stdout captured for this job."}
          </pre>
        </div>
        <div>
          <div className="text-xs font-medium text-gray-600 mb-1">stderr</div>
          <pre className="max-h-48 overflow-auto rounded-lg bg-gray-900 p-3 text-[11px] leading-tight text-red-300 whitespace-pre-wrap">
            {stderr || "No stderr captured for this job."}
          </pre>
        </div>
      </div>
    </div>
  );
}

// ── TicketDispatchTable ───────────────────────────────────────

function TicketDispatchTable({ tools, onDispatch }) {
  const [tickets, setTickets] = useState([]);
  const [projects, setProjects] = useState([]);
  const [loading, setLoading] = useState(true);
  const [dispatching, setDispatching] = useState(null);

  const availableTools = tools.filter(t => t.available);
  const unavailableTools = tools.filter(t => !t.available);
  const dispatchableTickets = tickets.filter(ticket => ticket.status !== "cancelled");

  // Load tickets
  useEffect(() => {
    const loadTickets = async () => {
      try {
        const data = await get("/dashboard/tickets");
        setTickets(data?.tickets || []);
      } catch (e) {
        console.error("Failed to load tickets:", e);
      } finally {
        setLoading(false);
      }
    };

    const loadProjects = async () => {
      try {
        const data = await get("/dashboard/projects");
        setProjects(Array.isArray(data) ? data : []);
      } catch (e) {
        console.error("Failed to load projects:", e);
      }
    };

    loadTickets();
    loadProjects();
  }, []);

  const handleToolSelect = async (ticketId, toolName, mode = "", projectDir = "") => {
    setDispatching(ticketId);
    try {
      await post("/dashboard/tools/dispatch", {
        ticket_id: ticketId,
        tool: toolName,
        mode: mode,
        project_dir: projectDir,
      });
      if (onDispatch) onDispatch();
    } catch (e) {
      console.error("Dispatch failed:", e);
    } finally {
      setDispatching(null);
    }
  };

  const getStatusBadgeClass = (status) => {
    if (status === 'done') return 'bg-emerald-100 text-emerald-700';
    if (status === 'in_progress') return 'bg-blue-100 text-blue-700';
    if (status === 'todo') return 'bg-gray-100 text-gray-700';
    return 'bg-yellow-100 text-yellow-700';
  };

  const getPriorityBadgeClass = (priority) => {
    if (priority === 'high') return 'bg-red-100 text-red-700';
    if (priority === 'medium') return 'bg-amber-100 text-amber-700';
    return 'bg-green-100 text-green-700';
  };

  const getToolCategoryClass = (category) => {
    if (category === 'agent_cli') return 'bg-purple-100 text-purple-700';
    if (category === 'ide') return 'bg-blue-100 text-blue-700';
    if (category === 'human') return 'bg-gray-100 text-gray-600';
    return 'bg-amber-100 text-amber-700';
  };

  const getProjectInfo = (projectId) => {
    if (!projectId) {
      return { label: "—", path: "", project: null };
    }

    const project = projects.find(p => p.id === projectId) || null;
    if (!project) {
      return { label: projectId, path: "", project: null };
    }

    const path = project.local_path || project.source_path || "";
    return {
      label: project.name || projectId,
      path,
      project,
    };
  };

  const renderCurrentTool = (currentTool, toolInfo) => {
    if (!currentTool) {
      return <span className="text-xs text-gray-400">None</span>;
    }

    const environmentName = toolInfo?.environment?.name || "";
    const environmentTarget = toolInfo?.environment?.target || "";

    return (
      <div className="flex flex-col gap-1">
        <div className="flex items-center gap-1 flex-wrap">
          <span className="text-xs font-medium">{currentTool.tool}</span>
          {currentTool.agent_mode && (
            <span className="text-xs text-gray-400">({currentTool.agent_mode})</span>
          )}
          {toolInfo && (
            <span className={`text-xs px-1.5 py-0.5 rounded ${getToolCategoryClass(toolInfo.category)}`}>
              {toolInfo.category?.replace('_', ' ')}
            </span>
          )}
        </div>
        {toolInfo?.environment && (
          <div className="flex flex-wrap gap-1">
            <span className="text-[11px] px-1.5 py-0.5 rounded bg-emerald-50 text-emerald-700 border border-emerald-100">
              env: {environmentName}
            </span>
            {environmentTarget && (
              <span className="text-[11px] text-emerald-600 truncate max-w-[220px]" title={environmentTarget}>
                {environmentTarget}
              </span>
            )}
          </div>
        )}
      </div>
    );
  };

  const renderStatusBadge = (ticket) => {
    const statusClass = getStatusBadgeClass(ticket.status);
    const displayStatus = ticket.status?.replace('_', ' ') || 'todo';
    
    return (
      <span className={`text-xs px-2 py-0.5 rounded-full ${statusClass}`}>
        {displayStatus}
      </span>
    );
  };

  const renderPriorityBadge = (ticket) => {
    const priorityClass = getPriorityBadgeClass(ticket.priority);
    const displayPriority = ticket.priority || 'medium';
    
    return (
      <span className={`text-xs px-2 py-0.5 rounded ${priorityClass}`}>
        {displayPriority}
      </span>
    );
  };

  if (loading) {
    return (
      <div className="bg-white border border-gray-200 rounded-lg p-4">
        <h3 className="text-sm font-semibold text-gray-700 mb-3">Dispatch Tool to Ticket</h3>
        <p className="text-sm text-gray-400">Loading tickets...</p>
      </div>
    );
  }

  if (dispatchableTickets.length === 0) {
    return (
      <div className="bg-white border border-gray-200 rounded-lg p-4">
        <h3 className="text-sm font-semibold text-gray-700 mb-3">Dispatch Tool to Ticket</h3>
        <p className="text-sm text-gray-400">No dispatchable tickets found. Create a non-cancelled ticket first to dispatch tools.</p>
      </div>
    );
  }

  return (
    <div className="bg-white border border-gray-200 rounded-lg p-4">
      <div className="flex justify-between items-center mb-3">
        <h3 className="text-sm font-semibold text-gray-700">Dispatch Tool to Ticket</h3>
        {dispatchableTickets.length > 0 && (
          <TableExport 
            data={dispatchableTickets.map(ticket => ({
              id: ticket.id,
              title: ticket.title,
              project: getProjectInfo(ticket.project_id).label,
              project_path: getProjectInfo(ticket.project_id).path,
              status: ticket.status?.replace('_', ' ') || 'todo',
              priority: ticket.priority || 'medium',
              current_tool: ticket.assigned_tool?.tool || 'None',
              tool_mode: ticket.assigned_tool?.agent_mode || '',
              current_tool_environment: tools.find(tool => tool.name === ticket.assigned_tool?.tool)?.environment?.name || '',
              current_tool_environment_kind: tools.find(tool => tool.name === ticket.assigned_tool?.tool)?.environment?.kind || '',
              description: ticket.description || ''
            }))}
            filename="tickets"
            title="Tickets"
          />
        )}
      </div>
      {unavailableTools.length > 0 && (
        <div className="mb-3 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800">
          <div className="font-medium">Unavailable tools are hidden from dispatch</div>
          <div className="mt-1">
            {availableTools.length > 0
              ? `Available tools: ${availableTools.map(tool => tool.name).join(", ")}.`
              : "No tools are currently available for dispatch."}
          </div>
          <div className="mt-1 text-amber-700">
            Hidden: {unavailableTools.map(tool => tool.name).join(", ")}. Open Tool Health to see why they are unavailable.
          </div>
        </div>
      )}
      <div className="overflow-x-auto">
        <table className="w-full text-sm">
          <thead>
            <tr className="border-b border-gray-200 text-xs text-gray-500 uppercase">
              <th className="text-left py-2 px-3">Ticket</th>
              <th className="text-left py-2 px-3">Title</th>
              <th className="text-left py-2 px-3">Project</th>
              <th className="text-left py-2 px-3">Status</th>
              <th className="text-left py-2 px-3">Priority</th>
              <th className="text-left py-2 px-3">Current Tool</th>
              <th className="text-left py-2 px-3">Assign Tool</th>
            </tr>
          </thead>
          <tbody>
            {dispatchableTickets.map(ticket => {
              const currentTool = ticket.assigned_tool;
              const toolInfo = currentTool ? tools.find(t => t.name === currentTool.tool) : null;
              const projectInfo = getProjectInfo(ticket.project_id);
              const projectDir = projectInfo.project?.local_path || "";
              
              return (
                <tr key={ticket.id} className="border-b border-gray-100 hover:bg-gray-50">
                  <td className="py-2 px-3 font-mono text-xs text-gray-600">{ticket.id}</td>
                  <td className="py-2 px-3">
                    <div className="max-w-[200px] truncate" title={ticket.title}>
                      {ticket.title}
                    </div>
                  </td>
                  <td className="py-2 px-3">
                    <div className="max-w-[240px] flex flex-col gap-0.5" title={projectInfo.path || projectInfo.label}>
                      <span className="text-xs font-medium text-gray-700 truncate">
                        {projectInfo.label}
                      </span>
                      <span className="text-xs font-mono text-gray-400 truncate">
                        {projectInfo.path || projectInfo.label}
                      </span>
                    </div>
                  </td>
                  <td className="py-2 px-3">
                    {renderStatusBadge(ticket)}
                  </td>
                  <td className="py-2 px-3">
                    {renderPriorityBadge(ticket)}
                  </td>
                  <td className="py-2 px-3">
                    {renderCurrentTool(currentTool, toolInfo)}
                  </td>
                  <td className="py-2 px-3">
                    <div className="flex items-center gap-1">
                      <select
                        value=""
                        onChange={(e) => {
                          if (e.target.value) {
                            const [toolName, mode] = e.target.value.split(':');
                            handleToolSelect(ticket.id, toolName, mode, projectDir);
                            e.target.value = '';
                          }
                        }}
                        disabled={dispatching === ticket.id || availableTools.length === 0}
                        className="text-xs border rounded px-2 py-1 disabled:opacity-50"
                      >
                        <option value="">Assign tool...</option>
                        {availableTools.map(tool => (
                          <optgroup key={tool.name} label={tool.name}>
                            <option value={`${tool.name}`}>
                              {tool.name} (default mode){tool.environment?.name ? ` · env: ${tool.environment.name}` : ''}
                            </option>
                            {tool.modes?.map(mode => (
                              <option key={mode} value={`${tool.name}:${mode}`}>
                                {tool.name} ({mode}){tool.environment?.name ? ` · env: ${tool.environment.name}` : ''}
                              </option>
                            ))}
                          </optgroup>
                        ))}
                      </select>
                      {dispatching === ticket.id && (
                        <span className="text-xs text-blue-600">Dispatching...</span>
                      )}
                    </div>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
      <p className="text-xs text-gray-400 mt-3">
        Select a tool from the dropdown to dispatch it to the ticket. Unavailable tools are hidden, and tools with multiple modes will show mode options.
      </p>
    </div>
  );
}

// ── QueueSummary ────────────────────────────────────────────

function QueueSummary({ queue, onStartStop }) {
  if (!queue) return null;
  return (
    <div className="flex items-center gap-4 text-sm">
      <span className="text-gray-500">Queue: <strong>{queue.queue_size || 0}</strong> pending</span>
      <span className="text-gray-500">Total jobs: <strong>{queue.total_jobs || 0}</strong></span>
      {queue.by_status && Object.entries(queue.by_status).map(([s, c]) => (
        <span key={s} className="text-xs text-gray-400">{s}: {c}</span>
      ))}
      <span className={`text-xs font-medium ${queue.running ? "text-emerald-600" : "text-gray-400"}`}>
        {queue.running ? "● Running" : "○ Stopped"}
      </span>
      <button onClick={() => onStartStop(!queue.running)}
        className={`text-xs px-2 py-1 rounded ${queue.running ? "bg-red-100 text-red-600 hover:bg-red-200" : "bg-emerald-100 text-emerald-600 hover:bg-emerald-200"}`}>
        {queue.running ? "Stop" : "Start"}
      </button>
    </div>
  );
}

// ── HealthTrends Component ───────────────────────────────────
function HealthTrends({ tools, history }) {
  const [showDetails, setShowDetails] = useState(false);

  const metrics = React.useMemo(() => {
    const now = Date.now();
    const oneHourAgo = now - 3600000;
    const oneDayAgo = now - 86400000;

    const recentChecks = history.filter(h => h.timestamp > oneHourAgo);
    const dayChecks = history.filter(h => h.timestamp > oneDayAgo);

    const degradationPattern = [];
    const recoveryPattern = [];

    tools.forEach(tool => {
      const toolHistory = history.filter(h => h.tool === tool.name).slice(0, 10);
      if (toolHistory.length >= 3) {
        const statuses = toolHistory.map(h => h.status);
        const wasOk = statuses.slice(1).every(s => s === "ok");
        const nowDegraded = tool.status !== "ok";
        
        if (wasOk && nowDegraded) degradationPattern.push(tool.name);
        if (!wasOk && tool.status === "ok") recoveryPattern.push(tool.name);
      }
    });

    return {
      recentChecks: recentChecks.length,
      dayChecks: dayChecks.length,
      degradationPattern,
      recoveryPattern,
      unstableTools: tools.filter(t => 
        history.filter(h => h.tool === t.name && h.status !== "ok").length > 2
      ).map(t => t.name),
    };
  }, [tools, history]);

  if (metrics.degradationPattern.length === 0 && metrics.recoveryPattern.length === 0 && metrics.unstableTools.length === 0) {
    return null;
  }

  return (
    <div className="mb-4 p-3 bg-amber-50 rounded-lg border border-amber-200">
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-2">
          <span className="text-amber-600">📊</span>
          <span className="text-sm font-medium text-amber-900">Health Trends</span>
        </div>
        <button onClick={() => setShowDetails(!showDetails)} className="text-xs text-amber-700 hover:underline">
          {showDetails ? "Hide" : "Details"}
        </button>
      </div>
      {showDetails && (
        <div className="mt-2 space-y-2 text-xs">
          {metrics.degradationPattern.length > 0 && (
            <div><span className="text-red-500">↓</span> <span className="font-medium">Recent degradation:</span> {metrics.degradationPattern.join(", ")}</div>
          )}
          {metrics.recoveryPattern.length > 0 && (
            <div><span className="text-emerald-500">↑</span> <span className="font-medium">Recovered:</span> {metrics.recoveryPattern.join(", ")}</div>
          )}
          {metrics.unstableTools.length > 0 && (
            <div><span className="text-amber-500">⚠</span> <span className="font-medium">Unstable tools:</span> {metrics.unstableTools.join(", ")}</div>
          )}
        </div>
      )}
    </div>
  );
}

// ── PredictiveAlerts Component ────────────────────────────────
function PredictiveAlerts({ tools, queue, jobs }) {
  const alerts = React.useMemo(() => {
    const result = [];
    const queuedJobs = queue?.jobs?.filter(j => j.status === "queued") || [];
    if (queuedJobs.length > 10) {
      result.push({ type: "warning", message: `Queue backlog: ${queuedJobs.length} jobs waiting` });
    }
    const failingTools = tools.filter(t => t.status === "unavailable");
    if (failingTools.length > 2) {
      result.push({ type: "critical", message: `${failingTools.length} tools unavailable` });
    }
    const stuckJobs = jobs.filter(j => j.status === "running" && j.started_at && (Date.now() - j.started_at * 1000) > 300000);
    if (stuckJobs.length > 0) {
      result.push({ type: "warning", message: `${stuckJobs.length} jobs running >5 min` });
    }
    return result;
  }, [tools, queue, jobs]);

  if (alerts.length === 0) return null;

  return (
    <div className="mb-4 space-y-2">
      {alerts.map((alert) => (
        <div key={`${alert.type}-${alert.message}`} className={`p-3 rounded-lg border ${alert.type === "critical" ? "bg-red-50 border-red-200" : "bg-amber-50 border-amber-200"}`}>
          <span className={alert.type === "critical" ? "text-red-500" : "text-amber-500"}>{alert.type === "critical" ? "🚨" : "⚠️"}</span>
          <span className={`text-sm font-medium ml-2 ${alert.type === "critical" ? "text-red-900" : "text-amber-900"}`}>{alert.message}</span>
        </div>
      ))}
    </div>
  );
}

// ── ToolHealthPanel ──────────────────────────────────────────

const HEALTH_STATUS_COLORS = {
  ok: "bg-emerald-100 text-emerald-700",
  degraded: "bg-amber-100 text-amber-700",
  unavailable: "bg-red-100 text-red-600",
};

function ToolHealthPanel() {
  const [health, setHealth] = useState(null);
  const [loading, setLoading] = useState(true);
  const [providers, setProviders] = useState({});
  const [availableProviders, setAvailableProviders] = useState([]);
  const [saving, setSaving] = useState(null);
  const [fixing, setFixing] = useState({});  // tool -> {action, status}
  const [bulkFixing, setBulkFixing] = useState(false);
  const [fixResults, setFixResults] = useState([]);
  const fixActionClass = {
    install: "bg-emerald-100 text-emerald-700",
    config: "bg-blue-100 text-blue-700",
    start_container: "bg-purple-100 text-purple-700",
    start_vm: "bg-orange-100 text-orange-700",
    info: "bg-gray-100 text-gray-600",
    retry: "bg-amber-100 text-amber-700",
  };

  const loadHealth = useCallback(async () => {
    setLoading(true);
    try {
      const data = await get("/dashboard/tools/healthcheck");
      setHealth(data || null);
      setProviders(data?.tool_providers || {});
      setAvailableProviders(data?.providers_configured || []);
    } catch (e) {
      console.error("Healthcheck failed:", e);
    } finally {
      setLoading(false);
    }
  }, []);

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

  const handleProviderChange = async (toolName, provider) => {
    setSaving(toolName);
    try {
      await post("/dashboard/tools/provider", { tool: toolName, provider });
      setProviders(prev => ({ ...prev, [toolName]: provider }));
    } catch (e) {
      console.error("Provider change failed:", e);
    } finally {
      setSaving(null);
    }
  };

  const handleAutoFix = async (toolName, fix) => {
    const action = fix.action;
    setFixing(prev => ({ ...prev, [toolName]: { action, status: 'running' } }));
    
    try {
      const result = await post("/dashboard/tools/autofix", {
        tool_name: toolName,
        fix_action: action,
        command: fix.command || "",
      });
      
      setFixResults(prev => [...prev, { tool: toolName, action, result, time: Date.now() }]);
      
      if (result.ticket_created) {
        // Human action ticket was created
        alert(`Ticket created for human action: ${result.ticket_created.id}`);
      }
      
      // Refresh health check to see if issue was resolved
      await loadHealth();
    } catch (e) {
      console.error("Auto-fix failed:", e);
      setFixResults(prev => [...prev, { tool: toolName, action, error: e.message, time: Date.now() }]);
    } finally {
      setFixing(prev => ({ ...prev, [toolName]: null }));
    }
  };

  const handleBulkAutoFix = async () => {
    setBulkFixing(true);
    setFixResults([]);
    
    try {
      const result = await post("/dashboard/tools/autofix/bulk", {});
      
      const executed = result.executed || 0;
      const humanRequired = result.human_required || 0;
      const failed = result.failed || 0;
      
      let message = `Auto-fix complete:\n`;
      message += `- ${executed} fixes executed automatically\n`;
      message += `- ${humanRequired} tickets created for human action\n`;
      if (failed > 0) {
        message += `- ${failed} fixes failed\n`;
      }
      
      alert(message);
      
      // Refresh health check
      await loadHealth();
    } catch (e) {
      console.error("Bulk auto-fix failed:", e);
      alert(`Bulk auto-fix failed: ${e.message || 'Unknown error'}`);
    } finally {
      setBulkFixing(false);
    }
  };

  const isAutoFixable = (action) => action === "install" || action === "start_container" || action === "start_vm";
  const requiresHuman = (action) => action === "info" || action === "config";

  if (loading) {
    return <Card title="Tool Health"><p className="text-sm text-gray-400">Running diagnostics...</p></Card>;
  }

  if (!health) {
    return <Card title="Tool Health"><p className="text-sm text-red-500">Healthcheck failed</p></Card>;
  }

  const summary = health?.summary;
  const tools = Array.isArray(health?.tools) ? health.tools : [];

  if (!summary) {
    return (
      <Card title="Tool Health">
        <div className="space-y-2">
          <p className="text-sm text-red-500">
            Healthcheck failed{health?.status ? ` (HTTP ${health.status})` : ""}
          </p>
          {health?.body && (
            <pre className="max-h-40 overflow-auto rounded bg-gray-50 px-3 py-2 text-xs text-gray-700 whitespace-pre-wrap">
              {typeof health.body === "string" ? health.body : JSON.stringify(health.body, null, 2)}
            </pre>
          )}
        </div>
      </Card>
    );
  }

  const degradedCount = (summary.degraded || 0) + (summary.unavailable || 0);

  return (
    <Card title={`Tool Health (${summary.ok} ok, ${summary.degraded} degraded, ${summary.unavailable} unavailable)`}>
      {/* Bulk auto-fix button */}
      {degradedCount > 0 && (
        <div className="mb-4 p-3 bg-blue-50 rounded-lg border border-blue-200">
          <div className="flex items-center justify-between">
            <div>
              <div className="text-sm font-medium text-blue-900">Auto-fix Available</div>
              <div className="text-xs text-blue-700">
                Automatically fix {degradedCount} issue{degradedCount > 1 ? 's' : ''} where possible.
                Human-required actions will create tickets.
              </div>
            </div>
            <button
              onClick={handleBulkAutoFix}
              disabled={bulkFixing}
              className="px-4 py-2 bg-blue-600 text-white text-sm rounded hover:bg-blue-700 disabled:opacity-50 font-medium"
            >
              {bulkFixing ? "Fixing..." : "🔧 Auto-fix All"}
            </button>
          </div>
        </div>
      )}

      {/* Fix results summary */}
      {fixResults.length > 0 && (
        <div className="mb-4 p-3 bg-gray-50 rounded-lg border border-gray-200">
          <div className="text-xs font-medium text-gray-700 mb-2">Recent Fix Results</div>
          <div className="space-y-1 max-h-32 overflow-auto">
            {fixResults.slice(-5).map((res) => {
              let resultDotClass = 'bg-amber-500';
              if (res.result?.executed) {
                resultDotClass = 'bg-emerald-500';
              } else if (res.result?.ticket_created) {
                resultDotClass = 'bg-purple-500';
              } else if (res.error) {
                resultDotClass = 'bg-red-500';
              }
              return (
              <div key={`${res.tool}-${res.action}-${res.time || ''}`} className="text-xs flex items-center gap-2">
                <span className={`w-2 h-2 rounded-full ${resultDotClass}`} />
                <span className="font-medium">{res.tool}</span>
                <span className="text-gray-500">{res.action}</span>
                {res.result?.executed && <span className="text-emerald-600">✓ fixed</span>}
                {res.result?.ticket_created && <span className="text-purple-600">🎫 ticket created</span>}
                {res.error && <span className="text-red-600">✗ {res.error}</span>}
              </div>
              );
            })}
          </div>
        </div>
      )}

      <div className="space-y-3">
        {tools.map(tool => {
          const statusClass = HEALTH_STATUS_COLORS[tool.status] || HEALTH_STATUS_COLORS.unavailable;
          const isHuman = tool.name === "human" || tool.category === "human";
          const isFixing = fixing[tool.name];
          const providerIsConfigured = tool.provider_configured !== false;
          return (
            <div key={tool.name} className="border border-gray-200 rounded-lg p-3">
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-2">
                  <span className="font-medium text-sm text-gray-900">{tool.name}</span>
                  <span className={`text-xs px-2 py-0.5 rounded-full ${statusClass}`}>{tool.status}</span>
                  <span className="text-xs text-gray-400">{tool.category}</span>
                  <span className={`text-xs px-2 py-0.5 rounded-full ${providerIsConfigured ? 'bg-emerald-50 text-emerald-700' : 'bg-gray-100 text-gray-500'}`}>
                    {providerIsConfigured ? 'provider ok' : 'provider missing'}
                  </span>
                  {tool.binary_found && <span className="text-xs text-emerald-600">binary OK</span>}
                  {tool.docker_instance && <span className="text-xs text-blue-600">docker OK</span>}
                  {tool.kvm_instance && <span className="text-xs text-purple-600">kvm OK</span>}
                  {tool.vm_instance && <span className="text-xs text-orange-600">vm OK</span>}
                  {isHuman && <span className="text-xs text-gray-500">manual</span>}
                </div>
                <div className="flex items-center gap-2">
                  {isHuman ? (
                    <span className="text-xs text-gray-500">Manual handoff — no provider needed</span>
                  ) : (
                    <>
                      <span className="text-xs text-gray-500">LLM Provider:</span>
                      <select
                        value={providers[tool.name] || "openrouter"}
                        onChange={(e) => handleProviderChange(tool.name, e.target.value)}
                        disabled={saving === tool.name}
                        className="text-xs border rounded px-2 py-1 disabled:opacity-50"
                      >
                        {availableProviders.map(p => (
                          <option key={p} value={p}>{p}</option>
                        ))}
                      </select>
                      {saving === tool.name && <span className="text-xs text-blue-500">Saving...</span>}
                    </>
                  )}
                </div>
              </div>

              {tool.issues.length > 0 && (
                <div className="mt-2 space-y-1">
                  {tool.issues.map((issue) => (
                    <div key={issue} className="text-xs text-amber-700 bg-amber-50 rounded px-2 py-1">
                      {issue}
                    </div>
                  ))}
                </div>
              )}

              {tool.fixes.length > 0 && (
                <div className="mt-2 space-y-1">
                  {tool.fixes.map((fix) => (
                    <div key={`${fix.label}-${fix.action}`} className="flex items-start gap-2 text-xs bg-gray-50 rounded px-2 py-1.5">
                      <span className={`px-1.5 py-0.5 rounded font-medium ${fixActionClass[fix.action] || "bg-gray-100 text-gray-600"}`}>{fix.action}</span>
                      <div className="flex-1">
                        <span className="font-medium text-gray-800">{fix.label}</span>
                        <span className="text-gray-500 ml-1">— {fix.description}</span>
                        {fix.command && (
                          <pre className="mt-1 bg-gray-900 text-green-300 rounded px-2 py-1 font-mono text-[11px] overflow-x-auto">{fix.command}</pre>
                        )}
                      </div>
                      {/* Auto-fix button */}
                      {isAutoFixable(fix.action) && (
                        <button
                          onClick={() => handleAutoFix(tool.name, fix)}
                          disabled={isFixing}
                          className="px-2 py-1 bg-emerald-600 text-white rounded text-xs hover:bg-emerald-700 disabled:opacity-50 font-medium"
                        >
                          {isFixing?.action === fix.action ? "Running..." : "Auto-fix"}
                        </button>
                      )}
                      {requiresHuman(fix.action) && (
                        <button
                          onClick={() => handleAutoFix(tool.name, fix)}
                          disabled={isFixing}
                          className="px-2 py-1 bg-purple-600 text-white rounded text-xs hover:bg-purple-700 disabled:opacity-50 font-medium"
                          title="Create ticket for human action"
                        >
                          {isFixing?.action === fix.action ? "Creating..." : "Create Ticket"}
                        </button>
                      )}
                    </div>
                  ))}
                </div>
              )}

              {isHuman && (
                <div className="mt-2 rounded-lg bg-gray-50 px-3 py-2 text-xs text-gray-600">
                  Use the System tab to create human tasks for tokens, installs, approvals, onboarding clicks, or provider account setup.
                </div>
              )}
            </div>
          );
        })}
      </div>
      <div className="mt-3 flex justify-end">
        <button
          onClick={loadHealth}
          className="text-xs px-3 py-1 rounded bg-blue-100 text-blue-700 hover:bg-blue-200 font-medium"
        >
          Re-run Diagnostics
        </button>
      </div>
    </Card>
  );
}

// ── ToolsPanel (main) ───────────────────────────────────────

function ToolsPanel({ selectedTool, setSelectedTool }) {
  const { tools, queue, jobs, loading, refresh } = useTools();
  const [selectedJobId, setSelectedJobId] = useState(null);
  const [selectedJob, setSelectedJob] = useState(null);
  const [selectedJobLoading, setSelectedJobLoading] = useState(false);
  const [selectedJobError, setSelectedJobError] = useState("");
  const [repairResult, setRepairResult] = useState(null);

  const handleStartStop = async (start) => {
    await post(`/dashboard/tools/queue/${start ? "start" : "stop"}`, {});
    refresh();
  };

  const handleRetryJob = useCallback(async (job) => {
    try {
      await post(`/dashboard/tools/queue/jobs/${job.id}/retry`, {});
      setRepairResult(null);
      setTimeout(() => refresh(), 500);
    } catch (e) {
      console.error("Retry failed:", e);
      setRepairResult({ error: e?.message || "Nie udało się ponowić joba." });
      setSelectedJobError("Nie udało się ponowić joba.");
    }
  }, [refresh]);

  const handleRepairJob = useCallback(async (job) => {
    setRepairResult(null);
    try {
      const data = await post(`/dashboard/tools/queue/jobs/${job.id}/repair`, {});
      setRepairResult(data);
      // Auto-select this job to show repair results
      if (selectedJobId !== job.id) {
        setSelectedJobId(job.id);
        setSelectedJob(job);
        try {
          const details = await get(`/dashboard/tools/queue/jobs/${job.id}`);
          setSelectedJob(details);
        } catch (refreshError) {
          console.warn("Failed to refresh repaired job details:", refreshError);
        }
      }
    } catch (e) {
      setRepairResult({ error: `Repair request failed: ${e.message || e}` });
    }
  }, [selectedJobId]);

  const handleSelectJob = useCallback(async (job) => {
    if (!job) return;
    if (selectedJobId === job.id) {
      setSelectedJobId(null);
      setSelectedJob(null);
      setSelectedJobError("");
      setRepairResult(null);
      return;
    }

    setSelectedJobId(job.id);
    setSelectedJob(job);
    setSelectedJobLoading(true);
    setSelectedJobError("");
    setRepairResult(null);

    try {
      const data = await get(`/dashboard/tools/queue/jobs/${job.id}`);
      setSelectedJob(data);
    } catch (e) {
      console.error("Failed to load job details:", e);
      setSelectedJobError("Nie udało się pobrać logów joba.");
    } finally {
      setSelectedJobLoading(false);
    }
  }, [selectedJobId]);

  if (loading) {
    return <Card title="Tools"><p className="text-gray-400 text-sm">Loading...</p></Card>;
  }

  const available = tools.filter(t => t.available);
  const unavailable = tools.filter(t => !t.available);

  return (
    <div className="space-y-6">
      <Card title={`Tool Registry (${available.length}/${tools.length} available)`}>
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
          {available.map(t => (
            <ToolCard 
              key={t.name} 
              tool={t} 
              isSelected={selectedTool === t.name}
              onSelect={setSelectedTool}
            />
          ))}
          {unavailable.map(t => (
            <ToolCard 
              key={t.name} 
              tool={t} 
              isSelected={selectedTool === t.name}
              onSelect={setSelectedTool}
            />
          ))}
        </div>
      </Card>

      <PredictiveAlerts tools={tools} queue={queue} jobs={jobs} />

      <ToolHealthPanel />

      <Card title="Dispatch">
        <TicketDispatchTable tools={tools} onDispatch={refresh} />
      </Card>

      <Card title="Job Queue">
        <QueueSummary queue={queue} onStartStop={handleStartStop} />
        {jobs.length > 0 ? (
          <div className="mt-3">
            <div className="flex justify-between items-center mb-2">
              <span className="text-xs text-gray-500">Recent jobs ({jobs.length})</span>
              <TableExport 
                data={jobs.map(job => ({
                  ...job,
                  ticket_title: job.ticket?.title || job.ticket?.task || '',
                  ticket_status: job.ticket?.status || '',
                  ticket_priority: job.ticket?.priority || '',
                  ticket_type: job.ticket?.type || '',
                  ticket_project_id: job.ticket?.project_id || '',
                  ticket_assigned_tool: job.ticket?.assigned_tool?.tool || '',
                  ticket_assigned_mode: job.ticket?.assigned_tool?.agent_mode || '',
                  ticket_assigned_model: job.ticket?.assigned_tool?.model || '',
                  result_success: job.result?.success ?? null,
                  result_exit_code: job.result?.exit_code ?? null,
                  result_duration_s: job.result?.duration_s ?? null,
                  result_stdout_lines: job.result?.stdout_lines ?? 0,
                  result_stderr_lines: job.result?.stderr_lines ?? 0,
                  result_stdout: job.result?.stdout || '',
                  result_stderr: job.result?.stderr || '',
                  result_files_changed: Array.isArray(job.result?.files_changed) ? job.result.files_changed.join(', ') : '',
                }))}
                filename="job_queue"
                title="Job Queue"
              />
            </div>
            <div className="overflow-x-auto">
              <table className="w-full text-left">
                <thead>
                  <tr className="border-b border-gray-200 text-xs text-gray-500 uppercase">
                    <th className="py-2 px-3">ID</th>
                    <th className="py-2 px-3">Ticket</th>
                    <th className="py-2 px-3">Tool</th>
                    <th className="py-2 px-3">Mode</th>
                    <th className="py-2 px-3">Status</th>
                    <th className="py-2 px-3">Duration</th>
                    <th className="py-2 px-3">Error</th>
                    <th className="py-2 px-3">Logs</th>
                    <th className="py-2 px-3">Actions</th>
                  </tr>
                </thead>
                <tbody>
                  {jobs.map(j => (
                    <JobRow
                      key={j.id}
                      job={j}
                      onClick={handleSelectJob}
                      isSelected={selectedJobId === j.id}
                      onRetry={handleRetryJob}
                      onRepair={handleRepairJob}
                    />
                  ))}
                </tbody>
              </table>
            </div>
            <JobDetailsPanel
              job={selectedJob}
              loading={selectedJobLoading}
              error={selectedJobError}
              onClose={() => {
                setSelectedJobId(null);
                setSelectedJob(null);
                setSelectedJobError("");
                setRepairResult(null);
              }}
              onRetry={handleRetryJob}
              onRepair={handleRepairJob}
              repairResult={repairResult}
            />
          </div>
        ) : (
          <p className="mt-3 text-sm text-gray-400">No jobs yet. Dispatch a tool to a ticket to start.</p>
        )}
      </Card>
    </div>
  );
}
