const COLUMNS = ['queued', 'running', 'waiting_human', 'waiting_subtasks', 'retry', 'succeeded', 'failed', 'cancelled'] as const;
const DETAIL_TABS = ['overview', 'activity', 'files', 'trace', 'browser', 'ssh'] as const;
const MOBILE_TABS = ['chat', 'overview', 'activity', 'files', 'trace', 'browser', 'ssh'] as const;
const SORT_MODES = ['updated', 'status', 'title'] as const;
const PAGES = ['tasks', 'pieces', 'settings', 'schedules', 'users', 'captcha', 'userfolder', 'help'] as const;
const SETTINGS_SECTIONS = [
  // User group
  'preferences',
  'notifications',
  'memory-learning',
  // System group
  'branding',
  'paths-storage',
  'execution',
  // LLM group
  'llm-workers',
  'gateway-server',
  'llm-metrics',
  // Agent Runtime group
  'ask-subtasks',
  'context',
  'safety',
  'reflection',
  // Tools group (sub-sections)
  'tools-web',
  'tools-browser',
  'tools-media',
  'tools-external',
  'tools-legacy-knowledge',
  // MCP & Connections
  'mcp',
  // SSH
  'ssh',
  // Legacy ids kept so old URLs still parse; redirected at runtime via
  // LEGACY_SECTION_REDIRECT (see SettingsPage).
  'browser-sessions',
  'provider',
  'workspace',
  'tools',
  'browser-settings',
  'search-filter',
  // Step 8 (2026-05-21): folded into 'gateway-server' as the Virtual
  // Keys section. Keep parseable so bookmarks land softly.
  'gateway-keys',
] as const;

export type DetailTabId = (typeof DETAIL_TABS)[number];
export type MobileTabId = (typeof MOBILE_TABS)[number];
export type SortMode = (typeof SORT_MODES)[number];
export type StatusColumn = (typeof COLUMNS)[number];
export type PageId = (typeof PAGES)[number];
export type SettingsSection = (typeof SETTINGS_SECTIONS)[number];

export const COLUMN_LIST = COLUMNS;
export const DETAIL_TAB_LIST = DETAIL_TABS;
export const MOBILE_TAB_LIST = MOBILE_TABS;
export const SORT_MODE_LIST = SORT_MODES;

export const COLUMN_LABELS: Record<string, string> = {
  queued: 'Inbox',
  running: 'Running',
  waiting_human: 'Waiting',
  waiting_subtasks: 'Subtasks',
  retry: 'Retry',
  succeeded: 'Done',
  failed: 'Failed',
  cancelled: 'Cancelled',
};

export interface UiUrlState {
  page: PageId;
  repo: string;
  status: 'all' | StatusColumn;
  search: string;
  sort: SortMode;
  detailTab: DetailTabId;
  mobileTab: MobileTabId;
  taskId: number | null;
  section?: SettingsSection;
  piece?: string;
  /** Source of the currently-selected piece ('builtin' | 'user-custom' | 'global-custom'). */
  pieceSource?: 'builtin' | 'user-custom' | 'global-custom';
  /** Active SideInfoPanel widget slug. Default: 'worker-status'. */
  dashboardWidget?: string;
  /** Selected help section id. Only meaningful when page === 'help'. */
  help?: string;
}

export function readUiUrlState(): UiUrlState {
  if (typeof window === 'undefined') {
    return {
      page: 'tasks',
      repo: '',
      status: 'all',
      search: '',
      sort: 'updated',
      detailTab: 'overview',
      mobileTab: 'chat',
      taskId: null,
    };
  }

  const params = new URLSearchParams(window.location.search);
  const page = params.get('page');
  const status = params.get('status');
  const sort = params.get('sort');
  const detailTab = params.get('tab');
  const mobileTab = params.get('mobileTab');
  const taskId = Number(params.get('task') ?? '');
  const section = params.get('section');
  const piece = params.get('piece');
  const pieceSourceRaw = params.get('pieceSource');
  const PIECE_SOURCES = ['builtin', 'user-custom', 'global-custom'] as const;
  const pieceSource = PIECE_SOURCES.includes(pieceSourceRaw as any)
    ? (pieceSourceRaw as UiUrlState['pieceSource'])
    : undefined;
  const dashboardWidget = params.get('dashboardWidget');
  const help = params.get('help');

  return {
    page: page && PAGES.includes(page as PageId) ? page as PageId : 'tasks',
    repo: params.get('repo') ?? '',
    status: status && (status === 'all' || COLUMNS.includes(status as StatusColumn))
      ? status as UiUrlState['status']
      : 'all',
    search: params.get('q') ?? '',
    sort: sort && SORT_MODES.includes(sort as SortMode) ? sort as SortMode : 'updated',
    detailTab: detailTab && DETAIL_TABS.includes(detailTab as DetailTabId)
      ? detailTab as DetailTabId
      : 'overview',
    mobileTab: mobileTab && MOBILE_TABS.includes(mobileTab as MobileTabId)
      ? mobileTab as MobileTabId
      : 'chat',
    taskId: Number.isFinite(taskId) && taskId > 0 ? taskId : null,
    section: section && SETTINGS_SECTIONS.includes(section as SettingsSection) ? section as SettingsSection : undefined,
    piece: piece || undefined,
    ...(pieceSource ? { pieceSource } : {}),
    ...(dashboardWidget ? { dashboardWidget } : {}),
    ...(help ? { help } : {}),
  };
}

export function buildUiUrlStateSearch(state: UiUrlState): string {
  const params = new URLSearchParams();
  if (state.page !== 'tasks') params.set('page', state.page);
  if (state.repo) params.set('repo', state.repo);
  if (state.status !== 'all') params.set('status', state.status);
  if (state.search) params.set('q', state.search);
  if (state.sort !== 'updated') params.set('sort', state.sort);
  if (state.detailTab !== 'overview') params.set('tab', state.detailTab);
  if (state.mobileTab !== 'chat') params.set('mobileTab', state.mobileTab);
  if (state.taskId) params.set('task', String(state.taskId));
  if (state.section) params.set('section', state.section);
  if (state.piece) params.set('piece', state.piece);
  if (state.pieceSource) params.set('pieceSource', state.pieceSource);
  if (state.help) params.set('help', state.help);
  if (state.dashboardWidget && state.dashboardWidget !== 'worker-status') {
    params.set('dashboardWidget', state.dashboardWidget);
  }
  return params.toString();
}

export function paramsEqualExcept(
  left: URLSearchParams,
  right: URLSearchParams,
  ignoredKeys: string[],
): boolean {
  const ignored = new Set(ignoredKeys);
  const normalize = (params: URLSearchParams) =>
    Array.from(params.entries())
      .filter(([key]) => !ignored.has(key))
      .sort(([leftKey, leftValue], [rightKey, rightValue]) =>
        leftKey.localeCompare(rightKey) || leftValue.localeCompare(rightValue),
      );
  return JSON.stringify(normalize(left)) === JSON.stringify(normalize(right));
}
