export function buildProjectGroups(threads = [], workspaces = [], hiddenProjects = []) {
  const projects = new Map();

  for (const workspace of workspaces) {
    const label = cleanLabel(workspace.label);
    if (!workspace.id || !label) continue;
    projects.set(workspace.id, { key: workspace.id, workspaceId: workspace.id, label, threads: [] });
  }

  for (const thread of threads) {
    const label = cleanLabel(thread.workspaceLabel);
    if (!label) continue;
    let group = thread.workspaceId ? projects.get(thread.workspaceId) : projects.get(`label:${label}`);
    const workspace = thread.workspaceId ? workspaces.find((item) => item.id === thread.workspaceId) : undefined;
    if (!group) {
      const key = thread.workspaceId || `label:${label}`;
      group = { key, workspaceId: thread.workspaceId || "", label: workspace?.label || label, threads: [] };
      projects.set(key, group);
    }
    group.threads.push(thread);
  }

  return [...projects.values()]
    .filter((group) => group.threads.length > 0)
    .filter((group) => latestTime(group.threads) > hiddenAt(hiddenProjects, group.key))
    .map((group) => ({ ...group, threads: sortThreads(group.threads) }))
    .sort((left, right) => latestTime(right.threads) - latestTime(left.threads) || left.label.localeCompare(right.label));
}

export function recentThreads(threads = []) {
  return sortThreads(threads);
}

export const INITIAL_PROJECT_THREAD_LIMIT = 5;
export const PROJECT_THREAD_LIMIT_STEP = 10;

export function visibleProjectThreads(threads = [], limit = INITIAL_PROJECT_THREAD_LIMIT) {
  const visibleCount = Number.isFinite(limit) ? Math.max(INITIAL_PROJECT_THREAD_LIMIT, Math.floor(limit)) : INITIAL_PROJECT_THREAD_LIMIT;
  return threads.slice(0, visibleCount);
}

export function nextProjectThreadLimit(current, total) {
  const currentLimit = Number.isFinite(current) ? Math.max(INITIAL_PROJECT_THREAD_LIMIT, Math.floor(current)) : INITIAL_PROJECT_THREAD_LIMIT;
  return Math.min(Math.max(0, total), currentLimit + PROJECT_THREAD_LIMIT_STEP);
}

export function projectlessThreads(threads = []) {
  return recentThreads(threads.filter((thread) => !cleanLabel(thread.workspaceId) && !cleanLabel(thread.workspaceLabel)));
}

export function shouldShowChatLoadMore(nextCursor, activeProject) {
  return Boolean(nextCursor) && !activeProject;
}

export function formatThreadAge(updatedAt, now = Date.now(), language = "en") {
  const then = Date.parse(updatedAt ?? "");
  if (!Number.isFinite(then)) return "";
  const minutes = Math.max(0, Math.floor((now - then) / 60000));
  if (minutes < 60) {
    const value = Math.max(1, minutes);
    return language === "ko" ? `${value}분 전` : `${value}m`;
  }
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return language === "ko" ? `${hours}시간 전` : `${hours}h`;
  const days = Math.floor(hours / 24);
  return language === "ko" ? `${days}일 전` : `${days}d`;
}

function hiddenAt(hiddenProjects, projectKey) {
  const value = hiddenProjects.find((item) => item.projectKey === projectKey)?.hiddenAt;
  return Date.parse(value ?? 0) || 0;
}

function sortThreads(threads) {
  return [...threads].sort((left, right) => Number(Boolean(right.pinned)) - Number(Boolean(left.pinned))
    || Date.parse(right.updatedAt ?? 0) - Date.parse(left.updatedAt ?? 0));
}

function latestTime(threads) {
  return Date.parse(threads[0]?.updatedAt ?? 0) || 0;
}

function cleanLabel(value) {
  return typeof value === "string" ? value.trim() : "";
}
