// components/tasks.jsx — TasksPanel: unified Tickets + Tools view.
// Board with inline job logs, queue controls, QuickDo at top.
// Depends on: React (useState, useCallback, useEffect, useMemo),
//             Card, StatusBadge, get, post, put from api.js,
//             QuickDo from home.jsx,
//             TICKET_STATUSES, STATUS_COLORS, PRIORITY_COLORS, TYPE_ICONS, TOOLS from tickets.jsx,
//             JOB_STATUS_COLORS from tools.jsx

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

function _resolveToolEnvironment(toolName, tools) {
  if (!toolName || !Array.isArray(tools)) return null;
  return tools.find((tool) => tool.name === toolName)?.environment || null;
}

function _resolveJobEnvironment(ticket, job, tools) {
  if (!job) return null;

  if (job.environment && typeof job.environment === "object") {
    return {
      id: job.environment.id || "",
      name: job.environment.name || "",
      kind: job.environment.kind || "",
      target: job.environment.target || "",
    };
  }

  if (job.environment_name || job.environment_kind || job.environment_target) {
    return {
      id: job.environment_id || "",
      name: job.environment_name || "",
      kind: job.environment_kind || "",
      target: job.environment_target || "",
    };
  }

  const assignedToolName = ticket?.assigned_tool?.tool || job.tool || "";
  return _resolveToolEnvironment(assignedToolName, tools);
}

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

  const convertToCSV = (rows) => {
    if (!rows || rows.length === 0) return '';

    const headers = Object.keys(rows[0]);
    const csvHeaders = headers.join(',');

    const csvRows = rows.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"
          aria-label="Close export menu"
          className="fixed inset-0 z-0 cursor-default border-0 bg-transparent p-0"
          onClick={() => setShowMenu(false)}
        />
      )}
    </div>
  );
}

// ── Hook: useTasksData — tickets + queue + tools in one ─────

function useTasksData() {
  const [tickets, setTickets] = useState([]);
  const [sprints, setSprints] = useState([]);
  const [projects, setProjects] = useState([]);
  const [queue, setQueue] = useState(null);
  const [jobs, setJobs] = useState([]);
  const [tools, setTools] = useState([]);
  const [loading, setLoading] = useState(true);

  const refresh = useCallback(async () => {
    setLoading(true);
    try {
      const [tData, sData, pData, qData, toolData] = await Promise.all([
        get("/dashboard/tickets"),
        get("/dashboard/sprints"),
        get("/dashboard/projects"),
        get("/dashboard/tools/queue"),
        get("/dashboard/tools/"),
      ]);
      setTickets(tData?.tickets || []);
      setSprints(sData?.sprints || []);
      setProjects(Array.isArray(pData) ? pData : []);
      setQueue(qData);
      setJobs(qData?.recent_jobs || []);
      setTools(toolData?.tools || []);
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    refresh();
    const timer = setInterval(refresh, 12000);
    return () => clearInterval(timer);
  }, [refresh]);

  return { tickets, sprints, projects, queue, jobs, tools, loading, refresh };
}

// ── TaskCardHeader — title + priority + status badges ────────

function TaskCardHeader({ ticket }) {
  const statusColor = STATUS_COLORS[ticket.status] || "bg-gray-100 text-gray-600";
  const priorityColor = PRIORITY_COLORS[ticket.priority] || "bg-gray-100 text-gray-600";
  const typeIcon = TYPE_ICONS[ticket.type] || "📋";
  return (
    <div className="flex items-start justify-between gap-2">
      <div className="flex items-center gap-1.5 min-w-0">
        <span title={ticket.type}>{typeIcon}</span>
        <span className="font-medium text-gray-900 truncate">{ticket.task || ticket.title}</span>
      </div>
      <div className="flex items-center gap-1 shrink-0">
        <span className={`text-[10px] px-1.5 py-0.5 rounded-full ${priorityColor}`}>{ticket.priority}</span>
        <span className={`text-[10px] px-1.5 py-0.5 rounded-full ${statusColor}`}>{ticket.status?.replace("_", " ")}</span>
      </div>
    </div>
  );
}

// ── TaskCardMeta — ticket id + tool + job execution status ───

function TaskCardMeta({ ticket, ticketJob, tools }) {
  const job = ticketJob || ticket.last_job;
  const resultSuccess = job?.result?.success ?? job?.success;
  let resultLabel = null;
  let resultLabelClass = "text-gray-500";
  if (typeof resultSuccess === "boolean") {
    resultLabel = resultSuccess ? "success" : "failed";
    resultLabelClass = resultSuccess ? "text-emerald-600" : "text-red-600";
  } else if (job) {
    const isActiveJob = job.status === "queued" || job.status === "running";
    resultLabel = isActiveJob ? "pending" : "unknown";
  }
  const environment = _resolveJobEnvironment(ticket, job, tools);
  return (
    <div className="mt-1.5 space-y-1">
      <div className="flex items-center gap-2 text-xs text-gray-400">
        <span className="font-mono">{ticket.id?.slice(0, 12)}</span>
        {ticket.assigned_tool && (
          <span className="px-1.5 py-0.5 bg-indigo-50 text-indigo-600 rounded">
            🔧 {ticket.assigned_tool.tool}
          </span>
        )}
        {environment && (
          <span className="px-1.5 py-0.5 bg-emerald-50 text-emerald-700 rounded border border-emerald-100" title={environment.target || environment.name}>
            env: {environment.name}{environment.kind ? ` · ${environment.kind.replace("_", " ")}` : ""}
          </span>
        )}
      </div>
      {job && (
        <div className="flex flex-wrap items-center gap-x-2 gap-y-1 text-[11px] text-gray-500">
          {job.project_dir && (
            <span className="font-mono truncate max-w-[220px]" title={job.project_dir}>
              📁 {job.project_dir}
            </span>
          )}
          {environment?.target && (
            <span className="font-mono truncate max-w-[260px]" title={environment.target}>
              🖥 {environment.target}
            </span>
          )}
          {job.prompt_length ? <span>{job.prompt_length} chars prompt</span> : null}
          {resultLabel && (
            <span className={resultLabelClass}>
              tool result: {resultLabel}
            </span>
          )}
        </div>
      )}
      {job && <TaskJobBadge job={job} />}
    </div>
  );
}

// ── TaskJobBadge — compact execution status line ─────────────

function TaskJobBadge({ job }) {
  if (!job) return null;

  const statusIconMap = {
    queued: "○",
    running: "⏳",
    done: "✅",
    failed: "❌",
    cancelled: "⊘",
  };
  const statusIcon = statusIconMap[job.status] || "?";

  const duration = job.duration_s > 0 ? `${Math.round(job.duration_s)}s` : null;
  const filesChanged = job.result?.files_changed;
  const hasOutput = job.result?.stdout;
  const resultSuccess = job.result?.success ?? job.success;
  let resultLabel = "unknown";
  let resultLabelClass = "text-gray-400";
  if (typeof resultSuccess === "boolean") {
    resultLabel = resultSuccess ? "success" : "failed";
    resultLabelClass = resultSuccess ? "text-emerald-500" : "text-red-500";
  } else if (job.status === "queued" || job.status === "running") {
    resultLabel = "pending";
  }

  let statusClass = "bg-gray-50 text-gray-500";
  if (job.status === "done") {
    statusClass = "bg-emerald-50 text-emerald-700";
  } else if (job.status === "failed") {
    statusClass = "bg-red-50 text-red-600";
  } else if (job.status === "running") {
    statusClass = "bg-amber-50 text-amber-700";
  }

  return (
    <div className={`flex items-center gap-1.5 text-[11px] px-2 py-1 rounded ${statusClass}`}>
      <span>{statusIcon}</span>
      <span className="font-medium">{job.status}</span>
      {job.tool && <span className="text-gray-400">via {job.tool}</span>}
      {resultLabel && (
        <span className={resultLabelClass}>
          • tool result: {resultLabel}
        </span>
      )}
      {duration && <span className="text-gray-400">• {duration}</span>}
      {filesChanged?.length > 0 && (
        <span className="text-gray-400">• {filesChanged.length} file{filesChanged.length > 1 ? "s" : ""}</span>
      )}
      {job.status === "done" && hasOutput && (
        <span className="text-emerald-500">• has output</span>
      )}
      {job.error && (
        <span className="text-red-400 truncate max-w-[150px]" title={job.error}>• {job.error}</span>
      )}
    </div>
  );
}

// ── TaskInlineLogs — poll + display stdout for running jobs ──

function TaskInlineLogs({ jobId, onDone }) {
  const [logs, setLogs] = useState("");

  useEffect(() => {
    if (!jobId) return;
    const timer = setInterval(async () => {
      const j = await get(`/dashboard/tools/queue/jobs/${jobId}`);
      if (j?.result?.stdout) setLogs(j.result.stdout.slice(-500));
      if (j?.status === "done" || j?.status === "failed") {
        clearInterval(timer);
        if (onDone) onDone();
      }
    }, 3000);
    return () => clearInterval(timer);
  }, [jobId]);

  return (
    <pre className="mt-2 p-2 bg-gray-900 text-green-300 text-[11px] rounded overflow-auto max-h-[120px] font-mono leading-tight">
      {logs || "Waiting for output..."}
    </pre>
  );
}

// ── TaskJobError — show error for failed jobs ────────────────

function TaskJobError({ error }) {
  if (!error) return null;
  return (
    <div className="mt-2 p-2 bg-red-50 border border-red-200 rounded text-xs text-red-700 truncate">
      {error}
    </div>
  );
}

// ── TaskJobResult — collapsible output for done jobs ─────────

function TaskJobResult({ result }) {
  const [expanded, setExpanded] = useState(false);
  if (!result) return null;
  const output = result.stdout || "";
  const files = result.files_changed || [];
  const preview = output.slice(-200).trim();
  return (
    <div className="mt-2">
      <button onClick={() => setExpanded(!expanded)}
        className="text-[11px] text-emerald-600 hover:text-emerald-800 font-medium">
        {expanded ? "▾ Hide output" : "▸ Show output"}
        {files.length > 0 && ` (${files.length} file${files.length > 1 ? "s" : ""} changed)`}
      </button>
      {expanded && (
        <div className="mt-1 space-y-1">
          {files.length > 0 && (
            <div className="text-[11px] text-gray-500">
              {files.slice(0, 8).map((f) => <div key={f} className="font-mono truncate">📄 {f}</div>)}
              {files.length > 8 && <div className="text-gray-400">...+{files.length - 8} more</div>}
            </div>
          )}
          {output && (
            <pre className="p-2 bg-gray-900 text-green-300 text-[11px] rounded overflow-auto max-h-[120px] font-mono leading-tight">
              {preview}
            </pre>
          )}
        </div>
      )}
    </div>
  );
}

// ── TaskStatusActions — expandable status change buttons ─────

function TaskStatusActions({ ticket, onRefresh }) {
  const [expanded, setExpanded] = useState(false);

  const handleStatusChange = async (newStatus) => {
    await put(`/dashboard/tickets/${ticket.id}`, { status: newStatus });
    if (onRefresh) onRefresh();
  };

  return (
    <div>
      <button onClick={() => setExpanded(!expanded)}
        className="mt-1.5 text-xs text-gray-400 hover:text-gray-600">
        {expanded ? "▴ Less" : "▸ Actions"}
      </button>
      {expanded && (
        <div className="mt-2 pt-2 border-t border-gray-100 flex flex-wrap gap-1">
          {TICKET_STATUSES.filter(s => s !== ticket.status).map(s => (
            <button key={s} onClick={() => handleStatusChange(s)}
              className="text-[11px] px-2 py-0.5 rounded border border-gray-200 hover:bg-gray-50">
              {s.replace("_", " ")}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

// ── TaskTicketCard — thin shell composing sub-components ─────

function TaskTicketCard({ ticket, jobs, tools, onRefresh }) {
  // Prefer real-time job from queue, fallback to server-enriched last_job
  const liveJob = jobs.find(j => j.ticket_id === ticket.id);
  const job = liveJob || ticket.last_job;
  const isActive = job && (job.status === "running" || job.status === "queued");
  const isDone = job?.status === "done";
  const isFailed = job?.status === "failed";

  return (
    <div className={`bg-white rounded-lg border p-3 text-sm transition-shadow hover:shadow-sm ${
      isActive ? "border-amber-300 ring-1 ring-amber-100" :
      isDone ? "border-emerald-200" :
      isFailed ? "border-red-200" :
      "border-gray-200"
    }`}>
      <TaskCardHeader ticket={ticket} />
      <TaskCardMeta ticket={ticket} ticketJob={job} tools={tools} />
      {isActive && <TaskInlineLogs jobId={job.id} onDone={onRefresh} />}
      {isFailed && <TaskJobError error={job.error} />}
      {isDone && job.result?.stdout && <TaskJobResult result={job.result} />}
      <TaskStatusActions ticket={ticket} onRefresh={onRefresh} />
    </div>
  );
}

// ── QueueBar — compact queue controls ────────────────────────

function QueueBar({ queue, onStartStop }) {
  if (!queue) return null;
  const byStatus = queue.by_status || {};
  return (
    <div className="flex items-center gap-4 px-4 py-2 bg-gray-50 rounded-lg border text-sm">
      <span className="font-medium text-gray-700">Queue</span>
      <span className="text-gray-500"><strong>{queue.queue_size || 0}</strong> pending</span>
      {byStatus.running > 0 && <span className="text-amber-600"><strong>{byStatus.running}</strong> running</span>}
      {byStatus.done > 0 && <span className="text-emerald-600"><strong>{byStatus.done}</strong> done</span>}
      {byStatus.failed > 0 && <span className="text-red-500"><strong>{byStatus.failed}</strong> failed</span>}
      <div className="ml-auto flex items-center gap-2">
        <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.5 py-1 rounded font-medium ${
            queue.running
              ? "bg-red-100 text-red-600 hover:bg-red-200"
              : "bg-emerald-100 text-emerald-600 hover:bg-emerald-200"
          }`}>
          {queue.running ? "⏸ Pause" : "▶ Start"}
        </button>
      </div>
    </div>
  );
}

function _extractTags(text) {
  const tags = [];
  const lower = text.toLowerCase();
  const keywords = {
    bug: ['bug', 'crash', 'error', 'fix', 'broken', 'fails', 'exception'],
    feature: ['feature', 'add', 'new', 'implement', 'create', 'build'],
    refactor: ['refactor', 'cleanup', 'optimize', 'simplify', 'restructure'],
    test: ['test', 'spec', 'coverage', 'unit test', 'e2e'],
    docs: ['docs', 'documentation', 'readme', 'docstring', 'comment'],
    ui: ['ui', 'design', 'layout', 'css', 'style', 'frontend'],
    api: ['api', 'endpoint', 'rest', 'graphql', 'backend'],
    security: ['security', 'auth', 'token', 'login', 'password', 'encrypt'],
    performance: ['performance', 'slow', 'fast', 'speed', 'cache', 'optimize'],
    database: ['db', 'database', 'sql', 'migration', 'schema'],
    deploy: ['deploy', 'release', 'ci/cd', 'pipeline', 'docker'],
  };
  for (const [tag, words] of Object.entries(keywords)) {
    if (words.some(w => lower.includes(w))) tags.push(tag);
  }
  return tags.slice(0, 5);
}

const HUMAN_REQUEST_TYPES = [
  { id: "api_token", label: "API token / secret" },
  { id: "dependency_install", label: "Install dependency" },
  { id: "clickthrough", label: "Click-through / onboarding" },
  { id: "provider_account", label: "Create provider account" },
  { id: "verification", label: "Verify / approve" },
  { id: "other", label: "Other manual step" },
];

function _humanRequestLabel(requestType) {
  return HUMAN_REQUEST_TYPES.find(type => type.id === requestType)?.label || "Manual task";
}

// ── QuickTaskCreator — unified simple task creation ──────────

function QuickTaskCreator({ projects, sprints, tools, onCreated, defaultTool = "auto" }) {
  const [task, setTask] = useState("");
  const [projectId, setProjectId] = useState("");
  const [sprintId, setSprintId] = useState("");
  const [priority, setPriority] = useState("medium");
  const [type, setType] = useState("task");
  const [tool, setTool] = useState(defaultTool);
  const [submitting, setSubmitting] = useState(false);
  const [feedback, setFeedback] = useState(null);
  const [autoTags, setAutoTags] = useState([]);

  // Auto-extract tags when task text changes
  useEffect(() => {
    setAutoTags(_extractTags(task));
  }, [task]);

  // Filter sprints by selected project
  const availableSprints = projectId
    ? sprints.filter(s => s.project_id === projectId || !s.project_id)
    : sprints;

  const handleSubmit = async () => {
    if (!task.trim() || submitting) return;
    setSubmitting(true);
    setFeedback(null);

    const payload = {
      task: task.trim(),
      project_id: projectId || null,
      sprint_id: sprintId || null,
      priority,
      type,
      tags: autoTags,
      status: "todo",
    };

    // Add tool assignment if specified
    if (tool && tool !== "auto") {
      payload.tool = tool;
    }

    try {
      const r = await post("/dashboard/tickets", payload);
      if (r?.id) {
        const toolInfo = r.assigned_tool ? ` -> ${r.assigned_tool.tool}` : "";
        setFeedback({ ok: true, text: `${r.id} created${toolInfo}` });
        setTask("");
        setProjectId("");
        setSprintId("");
        setPriority("medium");
        setType("task");
        setTool("auto");
        setAutoTags([]);
        if (onCreated) onCreated(r);
      } else {
        setFeedback({ ok: false, text: "Failed to create ticket" });
      }
    } catch (e) {
      setFeedback({ ok: false, text: String(e) });
    }
    setSubmitting(false);
    setTimeout(() => setFeedback(null), 4000);
  };

  const onKey = (e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); handleSubmit(); } };
  const canSubmit = task.trim() && !submitting;

  const toolOptions = React.useMemo(() => {
    const options = [
      { key: "builtin:auto", value: "auto", label: "Auto assign", icon: "🤖" },
      { key: "builtin:human", value: "human", label: "Human task", icon: "👤" },
      ...tools.map(t => ({ key: `tool:${t.name}`, value: t.name, label: t.name, icon: "🔧" })),
    ];

    const seen = new Set();
    return options.filter(option => {
      if (seen.has(option.value)) return false;
      seen.add(option.value);
      return true;
    });
  }, [tools]);

  return (
    <Card title="Quick Create" className="bg-gradient-to-r from-blue-50 to-indigo-50 border-blue-200">
      <div className="space-y-3">
        {/* Main task input */}
        <div className="flex gap-2 items-start">
          <textarea
            value={task}
            onChange={e => setTask(e.target.value)}
            onKeyDown={onKey}
            placeholder="What needs to be done? Describe in one sentence..."
            rows={2}
            className="flex-1 px-3 py-2 border border-blue-200 rounded-lg text-sm focus:ring-2 focus:ring-blue-300 focus:outline-none resize-none"
            autoFocus
          />
          <button
            onClick={handleSubmit}
            disabled={!canSubmit}
            className="px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700 disabled:opacity-50 whitespace-nowrap h-fit"
          >
            {submitting ? "..." : "Create"}
          </button>
        </div>

        {/* Controls row */}
        <div className="flex flex-wrap gap-2 items-center">
          {/* Project selector */}
          <select
            value={projectId}
            onChange={e => { setProjectId(e.target.value); setSprintId(""); }}
            className="text-xs px-2 py-1.5 border border-blue-200 rounded text-gray-700 bg-white"
          >
            <option value="">No project</option>
            {projects.map(p => (
              <option key={p.id} value={p.id}>{p.name || p.id}</option>
            ))}
          </select>

          {/* Sprint selector (filtered by project) */}
          <select
            value={sprintId}
            onChange={e => setSprintId(e.target.value)}
            className="text-xs px-2 py-1.5 border border-blue-200 rounded text-gray-700 bg-white"
            disabled={!projectId}
          >
            <option value="">No sprint</option>
            {availableSprints.map(s => (
              <option key={s.id} value={s.id}>{s.name}</option>
            ))}
          </select>

          {/* Priority */}
          <select
            value={priority}
            onChange={e => setPriority(e.target.value)}
            className={`text-xs px-2 py-1.5 border rounded font-medium ${
              priority === "critical" ? "border-red-300 bg-red-50 text-red-700" :
              priority === "high" ? "border-orange-300 bg-orange-50 text-orange-700" :
              priority === "low" ? "border-gray-300 bg-gray-50 text-gray-700" :
              "border-blue-200 bg-white text-gray-700"
            }`}
          >
            <option value="low">Low</option>
            <option value="medium">Medium</option>
            <option value="high">High</option>
            <option value="critical">Critical</option>
          </select>

          {/* Type */}
          <select
            value={type}
            onChange={e => setType(e.target.value)}
            className="text-xs px-2 py-1.5 border border-blue-200 rounded text-gray-700 bg-white"
          >
            {TICKET_TYPES.map(t => (
              <option key={t} value={t}>{TYPE_ICONS[t]} {t.replace("_", " ")}</option>
            ))}
          </select>

          {/* Tool assignment */}
          <select
            value={tool}
            onChange={e => setTool(e.target.value)}
            className="text-xs px-2 py-1.5 border border-blue-200 rounded text-gray-700 bg-white"
          >
            {toolOptions.map(t => (
              <option key={t.key} value={t.value}>{t.icon} {t.label}</option>
            ))}
          </select>
        </div>

        {/* Auto tags preview */}
        {autoTags.length > 0 && (
          <div className="flex items-center gap-2 text-xs">
            <span className="text-gray-500">Auto tags:</span>
            <div className="flex gap-1">
              {autoTags.map(tag => (
                <span key={tag} className="px-1.5 py-0.5 bg-blue-100 text-blue-700 rounded-full">
                  {tag}
                </span>
              ))}
            </div>
          </div>
        )}

        {/* Feedback */}
        {feedback && (
          <p className={`text-xs ${feedback.ok ? "text-emerald-600" : "text-red-500"}`}>
            {feedback.text}
          </p>
        )}
      </div>
    </Card>
  );
}

// ── TasksPanel (main) ────────────────────────────────────────

function TasksPanel() {
  const { tickets, sprints, projects, queue, jobs, tools, loading, refresh } = useTasksData();
  const [view, setView] = useState("board"); // board | list | table
  const [filterStatus, setFilterStatus] = useState("");

  const filtered = React.useMemo(() => {
    if (!filterStatus) return tickets;
    return tickets.filter(t => t.status === filterStatus);
  }, [tickets, filterStatus]);

  const boardColumns = React.useMemo(() => {
    const cols = {};
    // Simplified board columns: Backlog, To Do, In Progress, Done, Failed
    const boardStatuses = ["backlog", "todo", "in_progress", "in_review", "done", "cancelled"];
    boardStatuses.forEach(s => { cols[s] = []; });
    filtered.forEach(t => {
      if (cols[t.status]) cols[t.status].push(t);
    });
    return cols;
  }, [filtered]);

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

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

  return (
    <div className="space-y-4">
      {/* Quick Create - unified task creation for tools and human */}
      <QuickTaskCreator projects={projects} sprints={sprints} tools={tools} onCreated={refresh} />

      {/* Queue controls */}
      <QueueBar queue={queue} onStartStop={handleStartStop} />

      {/* Toolbar */}
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-2">
          <div className="flex border rounded overflow-hidden">
            <button onClick={() => setView("board")}
              className={`px-3 py-1 text-xs font-medium ${view === "board" ? "bg-blue-600 text-white" : "bg-white text-gray-600 hover:bg-gray-50"}`}>
              Board
            </button>
            <button onClick={() => setView("list")}
              className={`px-3 py-1 text-xs font-medium ${view === "list" ? "bg-blue-600 text-white" : "bg-white text-gray-600 hover:bg-gray-50"}`}>
              List
            </button>
            <button onClick={() => setView("table")}
              className={`px-3 py-1 text-xs font-medium ${view === "table" ? "bg-blue-600 text-white" : "bg-white text-gray-600 hover:bg-gray-50"}`}>
              Table
            </button>
          </div>
          <select value={filterStatus} onChange={e => setFilterStatus(e.target.value)}
            className="text-xs px-2 py-1 border rounded text-gray-600">
            <option value="">All statuses</option>
            {TICKET_STATUSES.map(s => <option key={s} value={s}>{s.replace("_", " ")}</option>)}
          </select>
          <span className="text-xs text-gray-400">{filtered.length} task{filtered.length !== 1 ? "s" : ""}</span>
        </div>
        <button onClick={refresh} className="text-xs px-3 py-1 bg-gray-100 text-gray-600 rounded hover:bg-gray-200">
          ↻ Refresh
        </button>
      </div>

      {/* Board view */}
      {view === "board" && (
        <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-3">
          {TICKET_STATUSES.map(status => (
            <div key={status} className="bg-gray-50 rounded-lg p-2 min-h-[200px]">
              <h5 className={`text-xs font-medium px-2 py-1 rounded mb-2 ${STATUS_COLORS[status] || "bg-gray-100 text-gray-600"}`}>
                {status.replace("_", " ")} ({boardColumns[status]?.length || 0})
              </h5>
              <div className="space-y-2">
                {(boardColumns[status] || []).map(t => (
                  <TaskTicketCard key={t.id} ticket={t} jobs={jobs} tools={tools} onRefresh={refresh} />
                ))}
              </div>
            </div>
          ))}
        </div>
      )}

      {/* List view */}
      {view === "list" && (
        <div className="space-y-2">
          {filtered.length === 0 ? (
            <Card title="No tasks"><p className="text-gray-400 text-sm">Use Quick Action above to create your first task.</p></Card>
          ) : (
            filtered.map(t => (
              <TaskTicketCard key={t.id} ticket={t} jobs={jobs} tools={tools} onRefresh={refresh} />
            ))
          )}
        </div>
      )}

      {/* Table view */}
      {view === "table" && (
        <Card title={`Tasks Table (${filtered.length})`}>
          <div className="overflow-x-auto">
            <table className="w-full text-left text-sm">
              <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">Type</th>
                  <th className="py-2 px-3">Title</th>
                  <th className="py-2 px-3">Status</th>
                  <th className="py-2 px-3">Priority</th>
                  <th className="py-2 px-3">Tool</th>
                  <th className="py-2 px-3">Project</th>
                </tr>
              </thead>
              <tbody>
                {filtered.length === 0 ? (
                  <tr>
                    <td colSpan="7" className="py-4 px-3 text-center text-gray-400">No tasks found</td>
                  </tr>
                ) : (
                  filtered.map(t => (
                    <tr key={t.id} className="border-b border-gray-100 hover:bg-gray-50">
                      <td className="py-2 px-3 text-xs font-mono text-gray-400">{t.id?.slice(0, 10)}</td>
                      <td className="py-2 px-3">
                        <span className="text-sm">{TYPE_ICONS[t.type] || "📋"}</span>
                      </td>
                      <td className="py-2 px-3">
                        <div className="font-medium text-gray-900">{t.task || t.title}</div>
                        {t.description && (
                          <div className="text-xs text-gray-500 truncate max-w-[300px]">{t.description}</div>
                        )}
                      </td>
                      <td className="py-2 px-3">
                        <span className={`text-[11px] px-1.5 py-0.5 rounded-full ${STATUS_COLORS[t.status] || "bg-gray-100"}`}>
                          {t.status?.replace("_", " ")}
                        </span>
                      </td>
                      <td className="py-2 px-3">
                        <span className={`text-[11px] px-1.5 py-0.5 rounded-full ${PRIORITY_COLORS[t.priority] || "bg-gray-100"}`}>
                          {t.priority}
                        </span>
                      </td>
                      <td className="py-2 px-3 text-xs">
                        {t.assigned_tool ? (
                          <span className="px-1.5 py-0.5 bg-indigo-50 text-indigo-700 rounded">{t.assigned_tool.tool}</span>
                        ) : (
                          <span className="text-gray-400">—</span>
                        )}
                      </td>
                      <td className="py-2 px-3 text-xs text-gray-500">{t.project_id || "—"}</td>
                    </tr>
                  ))
                )}
              </tbody>
            </table>
          </div>
        </Card>
      )}

      {/* Recent jobs table (collapsed) */}
      {jobs.length > 0 && (
        <Card title={`Recent Jobs (${jobs.length})`}>
          <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 => ({
                id: job.id,
                ticket: job.ticket_id,
                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 || '',
                tool: job.tool,
                mode: job.mode || '',
                status: job.status,
                duration: job.duration_s > 0 ? `${job.duration_s}s` : '—',
                exit_code: job.result?.exit_code ?? '',
                stdout_lines: job.result?.stdout_lines ?? 0,
                stderr_lines: job.result?.stderr_lines ?? 0,
                stdout: job.result?.stdout || '',
                stderr: job.result?.stderr || '',
                files_changed: Array.isArray(job.result?.files_changed) ? job.result.files_changed.join(', ') : '',
                error: job.error || ''
              }))}
              filename="recent_jobs"
              title="Recent Jobs"
            />
          </div>
          <div className="overflow-x-auto">
            <table className="w-full text-left text-sm">
              <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">Status</th>
                  <th className="py-2 px-3">Duration</th>
                  <th className="py-2 px-3">Error</th>
                </tr>
              </thead>
              <tbody>
                {jobs.map(j => (
                  <tr key={j.id} className="border-b border-gray-100 hover:bg-gray-50">
                    <td className="py-1.5 px-3 text-xs font-mono text-gray-400">{j.id?.slice(0, 10)}</td>
                    <td className="py-1.5 px-3 text-xs font-mono text-gray-500">{j.ticket_id?.slice(0, 12)}</td>
                    <td className="py-1.5 px-3 text-xs font-medium">{j.tool}</td>
                    <td className="py-1.5 px-3">
                      <span className={`text-[11px] px-1.5 py-0.5 rounded-full ${JOB_STATUS_COLORS[j.status] || "bg-gray-100"}`}>{j.status}</span>
                    </td>
                    <td className="py-1.5 px-3 text-xs text-gray-400">{j.duration_s > 0 ? `${j.duration_s}s` : "—"}</td>
                    <td className="py-1.5 px-3 text-xs text-red-400 truncate max-w-[200px]">{j.error || ""}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </Card>
      )}
    </div>
  );
}
