// Copyright (c) 2026 Hellmai Ltd
// SPDX-License-Identifier: AGPL-3.0-only

/**
 * Gates Presets
 *
 * WU-2037: Extracted from gates-config.ts
 *
 * Language ecosystem preset definitions and expansion logic.
 *
 * @module gates-presets
 */

import type { GatesExecutionConfig } from './gates-schemas.js';

/**
 * Gate preset definitions
 *
 * These provide sensible defaults for common language ecosystems.
 * Users can override fields via workspace.yaml software_delivery
 */
export const GATE_PRESETS: Record<string, Partial<GatesExecutionConfig>> = {
  node: {
    setup: 'npm ci || npm install',
    format: 'npx prettier --check .',
    lint: 'npx eslint .',
    typecheck: 'npx tsc --noEmit',
    test: 'npm test',
  },
  python: {
    setup: 'pip install -e ".[dev]" || pip install -r requirements.txt',
    format: 'ruff format --check .',
    lint: 'ruff check .',
    typecheck: 'mypy .',
    test: 'pytest',
  },
  go: {
    format: 'gofmt -l . | grep -v "^$" && exit 1 || exit 0',
    lint: 'golangci-lint run',
    typecheck: 'go vet ./...',
    test: 'go test ./...',
  },
  rust: {
    format: 'cargo fmt --check',
    lint: 'cargo clippy -- -D warnings',
    typecheck: 'cargo check',
    test: 'cargo test',
  },
  dotnet: {
    setup: 'dotnet restore',
    format: 'dotnet format --verify-no-changes',
    lint: 'dotnet build --no-restore -warnaserror',
    test: 'dotnet test --no-restore',
  },
  // WU-1118: Java/JVM, Ruby, and PHP presets
  java: {
    format: 'mvn spotless:check || ./gradlew spotlessCheck',
    lint: 'mvn checkstyle:check || ./gradlew checkstyleMain',
    typecheck: 'mvn compile -DskipTests || ./gradlew compileJava',
    test: 'mvn test || ./gradlew test',
  },
  ruby: {
    setup: 'bundle install',
    format: 'bundle exec rubocop --format simple --fail-level W',
    lint: 'bundle exec rubocop',
    test: 'bundle exec rspec',
  },
  php: {
    setup: 'composer install',
    format: 'vendor/bin/php-cs-fixer fix --dry-run --diff',
    lint: 'vendor/bin/phpstan analyse',
    test: 'vendor/bin/phpunit',
  },
};

/**
 * Expand a preset name into its default gate commands
 *
 * @param preset - Preset name (node, python, go, rust, dotnet) or undefined
 * @returns Partial gates config with preset defaults, or empty object if unknown
 */
export function expandPreset(preset: string | undefined): Partial<GatesExecutionConfig> {
  if (!preset) {
    return {};
  }

  return GATE_PRESETS[preset] ?? {};
}

/**
 * WU-2547: Preset-specific format-fix remediation commands.
 *
 * The `format` key in GATE_PRESETS above is the *verification* command used
 * by gates (e.g. `dotnet format --verify-no-changes`). When an agent or
 * user needs to actually fix a formatting failure they need the mutating
 * equivalent of that command — `dotnet format`, `cargo fmt`, etc.
 *
 * Onboarding templates used to hardcode `pnpm prettier --write <file>`,
 * which silently teaches agents the wrong habit in non-JS repos. init.ts
 * now resolves this map against the chosen preset and substitutes the
 * result into the `{{format_fix_command}}` placeholder.
 */
export const PRESET_FIX_COMMANDS: Record<string, string> = {
  node: 'pnpm prettier --write <file>',
  python: 'ruff format <file>',
  go: 'gofmt -w <file>',
  rust: 'cargo fmt',
  dotnet: 'dotnet format',
  java: 'mvn spotless:apply',
  ruby: 'bundle exec rubocop -a <file>',
  php: 'vendor/bin/php-cs-fixer fix <file>',
};

/**
 * WU-2547: Safe fallback when a caller passes an unknown preset.
 * Mirrors PRESET_FIX_COMMANDS.node so the two always match; kept as its own
 * constant to satisfy noUncheckedIndexedAccess without a non-null assertion.
 */
const DEFAULT_FORMAT_FIX_COMMAND = 'pnpm prettier --write <file>';

/**
 * WU-2547: Resolve the format-fix remediation command for a preset.
 *
 * Used by init.ts to populate the `{{format_fix_command}}` token in
 * onboarding templates so a .NET repo gets `dotnet format` guidance
 * instead of `pnpm prettier --write`.
 *
 * @param preset - Gate preset name (node, dotnet, python, etc.)
 * @returns The remediation command for the preset, or the node default
 *          if the preset is unknown (never returns empty).
 */
export function getFormatFixCommand(preset: string): string {
  return PRESET_FIX_COMMANDS[preset] ?? DEFAULT_FORMAT_FIX_COMMAND;
}
