// components/tickets.jsx — TicketsPanel with sprint management + multi-tool assignment.
// Depends on: React (useState, useCallback, useEffect), Card, StatusBadge, get, post from api.js

const TICKET_STATUSES = ["backlog", "todo", "in_progress", "in_review", "done", "cancelled"];
const TICKET_PRIORITIES = ["critical", "high", "medium", "low"];
const TICKET_TYPES = ["task", "bug", "feature", "refactor", "test", "docs"];
const TOOLS = ["vscode", "aider", "claude-code", "windsurf", "cursor", "roo-code", "continue", "copilot", "human"];

const STATUS_COLORS = {
  backlog: "bg-gray-100 text-gray-600",
  todo: "bg-blue-100 text-blue-700",
  in_progress: "bg-amber-100 text-amber-700",
  in_review: "bg-purple-100 text-purple-700",
  done: "bg-emerald-100 text-emerald-700",
  cancelled: "bg-red-100 text-red-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",
};

const PRIORITY_COLORS = {
  critical: "bg-red-200 text-red-800",
  high: "bg-orange-100 text-orange-700",
  medium: "bg-yellow-100 text-yellow-700",
  low: "bg-gray-100 text-gray-600",
};

const TYPE_ICONS = {
  task: "📋", bug: "🐛", feature: "✨", refactor: "🔧", test: "🧪", docs: "📄",
};

// ── Hook: useTickets ─────────────────────────────────────────
function useTickets() {
  const [tickets, setTickets] = useState([]);
  const [sprints, setSprints] = useState([]);
  const [loading, setLoading] = useState(true);

  const refreshTickets = useCallback(async () => {
    const data = await get("/dashboard/tickets");
    setTickets(data?.tickets || []);
  }, []);

  const refreshSprints = useCallback(async () => {
    const data = await get("/dashboard/sprints");
    setSprints(data?.sprints || []);
  }, []);

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

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

  return { tickets, sprints, loading, refresh, refreshTickets, refreshSprints };
}

// ── TicketCard ───────────────────────────────────────────────
function TicketCard({ ticket, onUpdate, sprints, isSelected, onSelect }) {
  const [expanded, setExpanded] = useState(false);
  const [assigning, setAssigning] = useState(false);

  const handleCardClick = () => {
    onSelect(ticket.id);
  };

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

  const handleAssign = async (tool) => {
    await post(`/dashboard/tickets/${ticket.id}/assign`, { tool });
    setAssigning(false);
    onUpdate();
  };

  return (
    <div 
      className={`bg-white rounded-lg border p-4 hover:shadow-md transition-shadow cursor-pointer text-left w-full ${
        isSelected ? 'border-blue-400 bg-blue-50' : 'border-gray-200'
      }`}
      onClick={handleCardClick}
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          handleCardClick();
        }
      }}
      role="button"
      tabIndex={0}
      aria-pressed={isSelected}
      aria-label={`Ticket ${ticket.id}: ${ticket.title}${isSelected ? ' (selected)' : ''}`}
    >
      <div className="flex items-start justify-between">
        <div className="flex-1 min-w-0">
          <div className="flex items-center gap-2 mb-1">
            <span className="text-sm">{TYPE_ICONS[ticket.type] || "📋"}</span>
            <span className="text-xs font-mono text-gray-400">{ticket.id}</span>
            <span className={`text-xs px-2 py-0.5 rounded-full ${PRIORITY_COLORS[ticket.priority]}`}>
              {ticket.priority}
            </span>
          </div>
          <h4 className="font-medium text-gray-900 truncate">{ticket.title}</h4>
          {ticket.description && expanded && (
            <p className="text-sm text-gray-500 mt-1">{ticket.description}</p>
          )}
        </div>
        <button onClick={(e) => { e.stopPropagation(); setExpanded(!expanded); }} className="text-gray-400 hover:text-gray-600 ml-2">
          {expanded ? "▲" : "▼"}
        </button>
      </div>

      <div className="flex items-center gap-2 mt-3 flex-wrap">
        <select
          value={ticket.status}
          onChange={(e) => { e.stopPropagation(); handleStatusChange(e.target.value); }}
          className={`text-xs px-2 py-1 rounded border-0 cursor-pointer ${STATUS_COLORS[ticket.status]}`}
        >
          {TICKET_STATUSES.map(s => <option key={s} value={s}>{s.replace("_", " ")}</option>)}
        </select>

        {ticket.assigned_tool ? (
          <span className="text-xs px-2 py-1 bg-indigo-100 text-indigo-700 rounded-full">
            🔧 {ticket.assigned_tool.tool}
            {ticket.assigned_tool.agent_mode && ` (${ticket.assigned_tool.agent_mode})`}
          </span>
        ) : (
          <button
            onClick={(e) => { e.stopPropagation(); setAssigning(!assigning); }}
            className="text-xs px-2 py-1 bg-gray-100 text-gray-600 rounded hover:bg-gray-200"
          >
            + Assign tool
          </button>
        )}

        {ticket.tags.map(tag => (
          <span key={tag} className="text-xs px-2 py-0.5 bg-sky-50 text-sky-600 rounded">{tag}</span>
        ))}

        {ticket.estimated_hours > 0 && (
          <span className="text-xs text-gray-400">⏱ {ticket.estimated_hours}h</span>
        )}
      </div>

      {assigning && (
        <div 
          className="mt-3 flex flex-wrap gap-1" 
          role="toolbar"
          aria-label="Tool assignment options"
          onKeyDown={(e) => {
            if (e.key === 'Escape') {
              e.preventDefault();
              setAssigning(false);
            }
          }}
        >
          {TOOLS.map(tool => (
            <button
              key={tool}
              onClick={() => handleAssign(tool)}
              className="text-xs px-2 py-1 bg-indigo-50 text-indigo-700 rounded hover:bg-indigo-100 focus:outline-none focus:ring-2 focus:ring-indigo-500"
              aria-label={`Assign ticket to ${tool}`}
            >
              {tool}
            </button>
          ))}
        </div>
      )}

      {expanded && ticket.files && ticket.files.length > 0 && (
        <div className="mt-2">
          <span className="text-xs font-medium text-gray-500">Files:</span>
          <div className="flex flex-wrap gap-1 mt-1">
            {ticket.files.map(f => (
              <span key={f} className="text-xs font-mono bg-gray-50 text-gray-500 px-1 rounded">{f}</span>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

// ── CreateTicketForm ─────────────────────────────────────────
function CreateTicketForm({ sprints, onCreated, onCancel }) {
  const [form, setForm] = useState({ task: "" });

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!form.task.trim()) return;
    await post("/dashboard/tickets", { task: form.task });
    onCreated();
  };

  const field = (name, value) => setForm(prev => ({ ...prev, [name]: value }));

  return (
    <form onSubmit={handleSubmit} className="bg-white rounded-lg border border-blue-200 p-4 space-y-3">
      <h4 className="font-medium text-blue-900">New Ticket</h4>
      <textarea
        className="w-full px-3 py-2 border rounded text-sm" placeholder="Content *" rows={3}
        value={form.task} onChange={e => field("task", e.target.value)} required
      />
      <div className="flex gap-2">
        <button type="submit" className="px-4 py-2 bg-blue-600 text-white text-sm rounded hover:bg-blue-700">
          Create Ticket
        </button>
        <button type="button" onClick={onCancel} className="px-4 py-2 bg-gray-100 text-gray-700 text-sm rounded hover:bg-gray-200">
          Cancel
        </button>
      </div>
    </form>
  );
}

// ── CreateSprintForm ─────────────────────────────────────────
function CreateSprintForm({ onCreated, onCancel }) {
  const [form, setForm] = useState({ name: "", goal: "", start_date: "", end_date: "" });

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!form.name.trim()) return;
    await post("/dashboard/sprints", form);
    onCreated();
  };

  const field = (name, value) => setForm(prev => ({ ...prev, [name]: value }));

  return (
    <form onSubmit={handleSubmit} className="bg-white rounded-lg border border-green-200 p-4 space-y-3">
      <h4 className="font-medium text-green-900">New Sprint</h4>
      <input className="w-full px-3 py-2 border rounded text-sm" placeholder="Sprint name *"
        value={form.name} onChange={e => field("name", e.target.value)} required />
      <input className="w-full px-3 py-2 border rounded text-sm" placeholder="Sprint goal"
        value={form.goal} onChange={e => field("goal", e.target.value)} />
      <div className="grid grid-cols-2 gap-2">
        <input className="px-2 py-1 border rounded text-sm" type="date"
          value={form.start_date} onChange={e => field("start_date", e.target.value)} />
        <input className="px-2 py-1 border rounded text-sm" type="date"
          value={form.end_date} onChange={e => field("end_date", e.target.value)} />
      </div>
      <div className="flex gap-2">
        <button type="submit" className="px-4 py-2 bg-green-600 text-white text-sm rounded hover:bg-green-700">
          Create Sprint
        </button>
        <button type="button" onClick={onCancel} className="px-4 py-2 bg-gray-100 text-gray-700 text-sm rounded hover:bg-gray-200">
          Cancel
        </button>
      </div>
    </form>
  );
}

// ── SprintCard ───────────────────────────────────────────────
function SprintCard({ sprint, onSelect, selected }) {
  let progressColor;
  if (sprint.progress_pct >= 100) {
    progressColor = "bg-emerald-500";
  } else if (sprint.progress_pct >= 50) {
    progressColor = "bg-blue-500";
  } else {
    progressColor = "bg-amber-500";
  }

  return (
    <button
      type="button"
      onClick={() => onSelect(sprint.id === selected ? null : sprint.id)}
      className={`p-3 rounded-lg border cursor-pointer transition-all text-left w-full ${
        sprint.id === selected ? "border-blue-400 bg-blue-50" : "border-gray-200 bg-white hover:border-gray-300"
      }`}
      aria-pressed={sprint.id === selected}
      aria-label={`Sprint ${sprint.name}${sprint.id === selected ? ' (selected)' : ''}`}
    >
      <div className="flex items-center justify-between">
        <h4 className="font-medium text-sm">{sprint.name}</h4>
        <span className={`text-xs px-2 py-0.5 rounded ${STATUS_COLORS[sprint.status] || "bg-gray-100 text-gray-600"}`}>
          {sprint.status}
        </span>
      </div>
      {sprint.goal && <p className="text-xs text-gray-500 mt-1">{sprint.goal}</p>}
      <div className="mt-2">
        <div className="flex justify-between text-xs text-gray-500 mb-1">
          <span>{sprint.tickets_done}/{sprint.tickets_total} done</span>
          <span>{sprint.progress_pct}%</span>
        </div>
        <div className="w-full bg-gray-200 rounded-full h-1.5">
          <div className={`${progressColor} h-1.5 rounded-full transition-all`}
            style={{ width: `${Math.min(sprint.progress_pct, 100)}%` }} />
        </div>
      </div>
      {sprint.start_date && (
        <div className="text-xs text-gray-400 mt-1">
          {sprint.start_date} → {sprint.end_date || "..."}
        </div>
      )}
    </button>
  );
}

function formatTimestamp(ts) {
  if (!ts) return "—";
  const date = new Date(ts * 1000);
  if (Number.isNaN(date.getTime())) return "—";
  return date.toLocaleString();
}

function TicketHistoryItem({ job, index }) {
  const result = job.result || null;
  const stdout = result?.stdout || "";
  const stderr = result?.stderr || "";
  const filesChanged = result?.files_changed || [];
  const statusClass = JOB_STATUS_COLORS[job.status] || JOB_STATUS_COLORS.queued;
  const success = result?.success ?? job.success;
  const execution = result?.execution || result?.execution_metadata || job.execution_metadata || {};
  const projectDir = job.project_dir || "—";
  const promptExcerpt = job.prompt_excerpt || "";
  const promptLength = job.prompt_length || 0;
  const isActiveJob = job.status === "queued" || job.status === "running";
  const resultLabel = success === true
    ? "success"
    : success === false
      ? (isActiveJob ? "pending" : "failed")
      : (isActiveJob ? "pending" : "unknown");

  return (
    <div className="rounded-lg border border-gray-200 bg-white p-3 space-y-2">
      <div className="flex items-start justify-between gap-3">
        <div className="min-w-0">
          <div className="flex items-center gap-2 flex-wrap">
            <span className="text-xs font-mono text-gray-400">#{index + 1}</span>
            <span className="text-xs font-mono text-gray-500">{job.id}</span>
            <span className={`text-xs px-2 py-0.5 rounded-full ${statusClass}`}>{job.status}</span>
          </div>
          <div className="text-sm font-medium text-gray-800 mt-1">{job.tool} {job.mode ? `· ${job.mode}` : ""}</div>
          <div className="text-xs text-gray-500 mt-1">
            Started: {formatTimestamp(job.started_at)} · Finished: {formatTimestamp(job.finished_at)} · Duration: {job.duration_s > 0 ? `${job.duration_s}s` : "—"}
          </div>
          <div className="text-xs text-gray-500 mt-1">
            Project: <span className="font-mono">{projectDir}</span> · Prompt: {promptLength} chars
            {job.model ? ` · Model: ${job.model}` : ""}
          </div>
        </div>
        <div className="text-right text-xs text-gray-500 shrink-0">
          <div className={resultLabel === "success" ? "text-emerald-600" : resultLabel === "failed" ? "text-red-600" : "text-gray-500"}>
            Tool result: {resultLabel}
          </div>
          {result?.exit_code !== undefined && (
            <div>Exit code: <span className="font-mono">{result.exit_code}</span></div>
          )}
          <div>stdout {result?.stdout_lines ?? 0} / stderr {result?.stderr_lines ?? 0}</div>
          {job.error && <div className="mt-1 text-red-600 max-w-[240px] truncate" title={job.error}>{job.error}</div>}
        </div>
      </div>

      {filesChanged.length > 0 && (
        <div className="text-xs text-gray-600">
          <span className="font-medium text-gray-500">Files:</span>{" "}
          {filesChanged.join(", ")}
        </div>
      )}

      {promptExcerpt && (
        <div className="text-xs text-gray-600">
          <span className="font-medium text-gray-500">Prompt:</span>{" "}
          <span className="whitespace-pre-wrap font-mono text-[11px] text-gray-700">{promptExcerpt}</span>
        </div>
      )}

      {Object.keys(execution).length > 0 && (
        <div className="text-xs text-gray-600">
          <span className="font-medium text-gray-500">Adapter:</span>{" "}
          <span className="font-mono">{execution.adapter || "—"}</span>
          {execution.binary && <span className="text-gray-500"> · {execution.binary}</span>}
        </div>
      )}

      <div className="grid grid-cols-1 md:grid-cols-2 gap-2">
        <div>
          <div className="text-xs font-medium text-gray-500 mb-1">stdout</div>
          <pre className="max-h-56 overflow-auto rounded bg-gray-900 p-3 text-[11px] leading-tight text-green-300 whitespace-pre-wrap">
            {stdout || "No stdout captured."}
          </pre>
        </div>
        <div>
          <div className="text-xs font-medium text-gray-500 mb-1">stderr</div>
          <pre className="max-h-56 overflow-auto rounded bg-gray-900 p-3 text-[11px] leading-tight text-red-300 whitespace-pre-wrap">
            {stderr || "No stderr captured."}
          </pre>
        </div>
      </div>
    </div>
  );
}

function TicketDetailPanel({ ticket, loading, error, onRefresh }) {
  if (!ticket) return null;

  const jobHistory = Array.isArray(ticket.job_history) ? [...ticket.job_history].sort((a, b) => (a.created_at || 0) - (b.created_at || 0)) : [];
  const latestJob = jobHistory.at(-1) || ticket.last_job;
  const latestExecutionMetadata = latestJob?.execution_metadata || latestJob?.result?.execution || latestJob?.result?.execution_metadata || {};
  const latestProjectDir = latestJob?.project_dir || "—";
  const latestPromptExcerpt = latestJob?.prompt_excerpt || "";
  const latestPromptLength = latestJob?.prompt_length || 0;
  const latestSuccess = latestJob?.success ?? latestJob?.result?.success;
  const latestExitCode = latestJob?.exit_code ?? latestJob?.result?.exit_code ?? null;
  const latestStdoutLines = latestJob?.stdout_lines ?? latestJob?.result?.stdout_lines ?? 0;
  const latestStderrLines = latestJob?.stderr_lines ?? latestJob?.result?.stderr_lines ?? 0;
  const latestFilesChanged = latestJob?.files_changed || latestJob?.result?.files_changed || [];
  const latestIsActiveJob = latestJob?.status === "queued" || latestJob?.status === "running";
  const latestResultLabel = latestJob
    ? (
      latestSuccess === true
        ? "success"
        : latestSuccess === false
          ? (latestIsActiveJob ? "pending" : "failed")
          : (latestIsActiveJob ? "pending" : "unknown")
    )
    : "—";

  return (
    <Card title={`Ticket details: ${ticket.title || ticket.id}`}>
      <div className="space-y-4">
        <div className="grid grid-cols-1 md: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">{ticket.title || ticket.task || ticket.id}</div>
            <div className="text-gray-500 mt-1 font-mono">{ticket.id}</div>
            <div className="text-gray-400 mt-1">{ticket.status}</div>
          </div>
          <div className="rounded-lg bg-white border border-gray-200 p-3">
            <div className="text-gray-400 uppercase">Assignment</div>
            <div className="font-medium text-gray-800 mt-1">{ticket.assigned_tool?.tool || "—"}</div>
            <div className="text-gray-400 mt-1">{ticket.assigned_tool?.agent_mode || ""}</div>
          </div>
          <div className="rounded-lg bg-white border border-gray-200 p-3">
            <div className="text-gray-400 uppercase">Latest job</div>
            <div className="font-medium text-gray-800 mt-1">{latestJob?.status || "—"}</div>
            <div className="text-gray-400 mt-1">{latestJob?.tool || "—"}</div>
          </div>
          <div className="rounded-lg bg-white border border-gray-200 p-3">
            <div className="text-gray-400 uppercase">Progress</div>
            <div className="font-medium text-gray-800 mt-1">{ticket.actual_hours > 0 ? `${ticket.actual_hours}h spent` : "—"}</div>
            <div className="text-gray-400 mt-1">{ticket.completed_at ? `Done: ${formatTimestamp(ticket.completed_at)}` : `Updated: ${formatTimestamp(ticket.updated_at)}`}</div>
          </div>
        </div>

        {ticket.description && (
          <div>
            <div className="text-xs font-medium text-gray-500 mb-1">Description</div>
            <p className="text-sm text-gray-700 whitespace-pre-wrap">{ticket.description}</p>
          </div>
        )}

        <div className="grid grid-cols-1 md:grid-cols-2 gap-3 text-sm">
          <div className="rounded-lg border border-gray-200 bg-white p-3">
            <div className="text-xs font-medium text-gray-500 mb-2">Project metadata</div>
            <div className="space-y-1 text-xs text-gray-600">
              <div><span className="font-medium text-gray-500">Project:</span> {ticket.project_id || "—"}</div>
              <div><span className="font-medium text-gray-500">Sprint:</span> {ticket.sprint_id || "—"}</div>
              <div><span className="font-medium text-gray-500">Priority:</span> {ticket.priority}</div>
              <div><span className="font-medium text-gray-500">Type:</span> {ticket.type}</div>
              <div><span className="font-medium text-gray-500">Created:</span> {formatTimestamp(ticket.created_at)}</div>
              <div><span className="font-medium text-gray-500">Updated:</span> {formatTimestamp(ticket.updated_at)}</div>
            </div>
          </div>

          <div className="rounded-lg border border-gray-200 bg-white p-3">
            <div className="text-xs font-medium text-gray-500 mb-2">Artifacts</div>
            <div className="space-y-2 text-xs text-gray-600">
              <div><span className="font-medium text-gray-500">Tags:</span> {ticket.tags?.length ? ticket.tags.join(", ") : "—"}</div>
              <div><span className="font-medium text-gray-500">Files:</span> {ticket.files?.length ? ticket.files.join(", ") : "—"}</div>
              <div><span className="font-medium text-gray-500">Estimated hours:</span> {ticket.estimated_hours || "—"}</div>
              <div><span className="font-medium text-gray-500">Actual hours:</span> {ticket.actual_hours || "—"}</div>
            </div>
          </div>
        </div>

        <div className="rounded-lg border border-gray-200 bg-white p-3">
          <div className="text-xs font-medium text-gray-500 mb-2">Latest execution evidence</div>
          <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-3 text-xs text-gray-600">
            <div>
              <div className="font-medium text-gray-500 uppercase">Context</div>
              <div className="mt-1 font-mono truncate" title={latestProjectDir}>{latestProjectDir}</div>
              <div className="mt-1">{latestJob?.mode || "—"}{latestJob?.model ? ` · ${latestJob.model}` : ""}</div>
              <div className="mt-1">Prompt: {latestPromptLength} chars</div>
            </div>
            <div>
              <div className="font-medium text-gray-500 uppercase">Tool execution result</div>
              <div className={`mt-1 font-medium ${latestResultLabel === "success" ? "text-emerald-600" : latestResultLabel === "failed" ? "text-red-600" : "text-gray-800"}`}>
                {latestResultLabel}
              </div>
              <div className="mt-1">Exit code: {latestExitCode ?? "—"}</div>
              <div className="mt-1">stdout {latestStdoutLines} / stderr {latestStderrLines}</div>
            </div>
            <div>
              <div className="font-medium text-gray-500 uppercase">Files</div>
              <div className="mt-1">{latestFilesChanged.length || 0} changed</div>
              {latestFilesChanged.length > 0 && (
                <div className="mt-1 max-h-20 overflow-auto font-mono text-[11px] whitespace-pre-wrap">
                  {latestFilesChanged.join(", ")}
                </div>
              )}
            </div>
            <div>
              <div className="font-medium text-gray-500 uppercase">Prompt excerpt</div>
              <div className="mt-1 max-h-20 overflow-auto whitespace-pre-wrap font-mono text-[11px] leading-snug text-gray-700">
                {latestPromptExcerpt || "—"}
              </div>
            </div>
          </div>
          {Object.keys(latestExecutionMetadata).length > 0 && (
            <div className="mt-3">
              <div className="text-xs font-medium text-gray-500 mb-1">Execution metadata</div>
              <pre className="max-h-40 overflow-auto rounded bg-gray-50 p-2 text-[11px] leading-snug text-gray-700 whitespace-pre-wrap">
                {JSON.stringify(latestExecutionMetadata, null, 2)}
              </pre>
            </div>
          )}
        </div>

        {ticket.comments?.length > 0 && (
          <div>
            <div className="text-xs font-medium text-gray-500 mb-2">Comments</div>
            <div className="space-y-2">
              {ticket.comments.map(comment => (
                <div key={comment.id} className="rounded-lg border border-gray-200 bg-white p-3">
                  <div className="flex items-center justify-between gap-2 text-xs text-gray-400">
                    <span className="font-medium text-gray-700">{comment.author || "user"}</span>
                    <span>{formatTimestamp(comment.created_at)}</span>
                  </div>
                  <p className="mt-2 text-sm text-gray-700 whitespace-pre-wrap">{comment.content}</p>
                </div>
              ))}
            </div>
          </div>
        )}

        <div>
          <div className="flex items-center justify-between gap-2 mb-2">
            <div className="text-xs font-medium text-gray-500">Realization timeline</div>
            {onRefresh && (
              <button onClick={onRefresh} className="text-xs px-2 py-1 rounded bg-gray-100 text-gray-600 hover:bg-gray-200">
                Refresh details
              </button>
            )}
          </div>
          {loading && <p className="text-xs text-gray-500">Loading ticket history...</p>}
          {error && <p className="text-xs text-red-600">{error}</p>}
          {!loading && !error && jobHistory.length === 0 && (
            <p className="text-xs text-gray-500">No job history yet.</p>
          )}
          <div className="space-y-3">
            {jobHistory.map((job, index) => (
              <TicketHistoryItem key={job.id} job={job} index={index} />
            ))}
          </div>
        </div>
      </div>
    </Card>
  );
}

// ── TicketsPanel (main) ──────────────────────────────────────
function TicketsPanel({ selectedTicket, setSelectedTicket }) {
  const { tickets, sprints, loading, refresh } = useTickets();
  const [showCreateTicket, setShowCreateTicket] = useState(false);
  const [showCreateSprint, setShowCreateSprint] = useState(false);
  const [selectedSprint, setSelectedSprint] = useState(null);
  const [filterStatus, setFilterStatus] = useState("");
  const [filterTool, setFilterTool] = useState("");
  const [view, setView] = useState("list"); // list | board
  const [selectedTicketDetails, setSelectedTicketDetails] = useState(null);
  const [selectedTicketLoading, setSelectedTicketLoading] = useState(false);
  const [selectedTicketError, setSelectedTicketError] = useState("");

  const loadSelectedTicket = useCallback(async (ticketId) => {
    if (!ticketId) {
      setSelectedTicketDetails(null);
      setSelectedTicketError("");
      setSelectedTicketLoading(false);
      return;
    }
    setSelectedTicketLoading(true);
    setSelectedTicketError("");
    try {
      const data = await get(`/dashboard/tickets/${ticketId}`);
      setSelectedTicketDetails(data);
    } catch (err) {
      setSelectedTicketDetails(null);
      setSelectedTicketError(err?.message || "Failed to load ticket details");
    } finally {
      setSelectedTicketLoading(false);
    }
  }, []);

  useEffect(() => {
    loadSelectedTicket(selectedTicket);
  }, [selectedTicket, loadSelectedTicket]);

  const handleRefresh = useCallback(async () => {
    await refresh();
    if (selectedTicket) {
      await loadSelectedTicket(selectedTicket);
    }
  }, [refresh, selectedTicket, loadSelectedTicket]);

  const filtered = React.useMemo(() => {
    let result = tickets;
    if (selectedSprint) result = result.filter(t => t.sprint_id === selectedSprint);
    if (filterStatus) result = result.filter(t => t.status === filterStatus);
    if (filterTool) result = result.filter(t => t.assigned_tool?.tool === filterTool);
    return result;
  }, [tickets, selectedSprint, filterStatus, filterTool]);

  const boardColumns = React.useMemo(() => {
    const cols = {};
    TICKET_STATUSES.forEach(s => { cols[s] = []; });
    filtered.forEach(t => { if (cols[t.status]) cols[t.status].push(t); });
    return cols;
  }, [filtered]);

  const stats = React.useMemo(() => ({
    total: tickets.length,
    todo: tickets.filter(t => t.status === "todo").length,
    in_progress: tickets.filter(t => t.status === "in_progress").length,
    done: tickets.filter(t => t.status === "done").length,
    by_tool: TOOLS.reduce((acc, tool) => {
      const count = tickets.filter(t => t.assigned_tool?.tool === tool).length;
      if (count > 0) acc[tool] = count;
      return acc;
    }, {}),
  }), [tickets]);

  if (loading) {
    return <Card title="Tickets & Sprints"><p className="text-gray-500">Loading...</p></Card>;
  }

  const selectedTicketSummary = selectedTicketDetails || tickets.find(t => t.id === selectedTicket) || null;

  return (
    <div className="space-y-4">
      {/* Stats bar */}
      <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
        <div className="bg-white rounded-lg border p-3 text-center">
          <div className="text-2xl font-bold text-gray-900">{stats.total}</div>
          <div className="text-xs text-gray-500">Total Tickets</div>
        </div>
        <div className="bg-white rounded-lg border p-3 text-center">
          <div className="text-2xl font-bold text-blue-600">{stats.todo}</div>
          <div className="text-xs text-gray-500">To Do</div>
        </div>
        <div className="bg-white rounded-lg border p-3 text-center">
          <div className="text-2xl font-bold text-amber-600">{stats.in_progress}</div>
          <div className="text-xs text-gray-500">In Progress</div>
        </div>
        <div className="bg-white rounded-lg border p-3 text-center">
          <div className="text-2xl font-bold text-emerald-600">{stats.done}</div>
          <div className="text-xs text-gray-500">Done</div>
        </div>
      </div>

      {/* Tool distribution */}
      {Object.keys(stats.by_tool).length > 0 && (
        <div className="bg-white rounded-lg border p-3">
          <h4 className="text-xs font-medium text-gray-500 mb-2">Assigned by Tool</h4>
          <div className="flex flex-wrap gap-2">
            {Object.entries(stats.by_tool).map(([tool, count]) => (
              <span key={tool} className="text-xs px-2 py-1 bg-indigo-50 text-indigo-700 rounded-full">
                {tool}: {count}
              </span>
            ))}
          </div>
        </div>
      )}

      {/* Sprints */}
      <Card title={`Sprints (${sprints.length})`}>
        <div className="grid grid-cols-1 md:grid-cols-3 gap-3 mb-3">
          {sprints.map(s => (
            <SprintCard key={s.id} sprint={s} selected={selectedSprint} onSelect={setSelectedSprint} />
          ))}
        </div>
        <button onClick={() => setShowCreateSprint(!showCreateSprint)}
          className="text-sm text-green-600 hover:text-green-700">
          + New Sprint
        </button>
        {showCreateSprint && (
          <div className="mt-3">
            <CreateSprintForm
              onCreated={() => { setShowCreateSprint(false); refresh(); }}
              onCancel={() => setShowCreateSprint(false)}
            />
          </div>
        )}
      </Card>

      {/* Toolbar */}
      <div className="flex items-center justify-between flex-wrap gap-2">
        <div className="flex items-center gap-2">
          <select className="text-sm px-2 py-1 border rounded" value={filterStatus}
            onChange={e => setFilterStatus(e.target.value)}>
            <option value="">All statuses</option>
            {TICKET_STATUSES.map(s => <option key={s} value={s}>{s.replace("_", " ")}</option>)}
          </select>
          <select className="text-sm px-2 py-1 border rounded" value={filterTool}
            onChange={e => setFilterTool(e.target.value)}>
            <option value="">All tools</option>
            {TOOLS.map(t => <option key={t} value={t}>{t}</option>)}
          </select>
          <div className="flex border rounded overflow-hidden">
            <button onClick={() => setView("list")}
              className={`px-2 py-1 text-xs ${view === "list" ? "bg-blue-600 text-white" : "bg-white text-gray-600"}`}>
              List
            </button>
            <button onClick={() => setView("board")}
              className={`px-2 py-1 text-xs ${view === "board" ? "bg-blue-600 text-white" : "bg-white text-gray-600"}`}>
              Board
            </button>
          </div>
        </div>
        <div className="flex gap-2">
          <button onClick={() => setShowCreateTicket(!showCreateTicket)}
            className="px-3 py-1.5 bg-blue-600 text-white text-sm rounded hover:bg-blue-700">
            + New Ticket
          </button>
          <button onClick={handleRefresh}
            className="px-3 py-1.5 bg-gray-100 text-gray-700 text-sm rounded hover:bg-gray-200">
            ↻ Refresh
          </button>
        </div>
      </div>

      {selectedTicketSummary && (
        <TicketDetailPanel
          ticket={selectedTicketSummary}
          loading={selectedTicketLoading}
          error={selectedTicketError}
          onRefresh={handleRefresh}
        />
      )}

      {/* Create form */}
      {showCreateTicket && (
        <CreateTicketForm
          sprints={sprints}
          onCreated={() => { setShowCreateTicket(false); handleRefresh(); }}
          onCancel={() => setShowCreateTicket(false)}
        />
      )}

      {/* Board view */}
      {view === "board" && (
        <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-2">
          {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]}`}>
                {status.replace("_", " ")} ({boardColumns[status]?.length || 0})
              </h5>
              <div className="space-y-2">
                {(boardColumns[status] || []).map((t) => (
                  <TicketCard 
                    key={t.id} 
                    ticket={t} 
                    onUpdate={handleRefresh} 
                    sprints={sprints}
                    isSelected={selectedTicket === t.id}
                    onSelect={setSelectedTicket}
                  />
                ))}
              </div>
            </div>
          ))}
        </div>
      )}

      {/* List view */}
      {view === "list" && (
        <div className="space-y-2">
          {filtered.length === 0 ? (
            <Card title="No tickets">
              <p className="text-gray-500 text-sm">Create your first ticket to start tracking tasks across tools.</p>
            </Card>
          ) : (
            filtered.map(t => (
              <TicketCard 
                key={t.id} 
                ticket={t} 
                onUpdate={handleRefresh} 
                sprints={sprints}
                isSelected={selectedTicket === t.id}
                onSelect={setSelectedTicket}
              />
            ))
          )}
        </div>
      )}
    </div>
  );
}
