// components/projects.jsx — ProjectsPanel: lightweight name-based projects with assignment overview.
// Depends on: React (useState, useCallback, useEffect, useMemo), Card, get, post, put, del from api.js

// ── Hook: useProjects (with SSE auto-refresh) ───────────────
function useProjects() {
  const [projects, setProjects] = useState([]);
  const [loading, setLoading] = useState(true);

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

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

  // SSE: subscribe to domain events for real-time project updates
  useEffect(() => {
    // Use shared SSE service to avoid connection limits
    if (!globalThis.SSEService) {
      console.warn("SSEService not available");
      return;
    }
    
    const projectEvents = new Set(["ProjectCreated", "ProjectUpdated", "ProjectRemoved", "ProjectScanned"]);
    const unsubscribe = globalThis.SSEService.subscribe(
      "ProjectsPanel",
      projectEvents,
      () => {
        refresh();
      }
    );
    
    return unsubscribe;
  }, [refresh]);

  return { projects, loading, refresh };
}

const PROJECTS_THEME_SURFACE_STYLE = {
  backgroundColor: "var(--dashboard-surface)",
  borderColor: "var(--dashboard-border)",
  color: "var(--dashboard-text)",
};

const PROJECTS_THEME_CONTROL_STYLE = {
  backgroundColor: "var(--dashboard-surface)",
  borderColor: "var(--dashboard-border)",
  color: "var(--dashboard-text)",
};

// ── Utility functions ───────────────────────────────────────────

function _normalizeApiResult(result, errorMessage) {
  if (result && typeof result === "object" && result.ok === false) {
    const body = result.body;
    const detail = typeof body === "string"
      ? body
      : body?.detail || body?.message || body?.error || errorMessage;
    throw new Error(detail || errorMessage || "Request failed");
  }
  if (result === null || result === undefined) {
    throw new Error(errorMessage || "API returned null/undefined");
  }
  return result;
}

function _projectKey(project) {
  if (!project) return "";
  return project.project_key || (project.source_path ? project.id : project.name) || project.id || project.name || "";
}

function _labelFrom(item, fallback = "") {
  if (!item) return fallback;
  return item.name || item.title || item.label || item.id || String(item);
}

function _metaFrom(item) {
  if (!item || typeof item !== "object") return "";
  return item.kind || item.role || item.default_model || item.default_instance || item.provider || item.engine || "";
}

function _environmentMeta(env) {
  if (!env || typeof env !== "object") return "";
  const kind = env.kind || "";
  const path = env.path || "";
  if (kind && path) return `${kind} · ${path}`;
  return kind || path || "";
}

function _toggleValue(list, value) {
  const current = Array.isArray(list) ? list : [];
  return current.includes(value) ? current.filter((item) => item !== value) : [...current, value];
}

function MultiSelectSection({
  title,
  subtitle,
  icon,
  items,
  selectedValues,
  onToggle,
  loading = false,
  emptyMessage = "No items available",
  itemKey = (item) => item?.id || item?.name || item,
  itemLabel = _labelFrom,
  itemMeta = _metaFrom,
}) {
  const selectedSet = new Set(selectedValues || []);
  const selectedItems = (Array.isArray(items) ? items : []).filter((item) => selectedSet.has(itemKey(item)));
  const hasItems = Array.isArray(items) && items.length > 0;
  let listContent = null;

  if (loading) {
    listContent = <p className="text-xs text-gray-500">Loading…</p>;
  } else if (hasItems) {
    listContent = items.map((item) => {
      const value = itemKey(item);
      const checked = selectedSet.has(value);
      return (
        <label key={value} className="flex cursor-pointer items-start gap-2 rounded-md px-2 py-1.5 hover:bg-blue-50">
          <input
            type="checkbox"
            checked={checked}
            onChange={() => onToggle(value)}
            className="mt-0.5 h-4 w-4 rounded border-blue-300 text-blue-600 focus:ring-blue-500"
          />
          <span className="min-w-0 flex-1 text-xs text-gray-700">
            <span className="block truncate font-medium">{itemLabel(item, value)}</span>
            {itemMeta(item) && <span className="block truncate text-[11px] text-gray-500">{itemMeta(item)}</span>}
          </span>
        </label>
      );
    });
  } else {
    listContent = <p className="text-xs text-gray-500">{emptyMessage}</p>;
  }

  return (
    <div className="rounded-lg border p-3 shadow-sm" style={PROJECTS_THEME_SURFACE_STYLE}>
      <div className="mb-2 flex items-start justify-between gap-2">
        <div>
          <h4 className="text-xs font-semibold text-gray-700">{icon} {title}</h4>
          {subtitle && <p className="text-[11px] text-gray-500">{subtitle}</p>}
        </div>
        <span className="text-[11px] text-gray-500">{selectedValues?.length || 0} selected</span>
      </div>

      <div className="max-h-44 space-y-1 overflow-y-auto pr-1">
        {listContent}
      </div>

      {selectedItems.length > 0 && (
        <div className="mt-2 flex flex-wrap gap-1">
          {selectedItems.map((item) => {
            const value = itemKey(item);
            return (
              <span key={value} className="inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[11px]" style={PROJECTS_THEME_CONTROL_STYLE}>
                <span className="max-w-[170px] truncate">{itemLabel(item, value)}</span>
                <button type="button" onClick={() => onToggle(value)} className="font-bold text-blue-500 hover:text-blue-700">×</button>
              </span>
            );
          })}
        </div>
      )}
    </div>
  );
}

// ── AssignmentBadge — small count badge with icon ─────────────

function AssignmentBadge({ icon, label, count, color }) {
  return (
    <div className={`flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg border ${color}`}>
      <span className="text-sm">{icon}</span>
      <div className="text-left">
        <div className="text-xs font-semibold leading-none">{count}</div>
        <div className="text-[10px] leading-none mt-0.5 opacity-70">{label}</div>
      </div>
    </div>
  );
}

// ── TicketTable — full table view with all columns ───────────

function TicketTable({ tickets, project }) {
  const [localTickets, setLocalTickets] = useState(tickets || []);
  const [savingCell, setSavingCell] = useState(null); // "ticketId:field"

  // Sync when parent re-fetches
  useEffect(() => { setLocalTickets(tickets || []); }, [tickets]);

  if (!localTickets || localTickets.length === 0) {
    return <p className="text-xs text-gray-500 italic">No tickets in this project.</p>;
  }

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

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

  const TYPE_OPTIONS = ["task", "bug", "feature", "refactor", "test", "docs"];
  const STATUS_OPTIONS = ["backlog", "todo", "in_progress", "in_review", "done", "cancelled"];
  const PRIORITY_OPTIONS = ["critical", "high", "medium", "low"];

  const handleFieldChange = async (ticketId, field, value) => {
    const cellKey = `${ticketId}:${field}`;
    setSavingCell(cellKey);
    try {
      await put(`/dashboard/tickets/${ticketId}`, { [field]: value });
      setLocalTickets((prev) =>
        prev.map((t) => (t.id === ticketId ? { ...t, [field]: value } : t))
      );
    } catch (err) {
      console.error(`Failed to update ticket ${ticketId} ${field}:`, err);
    } finally {
      setTimeout(() => setSavingCell(null), 400);
    }
  };

  const handleHoursChange = async (ticketId, value) => {
    const hours = parseFloat(value) || 0;
    handleFieldChange(ticketId, "estimated_hours", hours);
  };

  const selectClass = "appearance-none bg-transparent border-0 text-xs font-medium px-2 py-0.5 rounded cursor-pointer focus:ring-1 focus:ring-blue-300 focus:outline-none w-full";

  return (
    <div className="overflow-x-auto border border-blue-200 rounded-lg bg-blue-50 p-3 mt-3">
      <table className="w-full text-xs">
        <thead className="bg-blue-100 border-b border-blue-200">
          <tr>
            <th className="text-left px-3 py-2 font-semibold">ID</th>
            <th className="text-left px-3 py-2 font-semibold max-w-sm">Title</th>
            <th className="text-left px-3 py-2 font-semibold">Type</th>
            <th className="text-left px-3 py-2 font-semibold">Status</th>
            <th className="text-left px-3 py-2 font-semibold">Priority</th>
            <th className="text-left px-3 py-2 font-semibold">Tool</th>
            <th className="text-left px-3 py-2 font-semibold">Assignee</th>
            <th className="text-center px-3 py-2 font-semibold">Hours</th>
          </tr>
        </thead>
        <tbody>
          {localTickets.map((t, idx) => {
            const isSaving = (field) => savingCell === `${t.id}:${field}`;
            return (
              <tr key={t.id} className={idx % 2 === 0 ? "bg-white" : "bg-blue-50 bg-opacity-50"}>
                <td className="px-3 py-2 font-mono text-gray-600">{t.id}</td>
                <td className="px-3 py-2 truncate max-w-sm" title={t.title || t.task}>{t.title || t.task}</td>
                {/* Type — editable select */}
                <td className="px-3 py-1">
                  <select
                    value={t.type || "task"}
                    onChange={(e) => handleFieldChange(t.id, "type", e.target.value)}
                    className={selectClass}
                    style={{ opacity: isSaving("type") ? 0.5 : 1 }}
                    title="Change type"
                  >
                    {TYPE_OPTIONS.map((opt) => (
                      <option key={opt} value={opt}>{TYPE_ICONS[opt] || "•"} {opt}</option>
                    ))}
                  </select>
                </td>
                {/* Status — editable select */}
                <td className="px-3 py-1">
                  <select
                    value={t.status || "backlog"}
                    onChange={(e) => handleFieldChange(t.id, "status", e.target.value)}
                    className={`${selectClass} ${STATUS_COLORS[t.status] || "bg-gray-100"}`}
                    style={{ opacity: isSaving("status") ? 0.5 : 1 }}
                    title="Change status"
                  >
                    {STATUS_OPTIONS.map((opt) => (
                      <option key={opt} value={opt}>{opt.replace("_", " ")}</option>
                    ))}
                  </select>
                </td>
                {/* Priority — editable select */}
                <td className="px-3 py-1">
                  <select
                    value={t.priority || "medium"}
                    onChange={(e) => handleFieldChange(t.id, "priority", e.target.value)}
                    className={`${selectClass} ${PRIORITY_COLORS[t.priority] || "bg-gray-100"}`}
                    style={{ opacity: isSaving("priority") ? 0.5 : 1 }}
                    title="Change priority"
                  >
                    {PRIORITY_OPTIONS.map((opt) => (
                      <option key={opt} value={opt}>{opt}</option>
                    ))}
                  </select>
                </td>
                {/* Tool — read-only (requires assign endpoint) */}
                <td className="px-3 py-2 text-indigo-600 font-medium">
                  {t.assigned_tool?.tool || "—"}
                </td>
                {/* Assignee — read-only */}
                <td className="px-3 py-2 text-gray-600">
                  {t.assigned_user?.name || "—"}
                </td>
                {/* Hours — editable input */}
                <td className="px-3 py-1 text-center">
                  <input
                    type="number"
                    min="0"
                    step="0.5"
                    value={t.estimated_hours > 0 ? t.estimated_hours : ""}
                    placeholder="—"
                    onChange={(e) => handleHoursChange(t.id, e.target.value)}
                    className="w-14 text-center text-xs bg-transparent border-0 border-b border-transparent hover:border-gray-300 focus:border-blue-400 focus:ring-0 focus:outline-none py-0.5"
                    style={{ opacity: isSaving("estimated_hours") ? 0.5 : 1 }}
                    title="Set estimated hours"
                  />
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

// ── ProjectCard — name + assignment overview ──────────────────

function ProjectCard({ project, onRefresh, isSelected, onSelect, showingTickets, onToggleTickets, onEdit = () => {} }) {
  const [tickets, setTickets] = useState(null);
  const [deleting, setDeleting] = useState(false);
  const projectKey = _projectKey(project);
  const assignedTools = project.assigned_tool_details || (project.assigned_tools || []).map((name) => ({ name }));
  const assignedEnvironments = project.assigned_environments || [];
  const assignedLlms = project.assigned_llms || [];
  const assignedUsers = project.assigned_users || [];

  const ticketCount = project.ticket_count || 0;
  const envCount = assignedEnvironments.length;
  const toolCount = assignedTools.length;
  const llmCount = assignedLlms.length;
  const userCount = assignedUsers.length;

  const handleToggleTickets = async (e) => {
    e.stopPropagation();
    if (showingTickets) {
      onToggleTickets(null);
      return;
    }
    onToggleTickets(projectKey);
    if (tickets === null) {
      try {
        const data = await get(`/dashboard/projects/${projectKey}/tickets`);
        setTickets(Array.isArray(data) ? data : []);
      } catch (err) {
        console.error("Failed to load project tickets:", err);
        setTickets([]);
      }
    }
  };

  const handleDelete = async (e) => {
    e.stopPropagation();
    if (!confirm(`Delete project "${project.name}"?`)) return;
    setDeleting(true);
    try {
      _normalizeApiResult(await del(`/dashboard/projects/${project.id}`), "Failed to delete project");
      onRefresh();
    } catch (err) {
      console.error("Delete failed:", err);
    } finally {
      setDeleting(false);
    }
  };

  const handleEdit = (e) => {
    e.stopPropagation();
    if (onEdit) {
      onEdit(project);
    }
  };

  return (
    <div
      className={`bg-white rounded-lg border p-4 hover:shadow-md transition-shadow text-left w-full ${
        isSelected ? "border-blue-400 bg-blue-50" : "border-gray-200"
      }`}
    >
      <div className="flex items-center justify-between mb-3">
        <button
          type="button"
          onClick={() => onSelect(projectKey)}
          className="flex min-w-0 flex-1 items-center gap-2 text-left"
          aria-pressed={isSelected}
          aria-label={`Select project ${project.name}`}
        >
          <span className="text-lg">📋</span>
          <span className="min-w-0 flex-1">
            <span className="block truncate font-semibold text-gray-900 text-base">{project.name}</span>
            {project.description && <span className="block truncate text-xs text-gray-400">{project.description}</span>}
          </span>
        </button>
        <div className="flex items-center gap-1">
          {onEdit && (
            <button
              type="button"
              onClick={handleEdit}
              className="text-xs text-gray-500 hover:text-blue-600 px-1.5 py-0.5 rounded hover:bg-blue-50 transition-colors"
              title="Edit project"
              aria-label={`Edit project ${project.name}`}
            >
              ✎
            </button>
          )}
          <button
            type="button"
            onClick={handleDelete}
            disabled={deleting}
            className="text-xs text-gray-400 hover:text-red-500 px-1.5 py-0.5 rounded hover:bg-red-50 transition-colors"
            title="Delete project"
            aria-label={`Delete project ${project.name}`}
          >
            ✕
          </button>
        </div>
      </div>

      {project.source_path && (
        <div className="mb-3 text-xs text-gray-400 font-mono truncate" title={project.source_path}>
          {project.source_path}
        </div>
      )}

      <div className="flex flex-wrap gap-2 mb-3">
        <AssignmentBadge icon="📝" label="Tasks" count={ticketCount} color="bg-blue-50 border-blue-200 text-blue-700" />
        <AssignmentBadge icon="🖥️" label="Envs" count={envCount} color="bg-emerald-50 border-emerald-200 text-emerald-700" />
        <AssignmentBadge icon="🔧" label="Tools" count={toolCount} color="bg-amber-50 border-amber-200 text-amber-700" />
        <AssignmentBadge icon="🤖" label="LLMs" count={llmCount} color="bg-indigo-50 border-indigo-200 text-indigo-700" />
        <AssignmentBadge icon="👤" label="Users" count={userCount} color="bg-purple-50 border-purple-200 text-purple-700" />
      </div>

      {toolCount > 0 && (
        <div className="flex flex-wrap gap-1 mb-2">
          {assignedTools.map((tool) => (
            <span key={tool.name} className="text-xs px-2 py-0.5 bg-orange-50 text-orange-600 rounded-full border border-orange-200">
              {tool.name}
              {tool.default_model ? <span className="text-orange-400"> · {tool.default_model}</span> : null}
            </span>
          ))}
        </div>
      )}

      {assignedLlms.length > 0 && (
        <div className="flex flex-wrap gap-1 mb-2">
          {assignedLlms.map((llm) => (
            <span key={llm} className="text-xs px-2 py-0.5 bg-indigo-50 text-indigo-600 rounded-full border border-indigo-200">
              {llm}
            </span>
          ))}
        </div>
      )}

      {envCount > 0 && (
        <div className="flex flex-wrap gap-1 mb-2">
          {assignedEnvironments.map((e) => (
            <span key={e.id} className="text-xs px-2 py-0.5 bg-teal-50 text-teal-600 rounded-full border border-teal-200">
              {e.name} <span className="text-teal-400">({e.kind})</span>
            </span>
          ))}
        </div>
      )}

      {userCount > 0 && (
        <div className="flex flex-wrap gap-1 mb-2">
          {assignedUsers.map((user) => (
            <span key={user.id} className="text-xs px-2 py-0.5 bg-purple-50 text-purple-600 rounded-full border border-purple-200">
              {user.name}
              {user.role ? <span className="text-purple-400"> · {user.role}</span> : null}
            </span>
          ))}
        </div>
      )}

      <div className="flex items-center gap-2">
        <button onClick={handleToggleTickets}
          className="text-xs px-2.5 py-1 bg-gray-100 text-gray-600 rounded hover:bg-gray-200">
          {showingTickets ? "Hide tickets" : `Show tickets (${ticketCount})`}
        </button>
      </div>

      {showingTickets && (
        <>
          {tickets === null && <p className="text-xs text-gray-400 mt-3">Loading...</p>}
          {tickets && <TicketTable tickets={tickets} project={project} />}
        </>
      )}
    </div>
  );
}

// ── ProjectFormUnified — single form with Create / Edit tabs ──

function ProjectFormUnified({ project, onSaved, onCancel, options = {} }) {
  const isEditing = !!project;
  const [mode, setMode] = useState(isEditing ? "edit" : "create");

  // Sync mode when project prop changes (external edit trigger)
  useEffect(() => {
    setMode(project ? "edit" : "create");
  }, [project?.id]);

  const [name, setName] = useState(isEditing ? (project.name || "") : "");
  const [description, setDescription] = useState(isEditing ? (project.description || "") : "");
  const [selectedEnvs, setSelectedEnvs] = useState(
    isEditing
      ? (project.assigned_environments || project.environment_ids || []).map((env) => env?.id || env).filter(Boolean)
      : [],
  );
  const [selectedTools, setSelectedTools] = useState(
    isEditing
      ? (project.assigned_tool_details || project.assigned_tools || project.tool_names || [])
          .map((tool) => (typeof tool === "string" ? tool : tool?.name || tool?.tool || tool?.id || ""))
          .filter(Boolean)
      : [],
  );
  const [selectedLlms, setSelectedLlms] = useState(
    isEditing ? (project.assigned_llms || project.llm_names || []) : [],
  );
  const [selectedUsers, setSelectedUsers] = useState(
    isEditing
      ? (project.assigned_users || project.user_ids || []).map((user) => user?.id || user).filter(Boolean)
      : [],
  );
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState("");

  // Re-populate fields when switching to edit mode with a new project
  useEffect(() => {
    if (project && mode === "edit") {
      setName(project.name || "");
      setDescription(project.description || "");
      setSelectedEnvs(
        (project.assigned_environments || project.environment_ids || []).map((env) => env?.id || env).filter(Boolean),
      );
      setSelectedTools(
        (project.assigned_tool_details || project.assigned_tools || project.tool_names || [])
          .map((tool) => (typeof tool === "string" ? tool : tool?.name || tool?.tool || tool?.id || ""))
          .filter(Boolean),
      );
      setSelectedLlms(project.assigned_llms || project.llm_names || []);
      setSelectedUsers(
        (project.assigned_users || project.user_ids || []).map((user) => user?.id || user).filter(Boolean),
      );
      setError("");
    }
  }, [project?.id, mode]);

  const environmentOptionsSource = Array.isArray(options.environments) ? options.environments : [];
  const toolOptionsSource = Array.isArray(options.tools) ? options.tools : [];
  const modelOptionsSource = Array.isArray(options.models) ? options.models : [];
  const userOptionsSource = Array.isArray(options.users) ? options.users : [];

  const toolOptions = React.useMemo(() => {
    const seen = new Set();
    return toolOptionsSource
      .map((tool) => {
        const value = tool?.name || tool?.id || String(tool);
        if (!value || seen.has(value)) return null;
        seen.add(value);
        return {
          key: value,
          value,
          label: tool?.name || tool?.id || value,
          meta: tool?.default_model || tool?.description || tool?.default_instance || "",
        };
      })
      .filter(Boolean);
  }, [toolOptionsSource]);

  const modelOptions = React.useMemo(() => {
    const seen = new Set();
    return modelOptionsSource
      .map((model) => {
        const value = model?.id || model?.name || model?.model || model?.provider || String(model);
        if (!value || seen.has(value)) return null;
        seen.add(value);
        return {
          key: value,
          value,
          label: model?.name || model?.id || model?.model || value,
          meta: model?.provider || model?.type || model?.model || "",
        };
      })
      .filter(Boolean);
  }, [modelOptionsSource]);

  const userOptions = React.useMemo(() => {
    const seen = new Set();
    return userOptionsSource
      .map((user) => {
        const value = user?.id || user;
        if (!value || seen.has(value)) return null;
        seen.add(value);
        return {
          key: value,
          value,
          label: user?.name || user?.email || value,
          meta: user?.role || user?.email || "",
        };
      })
      .filter(Boolean);
  }, [userOptionsSource]);

  const handleSave = async () => {
    if (!name.trim()) { setError("Project name is required"); return; }
    setSubmitting(true);
    setError("");
    try {
      const payload = {
        name: name.trim(),
        description: description.trim(),
        environment_ids: selectedEnvs,
        tool_names: [...new Set(selectedTools)],
        llm_names: selectedLlms,
        user_ids: selectedUsers,
      };

      if (mode === "edit" && project) {
        await _normalizeApiResult(await put(`/dashboard/projects/${project.id}`, payload), "Failed to update project");
      } else {
        await _normalizeApiResult(await post("/dashboard/projects", payload), "Failed to create project");
      }

      if (onSaved) onSaved();
      if (mode === "create") resetForm();
    } catch (e) {
      setError(e.message || `Failed to ${mode === "edit" ? "update" : "create"} project`);
    }
    setSubmitting(false);
  };

  const resetForm = () => {
    setName("");
    setDescription("");
    setSelectedEnvs([]);
    setSelectedTools([]);
    setSelectedLlms([]);
    setSelectedUsers([]);
    setError("");
  };

  const handleSwitchToCreate = () => {
    setMode("create");
    resetForm();
    if (onCancel) onCancel();
  };

  const handleSwitchToEdit = () => {
    if (project) setMode("edit");
  };

  const isEditMode = mode === "edit" && project;
  const gradientClass = isEditMode
    ? "bg-gradient-to-r from-amber-50 to-orange-50 border-amber-200"
    : "bg-gradient-to-r from-blue-50 to-indigo-50 border-blue-200";
  const accentColor = isEditMode ? "amber" : "blue";
  const idPrefix = isEditMode ? "edit-project" : "project";

  return (
    <div className={`rounded-xl shadow-sm border p-5 transition-colors ${gradientClass}`} style={PROJECTS_THEME_SURFACE_STYLE}>
      {/* Tab bar */}
      <div className="flex items-center gap-1 mb-4 border-b pb-2" style={{ borderColor: "var(--dashboard-border)" }}>
        <button
          type="button"
          onClick={handleSwitchToCreate}
          className={`px-3 py-1.5 text-xs font-medium rounded-t-lg transition-colors ${
            mode === "create"
              ? "bg-blue-600 text-white"
              : "bg-gray-100 text-gray-600 hover:bg-gray-200"
          }`}
        >
          + Create
        </button>
        <button
          type="button"
          onClick={handleSwitchToEdit}
          disabled={!project}
          className={`px-3 py-1.5 text-xs font-medium rounded-t-lg transition-colors ${
            mode === "edit" && project
              ? "bg-amber-600 text-white"
              : "bg-gray-100 text-gray-400 hover:bg-gray-200 disabled:opacity-40 disabled:cursor-not-allowed"
          }`}
        >
          Edit{project ? `: ${project.name}` : ""}
        </button>
        <div className="flex-1" />
        {isEditMode && onCancel && (
          <button type="button" onClick={handleSwitchToCreate} className="text-xs text-gray-500 hover:text-gray-700">
            Cancel edit
          </button>
        )}
        {!isEditMode && (
          <button type="button" onClick={resetForm} className="text-xs text-gray-500 hover:text-gray-700">
            Reset
          </button>
        )}
      </div>

      <div className="space-y-4">
        <div className="space-y-2">
          <label className="text-xs font-medium text-gray-600" htmlFor={`${idPrefix}-name-input`}>
            Project Name *
          </label>
          <textarea
            id={`${idPrefix}-name-input`}
            value={name}
            onChange={e => setName(e.target.value)}
            placeholder="What should this project be called?"
            rows="2"
            className="w-full px-3 py-2 border border-blue-200 rounded-lg text-sm focus:ring-2 focus:ring-blue-300 focus:outline-none resize-none"
            style={PROJECTS_THEME_CONTROL_STYLE}
            onKeyDown={(e) => {
              if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
                e.preventDefault();
                handleSave();
              }
            }}
          />
        </div>

        <div className="space-y-2">
          <label className="text-xs font-medium text-gray-600" htmlFor={`${idPrefix}-description-input`}>
            Description
          </label>
          <input
            id={`${idPrefix}-description-input`}
            value={description}
            onChange={e => setDescription(e.target.value)}
            placeholder="Optional project notes"
            className="w-full px-3 py-2 border border-blue-200 rounded-lg text-sm focus:ring-2 focus:ring-blue-300 focus:outline-none"
            style={PROJECTS_THEME_CONTROL_STYLE}
          />
        </div>

        <div className="grid gap-3 lg:grid-cols-2">
          <MultiSelectSection
            title="Environments"
            subtitle="Attach many environments to the project"
            icon="🖥️"
            items={environmentOptionsSource}
            selectedValues={selectedEnvs}
            onToggle={(envId) => setSelectedEnvs((prev) => _toggleValue(prev, envId))}
            loading={false}
            itemKey={(env) => env.id}
            itemLabel={(env) => env.name || env.id}
            itemMeta={_environmentMeta}
            emptyMessage="No environments available"
          />

          <MultiSelectSection
            title="Tools"
            subtitle="Choose one or many tool runtimes"
            icon="🔧"
            items={toolOptions}
            selectedValues={selectedTools}
            onToggle={(toolName) => setSelectedTools((prev) => _toggleValue(prev, toolName))}
            loading={false}
            itemKey={(tool) => tool.value}
            itemLabel={(tool) => tool.label}
            itemMeta={(tool) => tool.meta}
            emptyMessage="No tools available"
          />

          <MultiSelectSection
            title="LLMs"
            subtitle="Attach any number of models"
            icon="🤖"
            items={modelOptions}
            selectedValues={selectedLlms}
            onToggle={(modelKey) => setSelectedLlms((prev) => _toggleValue(prev, modelKey))}
            loading={false}
            itemKey={(model) => model.value}
            itemLabel={(model) => model.label}
            itemMeta={(model) => model.meta}
            emptyMessage="No models available"
          />

          <MultiSelectSection
            title="Users"
            subtitle="Assign multiple users to the project"
            icon="👤"
            items={userOptions}
            selectedValues={selectedUsers}
            onToggle={(userId) => setSelectedUsers((prev) => _toggleValue(prev, userId))}
            loading={false}
            itemKey={(user) => user.value}
            itemLabel={(user) => user.label}
            itemMeta={(user) => user.meta}
            emptyMessage="No users available"
          />
        </div>

        <div className="flex gap-2 pt-1">
          <button
            onClick={handleSave}
            disabled={submitting || !name.trim()}
            className={`flex-1 px-4 py-2 text-white text-sm font-medium rounded-lg hover:opacity-90 disabled:opacity-50 transition-colors ${
              isEditMode ? "bg-amber-600 hover:bg-amber-700" : "bg-blue-600 hover:bg-blue-700"
            }`}
          >
            {submitting
              ? (isEditMode ? "Saving..." : "Creating...")
              : (isEditMode ? "Save Changes" : "Create Project")}
          </button>
          {isEditMode && onCancel && (
            <button
              type="button"
              onClick={handleSwitchToCreate}
              className="px-4 py-2 bg-gray-100 text-gray-700 text-sm font-medium rounded-lg hover:bg-gray-200 transition-colors"
            >
              Cancel
            </button>
          )}
          {!isEditMode && (
            <button
              type="button"
              onClick={resetForm}
              className="px-4 py-2 bg-gray-100 text-gray-700 text-sm font-medium rounded-lg hover:bg-gray-200 transition-colors"
            >
              Clear
            </button>
          )}
        </div>

        {error && <p className="text-xs text-red-600 font-medium">{error}</p>}
      </div>
    </div>
  );
}

// ── ProjectsPanel (main) ─────────────────────────────────────
function ProjectsPanel({ selectedProject, setSelectedProject, lastAction, viewMode, setLastAction, setViewMode, setContext }) {
  const { projects, loading, refresh } = useProjects();
  const [expandedProject, setExpandedProject] = useState(null);
  const [editingProjectId, setEditingProjectId] = useState(null);
  const [projectOptions, setProjectOptions] = useState({
    environments: [],
    tools: [],
    models: [],
    users: []
  });

  useEffect(() => {
    const isProjectTicketsView = lastAction === "view_project_tickets" || viewMode === "project_tickets";
    setExpandedProject(isProjectTicketsView ? selectedProject : null);
  }, [selectedProject, lastAction, viewMode]);

  useEffect(() => {
    const loadProjectOptions = async () => {
      try {
        const [envsData, toolsData, modelsData, usersData] = await Promise.all([
          get("/dashboard/environments"),
          get("/dashboard/tools"),
          get("/v1/models"),
          get("/dashboard/users?include_inactive=true"),
        ]);
        let modelList = [];
        if (Array.isArray(modelsData?.data)) {
          modelList = modelsData.data;
        } else if (Array.isArray(modelsData?.models)) {
          modelList = modelsData.models;
        }
        setProjectOptions({
          environments: envsData?.environments || [],
          tools: toolsData?.tools || [],
          models: modelList,
          users: usersData?.users || []
        });
      } catch (e) {
        console.error("Failed to load project options:", e);
      }
    };
    loadProjectOptions();
  }, []);

  const syncProjectUrl = useCallback((projectKey, action, view) => {
    const url = new URL(globalThis.location.href);
    url.hash = "#projects";
    if (projectKey) {
      url.searchParams.set("project", projectKey);
    } else {
      url.searchParams.delete("project");
    }
    if (action) url.searchParams.set("action", action);
    else url.searchParams.delete("action");
    if (view) url.searchParams.set("view", view);
    else url.searchParams.delete("view");
    url.searchParams.set("context", "projects");
    globalThis.history.replaceState({}, "", url);
  }, []);

  const handleSelectProject = useCallback((projectKey) => {
    setSelectedProject(projectKey);
    setExpandedProject(null);
    setEditingProjectId(null);
    setLastAction("select_project");
    setViewMode(null);
    setContext("projects");
    syncProjectUrl(projectKey, "select_project", null);
  }, [setSelectedProject, setLastAction, setViewMode, setContext, syncProjectUrl]);

  const handleEditProject = useCallback((project) => {
    setSelectedProject(_projectKey(project));
    setEditingProjectId(project.id);
    setExpandedProject(null);
    setLastAction("select_project");
    setViewMode(null);
    setContext("projects");
    syncProjectUrl(_projectKey(project), "select_project", null);
  }, [setSelectedProject, setLastAction, setViewMode, setContext, syncProjectUrl]);

  const handleCancelEdit = useCallback(() => {
    setEditingProjectId(null);
    setLastAction("select_project");
    setViewMode(null);
    setContext("projects");
    syncProjectUrl(selectedProject, "select_project", null);
  }, [selectedProject, setLastAction, setViewMode, setContext, syncProjectUrl]);

  const handleProjectSaved = useCallback(async () => {
    setEditingProjectId(null);
    await refresh();
  }, [refresh]);

  const handleToggleTickets = useCallback((projectKey) => {
    if (!projectKey) {
      setExpandedProject(null);
      setEditingProjectId(null);
      setLastAction("select_project");
      setViewMode(null);
      setContext("projects");
      syncProjectUrl(selectedProject, "select_project", null);
      return;
    }
    setSelectedProject(projectKey);
    setEditingProjectId(null);
    if (expandedProject === projectKey) {
      setExpandedProject(null);
      setLastAction("select_project");
      setViewMode(null);
      setContext("projects");
      syncProjectUrl(projectKey, "select_project", null);
      return;
    }
    setExpandedProject(projectKey);
    setLastAction("view_project_tickets");
    setViewMode("project_tickets");
    setContext("projects");
    syncProjectUrl(projectKey, "view_project_tickets", "project_tickets");
  }, [expandedProject, selectedProject, setSelectedProject, setLastAction, setViewMode, setContext, syncProjectUrl]);

  const editingProject = projects.find(p => p.id === editingProjectId);

  const stats = React.useMemo(() => ({
    total: projects.length,
    totalTickets: projects.reduce((sum, p) => sum + (p.ticket_count || 0), 0),
    totalEnvs: projects.reduce((sum, p) => sum + (p.assigned_environments || []).length, 0),
    totalTools: projects.reduce((sum, p) => sum + (p.assigned_tools || []).length, 0),
    totalLlms: projects.reduce((sum, p) => sum + (p.assigned_llms || []).length, 0),
    totalUsers: projects.reduce((sum, p) => sum + (p.assigned_users || []).length, 0),
  }), [projects]);

  const projectCountSuffix = projects.length === 1 ? "" : "s";

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

  return (
    <div className="space-y-4">
      <ProjectFormUnified
        key={editingProject ? editingProject.id : "project-create"}
        project={editingProject || null}
        onSaved={handleProjectSaved}
        onCancel={handleCancelEdit}
        options={projectOptions}
      />

      {/* Stats */}
      <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 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">Projects</div>
        </div>
        <div className="bg-white rounded-lg border p-3 text-center">
          <div className="text-2xl font-bold text-blue-600">{stats.totalTickets}</div>
          <div className="text-xs text-gray-500">Total Tasks</div>
        </div>
        <div className="bg-white rounded-lg border p-3 text-center">
          <div className="text-2xl font-bold text-emerald-600">{stats.totalEnvs}</div>
          <div className="text-xs text-gray-500">Environments</div>
        </div>
        <div className="bg-white rounded-lg border p-3 text-center">
          <div className="text-2xl font-bold text-amber-600">{stats.totalTools}</div>
          <div className="text-xs text-gray-500">Tools</div>
        </div>
        <div className="bg-white rounded-lg border p-3 text-center">
          <div className="text-2xl font-bold text-indigo-600">{stats.totalLlms}</div>
          <div className="text-xs text-gray-500">LLMs</div>
        </div>
        <div className="bg-white rounded-lg border p-3 text-center">
          <div className="text-2xl font-bold text-purple-600">{stats.totalUsers}</div>
          <div className="text-xs text-gray-500">Users</div>
        </div>
      </div>

      {/* Toolbar */}
      <div className="flex items-center justify-between flex-wrap gap-2">
        <div className="text-sm text-gray-500">{projects.length} project{projectCountSuffix}</div>
        <div className="flex gap-2">
          <button onClick={refresh}
            className="px-3 py-1.5 bg-gray-100 text-gray-700 text-sm rounded hover:bg-gray-200">
            ↻ Refresh
          </button>
        </div>
      </div>

      {/* Project cards */}
      <div className="space-y-3">
        {projects.length === 0 ? (
          <Card title="No projects">
            <p className="text-gray-500 text-sm">
              No projects yet. Use the quick create form above, or tickets will auto-create date-based projects.
            </p>
          </Card>
        ) : (
          projects.map((p) => (
            <ProjectCard
              key={p.id}
              project={p}
              onRefresh={refresh}
              isSelected={selectedProject === _projectKey(p)}
              onSelect={handleSelectProject}
              showingTickets={expandedProject === _projectKey(p)}
              onToggleTickets={handleToggleTickets}
              onEdit={handleEditProject}
            />
          ))
        )}
      </div>
    </div>
  );
}
