/**
 * Package Manager Utilities
 * Handles package manager-specific commands and configurations
 * Now supports multiple languages and their package managers
 */

import { getPackageManagerForTechnology } from './technology-registry';

export type PackageManager =
  // Node.js
  | 'npm' | 'yarn' | 'pnpm' | 'bun'
  // Python
  | 'pip' | 'poetry' | 'pipenv' | 'conda'
  // Go
  | 'go'
  // Rust
  | 'cargo'
  // Ruby
  | 'gem' | 'bundler'
  // PHP
  | 'composer'
  // Java
  | 'maven' | 'gradle'
  // .NET
  | 'nuget' | 'dotnet';

export interface PackageManagerCommands {
  install: string;
  installGlobal: (pkg: string) => string;
  run: (script: string) => string;
  test: string;
  build: string;
  add: (pkg: string) => string;
  addDev: (pkg: string) => string;
  link: string;
  exec: (cmd: string) => string;
}

/**
 * Get package manager from workflow config (technology-aware)
 */
export function getPackageManager(workflow: any, componentName?: string): PackageManager {
  let technology: string | undefined;
  let configuredPm: string | undefined;

  // Get technology and configured package manager for this component
  if (componentName && workflow.stack?.[componentName]) {
    const component = workflow.stack[componentName];
    technology = component.technology;
    configuredPm = component.config?.packageManager;
  }

  // If no component-specific config, check workflow-level
  if (!configuredPm && workflow.workflow?.config?.packageManager) {
    configuredPm = workflow.workflow.config.packageManager;
  }

  // If we have a technology, use technology-aware resolution
  if (technology) {
    return getPackageManagerForTechnology(technology, configuredPm) as PackageManager;
  }

  // Fallback to configured or default
  return (configuredPm || 'npm') as PackageManager;
}

/**
 * Get package manager commands for a given package manager
 */
export function getPackageManagerCommands(pm: PackageManager): PackageManagerCommands {
  const commands: Record<PackageManager, PackageManagerCommands> = {
    npm: {
      install: 'npm install',
      installGlobal: (pkg: string) => `npm install -g ${pkg}`,
      run: (script: string) => `npm run ${script}`,
      test: 'npm test',
      build: 'npm run build',
      add: (pkg: string) => `npm install ${pkg}`,
      addDev: (pkg: string) => `npm install --save-dev ${pkg}`,
      link: 'npm link',
      exec: (cmd: string) => `npx ${cmd}`,
    },
    yarn: {
      install: 'yarn install',
      installGlobal: (pkg: string) => `yarn global add ${pkg}`,
      run: (script: string) => `yarn ${script}`,
      test: 'yarn test',
      build: 'yarn build',
      add: (pkg: string) => `yarn add ${pkg}`,
      addDev: (pkg: string) => `yarn add --dev ${pkg}`,
      link: 'yarn link',
      exec: (cmd: string) => `yarn ${cmd}`,
    },
    pnpm: {
      install: 'pnpm install',
      installGlobal: (pkg: string) => `pnpm add -g ${pkg}`,
      run: (script: string) => `pnpm ${script}`,
      test: 'pnpm test',
      build: 'pnpm build',
      add: (pkg: string) => `pnpm add ${pkg}`,
      addDev: (pkg: string) => `pnpm add -D ${pkg}`,
      link: 'pnpm link',
      exec: (cmd: string) => `pnpm exec ${cmd}`,
    },
    bun: {
      install: 'bun install',
      installGlobal: (pkg: string) => `bun add -g ${pkg}`,
      run: (script: string) => `bun ${script}`,
      test: 'bun test',
      build: 'bun build',
      add: (pkg: string) => `bun add ${pkg}`,
      addDev: (pkg: string) => `bun add -d ${pkg}`,
      link: 'bun link',
      exec: (cmd: string) => `bun ${cmd}`,
    },
    // Python
    pip: {
      install: 'pip install -r requirements.txt',
      installGlobal: (pkg: string) => `pip install ${pkg}`,
      run: (script: string) => `python -m ${script}`,
      test: 'pytest',
      build: 'python setup.py build',
      add: (pkg: string) => `pip install ${pkg}`,
      addDev: (pkg: string) => `pip install ${pkg}`,
      link: 'pip install -e .',
      exec: (cmd: string) => `python -m ${cmd}`,
    },
    poetry: {
      install: 'poetry install',
      installGlobal: (pkg: string) => `pipx install ${pkg}`,
      run: (script: string) => `poetry run ${script}`,
      test: 'poetry run pytest',
      build: 'poetry build',
      add: (pkg: string) => `poetry add ${pkg}`,
      addDev: (pkg: string) => `poetry add --group dev ${pkg}`,
      link: 'poetry install',
      exec: (cmd: string) => `poetry run ${cmd}`,
    },
    pipenv: {
      install: 'pipenv install',
      installGlobal: (pkg: string) => `pip install ${pkg}`,
      run: (script: string) => `pipenv run ${script}`,
      test: 'pipenv run pytest',
      build: 'pipenv run python setup.py build',
      add: (pkg: string) => `pipenv install ${pkg}`,
      addDev: (pkg: string) => `pipenv install --dev ${pkg}`,
      link: 'pipenv install -e .',
      exec: (cmd: string) => `pipenv run ${cmd}`,
    },
    conda: {
      install: 'conda install --file requirements.txt',
      installGlobal: (pkg: string) => `conda install ${pkg}`,
      run: (script: string) => `python ${script}`,
      test: 'pytest',
      build: 'python setup.py build',
      add: (pkg: string) => `conda install ${pkg}`,
      addDev: (pkg: string) => `conda install ${pkg}`,
      link: 'conda develop .',
      exec: (cmd: string) => `${cmd}`,
    },
    // Go
    go: {
      install: 'go mod download',
      installGlobal: (pkg: string) => `go install ${pkg}@latest`,
      run: (script: string) => `go run ${script}`,
      test: 'go test ./...',
      build: 'go build',
      add: (pkg: string) => `go get ${pkg}`,
      addDev: (pkg: string) => `go get ${pkg}`, // No distinction in Go
      link: 'go install',
      exec: (cmd: string) => `go run ${cmd}`,
    },
    // Rust
    cargo: {
      install: 'cargo fetch',
      installGlobal: (pkg: string) => `cargo install ${pkg}`,
      run: (_script: string) => `cargo run`, // Cargo doesn't use script names
      test: 'cargo test',
      build: 'cargo build',
      add: (pkg: string) => `cargo add ${pkg}`,
      addDev: (pkg: string) => `cargo add --dev ${pkg}`,
      link: 'cargo install --path .',
      exec: (cmd: string) => `cargo ${cmd}`,
    },
    // Ruby
    gem: {
      install: 'gem install',
      installGlobal: (pkg: string) => `gem install ${pkg}`,
      run: (script: string) => `ruby ${script}`,
      test: 'rake test',
      build: 'gem build',
      add: (pkg: string) => `gem install ${pkg}`,
      addDev: (pkg: string) => `gem install ${pkg}`,
      link: 'gem install --local',
      exec: (cmd: string) => `${cmd}`,
    },
    bundler: {
      install: 'bundle install',
      installGlobal: (pkg: string) => `gem install ${pkg}`,
      run: (script: string) => `bundle exec ${script}`,
      test: 'bundle exec rspec',
      build: 'gem build',
      add: (pkg: string) => `bundle add ${pkg}`,
      addDev: (pkg: string) => `bundle add ${pkg} --group development`,
      link: 'bundle install --local',
      exec: (cmd: string) => `bundle exec ${cmd}`,
    },
    // PHP
    composer: {
      install: 'composer install',
      installGlobal: (pkg: string) => `composer global require ${pkg}`,
      run: (script: string) => `composer run ${script}`,
      test: 'composer test',
      build: 'composer build',
      add: (pkg: string) => `composer require ${pkg}`,
      addDev: (pkg: string) => `composer require --dev ${pkg}`,
      link: 'composer install',
      exec: (cmd: string) => `composer exec ${cmd}`,
    },
    // Java
    maven: {
      install: 'mvn install',
      installGlobal: (pkg: string) => `mvn install ${pkg}`,
      run: (_script: string) => `mvn exec:java`, // Maven uses pom.xml config
      test: 'mvn test',
      build: 'mvn package',
      add: (pkg: string) => `# Add to pom.xml: ${pkg}`,
      addDev: (pkg: string) => `# Add to pom.xml: ${pkg}`,
      link: 'mvn install',
      exec: (cmd: string) => `mvn ${cmd}`,
    },
    gradle: {
      install: 'gradle build',
      installGlobal: (pkg: string) => `gradle install ${pkg}`,
      run: (_script: string) => `gradle run`, // Gradle uses build.gradle config
      test: 'gradle test',
      build: 'gradle build',
      add: (pkg: string) => `# Add to build.gradle: ${pkg}`,
      addDev: (pkg: string) => `# Add to build.gradle: ${pkg}`,
      link: 'gradle install',
      exec: (cmd: string) => `gradle ${cmd}`,
    },
    // .NET
    nuget: {
      install: 'nuget restore',
      installGlobal: (pkg: string) => `nuget install ${pkg}`,
      run: (_script: string) => `dotnet run`, // NuGet uses project file config
      test: 'dotnet test',
      build: 'dotnet build',
      add: (pkg: string) => `nuget install ${pkg}`,
      addDev: (pkg: string) => `nuget install ${pkg}`,
      link: 'nuget install',
      exec: (cmd: string) => `dotnet ${cmd}`,
    },
    dotnet: {
      install: 'dotnet restore',
      installGlobal: (pkg: string) => `dotnet tool install -g ${pkg}`,
      run: (_script: string) => `dotnet run`, // .NET uses project file config
      test: 'dotnet test',
      build: 'dotnet build',
      add: (pkg: string) => `dotnet add package ${pkg}`,
      addDev: (pkg: string) => `dotnet add package ${pkg}`,
      link: 'dotnet build',
      exec: (cmd: string) => `dotnet ${cmd}`,
    },
  };

  return commands[pm];
}

/**
 * Generate installation instructions for all package managers
 */
export function generateInstallInstructions(
  packageName: string,
  preferredPm?: PackageManager
): string {
  const npm = `npm install -g ${packageName}`;
  const yarn = `yarn global add ${packageName}`;
  const pnpm = `pnpm add -g ${packageName}`;
  const bun = `bun add -g ${packageName}`;

  if (preferredPm) {
    const pmCommands = getPackageManagerCommands(preferredPm);
    return `\`\`\`bash
# Using ${preferredPm} (recommended)
${pmCommands.installGlobal(packageName)}

# Or using other package managers:
# npm
${npm}

# yarn
${yarn}

# pnpm
${pnpm}

# bun
${bun}
\`\`\``;
  }

  return `\`\`\`bash
# Using npm
${npm}

# Using yarn
${yarn}

# Using pnpm
${pnpm}

# Using bun
${bun}
\`\`\``;
}

/**
 * Get lock file name for package manager
 */
export function getLockFileName(pm: PackageManager): string | undefined {
  const lockFiles: Partial<Record<PackageManager, string>> = {
    npm: 'package-lock.json',
    yarn: 'yarn.lock',
    pnpm: 'pnpm-lock.yaml',
    bun: 'bun.lockb',
    poetry: 'poetry.lock',
    pipenv: 'Pipfile.lock',
    bundler: 'Gemfile.lock',
    composer: 'composer.lock',
    cargo: 'Cargo.lock',
    // go, pip, conda, gem, maven, gradle, nuget, dotnet don't use lock files or use different mechanisms
  };

  return lockFiles[pm];
}

/**
 * Get package manager setup for CI/CD (GitHub Actions)
 */
export function getCISetup(pm: PackageManager): { setup: string; cache: string } | undefined {
  const setups: Partial<Record<PackageManager, { setup: string; cache: string }>> = {
    npm: {
      setup: `- uses: actions/setup-node@v3
      with:
        node-version: '20'
        cache: 'npm'`,
      cache: 'npm',
    },
    yarn: {
      setup: `- uses: actions/setup-node@v3
      with:
        node-version: '20'
        cache: 'yarn'`,
      cache: 'yarn',
    },
    pnpm: {
      setup: `- uses: pnpm/action-setup@v2
      with:
        version: 8
    - uses: actions/setup-node@v3
      with:
        node-version: '20'
        cache: 'pnpm'`,
      cache: 'pnpm',
    },
    bun: {
      setup: `- uses: oven-sh/setup-bun@v1
      with:
        bun-version: latest`,
      cache: 'bun',
    },
    pip: {
      setup: `- uses: actions/setup-python@v4
      with:
        python-version: '3.11'
        cache: 'pip'`,
      cache: 'pip',
    },
    poetry: {
      setup: `- uses: actions/setup-python@v4
      with:
        python-version: '3.11'
        cache: 'poetry'`,
      cache: 'poetry',
    },
    pipenv: {
      setup: `- uses: actions/setup-python@v4
      with:
        python-version: '3.11'
        cache: 'pipenv'`,
      cache: 'pipenv',
    },
    go: {
      setup: `- uses: actions/setup-go@v4
      with:
        go-version: '1.21'
        cache: true`,
      cache: 'go',
    },
    cargo: {
      setup: `- uses: actions-rs/toolchain@v1
      with:
        toolchain: stable
        profile: minimal
    - uses: Swatinem/rust-cache@v2`,
      cache: 'cargo',
    },
    bundler: {
      setup: `- uses: ruby/setup-ruby@v1
      with:
        ruby-version: '3.2'
        bundler-cache: true`,
      cache: 'bundler',
    },
    composer: {
      setup: `- uses: shivammathur/setup-php@v2
      with:
        php-version: '8.2'
        tools: composer`,
      cache: 'composer',
    },
    maven: {
      setup: `- uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'
        cache: 'maven'`,
      cache: 'maven',
    },
    gradle: {
      setup: `- uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'
        cache: 'gradle'`,
      cache: 'gradle',
    },
    dotnet: {
      setup: `- uses: actions/setup-dotnet@v3
      with:
        dotnet-version: '7.0.x'`,
      cache: 'dotnet',
    },
    // conda, gem, nuget don't have standard cache setups or use different mechanisms
  };

  return setups[pm];
}
