import { type Application } from 'express';
import { Repository } from '../db/repository.js';
import type { BrowserSessionRepo } from '../db/browser-session-repo.js';
import { type LocalTasksDeps } from './local-tasks-shared.js';
import { registerLocalTaskCrudRoutes } from './local-tasks-crud-api.js';
import { registerLocalTaskCommentsRoutes } from './local-tasks-comments-api.js';
import { registerLocalTaskToolRequestRoutes } from './local-tasks-tool-requests-api.js';
import { registerLocalTaskPackageRequestRoutes } from './local-tasks-package-requests-api.js';
import { registerLocalTaskControlRoutes } from './local-tasks-control-api.js';
import { registerLocalTaskStreamRoutes } from './local-tasks-stream-api.js';

export interface LocalTasksApiOptions {
  repo: Repository;
  worktreeDir?: string;
  /**
   * Per-user folder root (config.userFolderRoot). Used to resolve the effective
   * space for a new task's workspace path. Defaults to './data/users'.
   */
  userFolderRoot?: string;
  generateTitle?: (body: string, ownerId?: string) => Promise<string>;
  selectPiece?: (body: string, fileNames: string[], userId?: string) => Promise<string>;
  /**
   * Server-side validator for piece names accepted by the
   * /continue endpoint. Returns true if the piece is loadable.
   * When unset, /continue rejects all requests with 500 (misconfiguration).
   *
   * `ownerId` is the task owner's user id; when provided the implementation
   * MUST also check the owner's per-user piece dir so that user-custom pieces
   * are accepted (matching the resolution order the worker uses at run time).
   */
  pieceExists?: (name: string, ownerId?: string) => boolean;
  /**
   * Optional. When set, accepting browserSessionProfileId on task create
   * verifies the profile belongs to the requesting user. Without it, the
   * field is silently dropped (legacy / no-auth deployments).
   */
  sessRepo?: BrowserSessionRepo;
  /**
   * Optional. Returns the current upload size limit (MB) for task creation
   * and comment posting. Called per request so config changes take effect
   * without a server restart. Clamped to [1, 1000] MB. Default: 50.
   */
  getMaxUploadMb?: () => number;
  /**
   * Whether the auth subsystem is wired. When `false` (no-auth single-user
   * deployment) requests carry no `req.user`, so new tasks/jobs are registered
   * under the stable `local` owner instead of NULL — keeping per-task
   * reflection / visibility lookups (which key on owner_id) consistent with the
   * rest of the no-auth path. Defaults to `true` (owner stays req.user.id).
   */
  authActive?: boolean;
  /**
   * Optional. On-demand prompt coach: evaluates a draft task prompt (before any
   * task is created) using the cheap model + the user's own context. When unset,
   * the /evaluate-prompt route returns 503. Stateless — nothing is persisted.
   */
  evaluatePrompt?: (req: {
    instruction: string;
    piece?: string;
    userId: string;
    signal?: AbortSignal;
  }) => Promise<unknown>;
  /** Timeout (ms) for a prompt-coach evaluation before it is aborted. Default 30000. */
  evaluatePromptTimeoutMs?: number;
  /**
   * Package-request mechanism (PR3): returns the current Python packages config
   * so an approval can install into the task's space overlay. Called per
   * request so config changes take effect without a restart. When unset,
   * approving a package request fails with 400 (feature disabled).
   */
  getPythonPackagesConfig?: () => import('../config.js').PythonPackagesConfigShape | undefined;
}

/**
 * Mount the /api/local/tasks/* routes. The route handlers live in focused
 * sibling modules (crud / comments / tool-requests / control / stream); this
 * function only builds the shared dependency bundle and registers each group.
 */
export function mountLocalTasksApi(app: Application, opts: LocalTasksApiOptions): void {
  // No-auth single-user mode: register new tasks/jobs under the 'local' owner
  // (the same namespace pieces/memory/reflection use) rather than letting the
  // missing req.user fall through to a NULL owner. In auth mode this is
  // undefined, so `req.user.id ?? noAuthOwner` keeps the real owner.
  const noAuthOwner: string | undefined = (opts.authActive ?? true) ? undefined : 'local';

  const deps: LocalTasksDeps = {
    repo: opts.repo,
    worktreeDir: opts.worktreeDir,
    userFolderRoot: opts.userFolderRoot ?? './data/users',
    sessRepo: opts.sessRepo,
    noAuthOwner,
    opts,
  };

  registerLocalTaskCrudRoutes(app, deps);
  registerLocalTaskToolRequestRoutes(app, deps);
  registerLocalTaskPackageRequestRoutes(app, deps);
  registerLocalTaskCommentsRoutes(app, deps);
  registerLocalTaskControlRoutes(app, deps);
  registerLocalTaskStreamRoutes(app, deps);
}
