/**
 * Default action routing
 *
 * Handles the default (no subcommand) action: task execution,
 * pipeline mode, or interactive mode.
 */

import { info, error, withProgress } from '../../shared/ui/index.js';
import { confirm } from '../../shared/prompt/index.js';
import { getErrorMessage } from '../../shared/utils/index.js';
import { getLabel } from '../../shared/i18n/index.js';
import { fetchIssue, formatIssueAsTask, checkGhCli, parseIssueNumbers, type GitHubIssue } from '../../infra/github/index.js';
import { selectAndExecuteTask, determinePiece, saveTaskFromInteractive, createIssueFromTask, type SelectAndExecuteOptions } from '../../features/tasks/index.js';
import { executePipeline } from '../../features/pipeline/index.js';
import {
  interactiveMode,
  selectInteractiveMode,
  selectRecentSession,
  passthroughMode,
  quietMode,
  personaMode,
  resolveLanguage,
  type InteractiveModeResult,
} from '../../features/interactive/index.js';
import { getPieceDescription, loadGlobalConfig } from '../../infra/config/index.js';
import { DEFAULT_PIECE_NAME } from '../../shared/constants.js';
import { program, resolvedCwd, pipelineMode } from './program.js';
import { resolveAgentOverrides, parseCreateWorktreeOption, isDirectTask } from './helpers.js';

/**
 * Resolve issue references from CLI input.
 *
 * Handles two sources:
 * - --issue N option (numeric issue number)
 * - Positional argument containing issue references (#N or "#1 #2")
 *
 * Returns resolved issues and the formatted task text for interactive mode.
 * Throws on gh CLI unavailability or fetch failure.
 */
async function resolveIssueInput(
  issueOption: number | undefined,
  task: string | undefined,
): Promise<{ issues: GitHubIssue[]; initialInput: string } | null> {
  if (issueOption) {
    const ghStatus = checkGhCli();
    if (!ghStatus.available) {
      throw new Error(ghStatus.error);
    }
    const issue = await withProgress(
      'Fetching GitHub Issue...',
      (fetchedIssue) => `GitHub Issue fetched: #${fetchedIssue.number} ${fetchedIssue.title}`,
      async () => fetchIssue(issueOption),
    );
    return { issues: [issue], initialInput: formatIssueAsTask(issue) };
  }

  if (task && isDirectTask(task)) {
    const ghStatus = checkGhCli();
    if (!ghStatus.available) {
      throw new Error(ghStatus.error);
    }
    const tokens = task.trim().split(/\s+/);
    const issueNumbers = parseIssueNumbers(tokens);
    if (issueNumbers.length === 0) {
      throw new Error(`Invalid issue reference: ${task}`);
    }
    const issues = await withProgress(
      'Fetching GitHub Issue...',
      (fetchedIssues) => `GitHub Issues fetched: ${fetchedIssues.map((issue) => `#${issue.number}`).join(', ')}`,
      async () => issueNumbers.map((n) => fetchIssue(n)),
    );
    return { issues, initialInput: issues.map(formatIssueAsTask).join('\n\n---\n\n') };
  }

  return null;
}

/**
 * Execute default action: handle task execution, pipeline mode, or interactive mode.
 * Exported for use in slash-command fallback logic.
 */
export async function executeDefaultAction(task?: string): Promise<void> {
  const opts = program.opts();
  const agentOverrides = resolveAgentOverrides(program);
  const createWorktreeOverride = parseCreateWorktreeOption(opts.createWorktree as string | undefined);
  const selectOptions: SelectAndExecuteOptions = {
    autoPr: opts.autoPr === true,
    repo: opts.repo as string | undefined,
    piece: opts.piece as string | undefined,
    createWorktree: createWorktreeOverride,
  };

  // --- Pipeline mode (non-interactive): triggered by --pipeline ---
  if (pipelineMode) {
    const exitCode = await executePipeline({
      issueNumber: opts.issue as number | undefined,
      task: opts.task as string | undefined,
      piece: (opts.piece as string | undefined) ?? DEFAULT_PIECE_NAME,
      branch: opts.branch as string | undefined,
      autoPr: opts.autoPr === true,
      repo: opts.repo as string | undefined,
      skipGit: opts.skipGit === true,
      cwd: resolvedCwd,
      provider: agentOverrides?.provider,
      model: agentOverrides?.model,
    });

    if (exitCode !== 0) {
      process.exit(exitCode);
    }
    return;
  }

  // --- Normal (interactive) mode ---

  // Resolve --task option to task text (direct execution, no interactive mode)
  const taskFromOption = opts.task as string | undefined;
  if (taskFromOption) {
    await selectAndExecuteTask(resolvedCwd, taskFromOption, selectOptions, agentOverrides);
    return;
  }

  // Resolve issue references (--issue N or #N positional arg) before interactive mode
  let initialInput: string | undefined = task;

  try {
    const issueResult = await resolveIssueInput(opts.issue as number | undefined, task);
    if (issueResult) {
      selectOptions.issues = issueResult.issues;
      initialInput = issueResult.initialInput;
    }
  } catch (e) {
    error(getErrorMessage(e));
    process.exit(1);
  }

  // All paths below go through interactive mode
  const globalConfig = loadGlobalConfig();
  const lang = resolveLanguage(globalConfig.language);

  const pieceId = await determinePiece(resolvedCwd, selectOptions.piece);
  if (pieceId === null) {
    info(getLabel('interactive.ui.cancelled', lang));
    return;
  }

  const previewCount = globalConfig.interactivePreviewMovements;
  const pieceDesc = getPieceDescription(pieceId, resolvedCwd, previewCount);

  // Mode selection after piece selection
  const selectedMode = await selectInteractiveMode(lang, pieceDesc.interactiveMode);
  if (selectedMode === null) {
    info(getLabel('interactive.ui.cancelled', lang));
    return;
  }

  const pieceContext = {
    name: pieceDesc.name,
    description: pieceDesc.description,
    pieceStructure: pieceDesc.pieceStructure,
    movementPreviews: pieceDesc.movementPreviews,
  };

  let result: InteractiveModeResult;

  switch (selectedMode) {
    case 'assistant': {
      let selectedSessionId: string | undefined;
      const provider = globalConfig.provider;
      if (provider === 'claude') {
        const shouldSelectSession = await confirm(
          getLabel('interactive.sessionSelector.confirm', lang),
          false,
        );
        if (shouldSelectSession) {
          const sessionId = await selectRecentSession(resolvedCwd, lang);
          if (sessionId) {
            selectedSessionId = sessionId;
          }
        }
      }
      result = await interactiveMode(resolvedCwd, initialInput, pieceContext, selectedSessionId);
      break;
    }

    case 'passthrough':
      result = await passthroughMode(lang, initialInput);
      break;

    case 'quiet':
      result = await quietMode(resolvedCwd, initialInput, pieceContext);
      break;

    case 'persona': {
      if (!pieceDesc.firstMovement) {
        info(getLabel('interactive.ui.personaFallback', lang));
        result = await interactiveMode(resolvedCwd, initialInput, pieceContext);
      } else {
        result = await personaMode(resolvedCwd, pieceDesc.firstMovement, initialInput, pieceContext);
      }
      break;
    }
  }

  switch (result.action) {
    case 'execute':
      selectOptions.interactiveUserInput = true;
      selectOptions.piece = pieceId;
      selectOptions.interactiveMetadata = { confirmed: true, task: result.task };
      await selectAndExecuteTask(resolvedCwd, result.task, selectOptions, agentOverrides);
      break;

    case 'create_issue':
      {
        const issueNumber = createIssueFromTask(result.task);
        if (issueNumber !== undefined) {
          await saveTaskFromInteractive(resolvedCwd, result.task, pieceId, {
            issue: issueNumber,
            confirmAtEndMessage: 'Add this issue to tasks?',
          });
        }
      }
      break;

    case 'save_task':
      await saveTaskFromInteractive(resolvedCwd, result.task, pieceId);
      break;

    case 'cancel':
      break;
  }
}

program
  .argument('[task]', 'Task to execute (or GitHub issue reference like "#6")')
  .action(executeDefaultAction);
