import path from 'path';

import { DATA_DIR, GROUPS_DIR, TENANT_HOME_BASE } from './config.js';

const SEGMENT_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
const RESERVED_FOLDERS = new Set(['global']);

/**
 * Validate a simple folder name (single segment, no slashes).
 */
export function isValidFolderName(name: string): boolean {
  if (!name) return false;
  if (name !== name.trim()) return false;
  if (!SEGMENT_PATTERN.test(name)) return false;
  if (name.includes('..')) return false;
  if (RESERVED_FOLDERS.has(name.toLowerCase())) return false;
  return true;
}

/**
 * Validate a group folder — either a simple name ("general") or
 * a tenant-scoped composite ("tenantId/general").
 */
export function isValidGroupFolder(folder: string): boolean {
  if (!folder) return false;
  if (folder !== folder.trim()) return false;
  if (folder.includes('\\')) return false;
  if (folder.includes('..')) return false;

  // Composite: tenantId/folderName
  if (folder.includes('/')) {
    const parts = folder.split('/');
    if (parts.length !== 2) return false;
    return isValidFolderName(parts[0]) && isValidFolderName(parts[1]);
  }

  // Simple: just a folder name
  return isValidFolderName(folder);
}

export function assertValidGroupFolder(folder: string): void {
  if (!isValidGroupFolder(folder)) {
    throw new Error(`Invalid group folder "${folder}"`);
  }
}

/**
 * Build the composite folder key used for filesystem paths.
 * If tenantId is provided and folder isn't already composite, prepends it.
 */
export function groupKey(tenantId: string, folder: string): string {
  // Already composite
  if (folder.includes('/')) return folder;
  return `${tenantId}/${folder}`;
}

/**
 * Parse a composite folder into tenant and folder parts.
 * Returns undefined if the folder is not composite.
 */
function parseTenantFolder(folder: string):
  | {
      tenantId: string;
      folderName: string;
    }
  | undefined {
  if (!folder.includes('/')) return undefined;
  const parts = folder.split('/');
  if (parts.length !== 2) return undefined;
  return { tenantId: parts[0], folderName: parts[1] };
}

/**
 * Resolve the group folder path on the filesystem.
 * For tenant-scoped folders (tenant/folder), resolves to /home/<tenant>/nanoclaw/groups/<folder>.
 * For simple folders, resolves relative to GROUPS_DIR (process.cwd()/groups/).
 */
export function resolveGroupFolderPath(folder: string): string {
  assertValidGroupFolder(folder);

  const parsed = parseTenantFolder(folder);
  if (parsed) {
    const tenantHome = path.join(
      TENANT_HOME_BASE,
      parsed.tenantId,
      'nanoclaw',
      'groups',
      parsed.folderName,
    );
    return tenantHome;
  }

  // Simple folder — relative to GROUPS_DIR
  const groupPath = path.resolve(GROUPS_DIR, folder);
  const rel = path.relative(GROUPS_DIR, groupPath);
  if (rel.startsWith('..') || path.isAbsolute(rel)) {
    throw new Error(`Path escapes base directory: ${groupPath}`);
  }
  return groupPath;
}

/**
 * Resolve the IPC path for a group.
 * For tenant-scoped folders, resolves to /home/<tenant>/nanoclaw/data/ipc/<folder>.
 * For simple folders, resolves relative to DATA_DIR/ipc/.
 */
export function resolveGroupIpcPath(folder: string): string {
  assertValidGroupFolder(folder);

  const parsed = parseTenantFolder(folder);
  if (parsed) {
    return path.join(
      TENANT_HOME_BASE,
      parsed.tenantId,
      'nanoclaw',
      'data',
      'ipc',
      parsed.folderName,
    );
  }

  const ipcBaseDir = path.resolve(DATA_DIR, 'ipc');
  const ipcPath = path.resolve(ipcBaseDir, folder);
  const rel = path.relative(ipcBaseDir, ipcPath);
  if (rel.startsWith('..') || path.isAbsolute(rel)) {
    throw new Error(`Path escapes base directory: ${ipcPath}`);
  }
  return ipcPath;
}

/**
 * Resolve the sessions path for a group.
 * For tenant-scoped folders, resolves to /home/<tenant>/nanoclaw/data/sessions/<folder>.
 * For simple folders, resolves relative to DATA_DIR/sessions/.
 */
export function resolveGroupSessionsPath(folder: string): string {
  assertValidGroupFolder(folder);

  const parsed = parseTenantFolder(folder);
  if (parsed) {
    return path.join(
      TENANT_HOME_BASE,
      parsed.tenantId,
      'nanoclaw',
      'data',
      'sessions',
      parsed.folderName,
    );
  }

  const sessionsBaseDir = path.resolve(DATA_DIR, 'sessions');
  const sessionsPath = path.resolve(sessionsBaseDir, folder);
  const rel = path.relative(sessionsBaseDir, sessionsPath);
  if (rel.startsWith('..') || path.isAbsolute(rel)) {
    throw new Error(`Path escapes base directory: ${sessionsPath}`);
  }
  return sessionsPath;
}
