#!/usr/bin/env bash

# Test mise prepare (mise prep) command

# Test --list with no providers configured
assert_contains "mise prepare --list" "No prepare providers found"

# Create a package-lock.json (npm provider needs to be configured to detect it)
cat >package-lock.json <<'EOF'
{
  "name": "test-project",
  "lockfileVersion": 3,
  "packages": {}
}
EOF

# Still no providers without explicit config
assert_contains "mise prepare --list" "No prepare providers found"

# Configure npm provider explicitly
cat >mise.toml <<'EOF'
[prepare.npm]
EOF

# Now npm provider should be detected
assert_contains "mise prepare --list" "npm"
assert_contains "mise prepare --list" "package-lock.json"
assert_contains "mise prepare --list" "node_modules"

# Test --dry-run shows what would run
assert_contains "mise prepare --dry-run" "npm"

# Test alias works
assert_contains "mise prep --list" "npm"

# Test with custom prepare provider (no .rules. prefix)
cat >mise.toml <<'EOF'
[prepare.npm]

[prepare.codegen]
sources = ["schema.graphql"]
outputs = ["generated/"]
run = "echo codegen"
EOF

# Create source file
touch schema.graphql

assert_contains "mise prepare --list" "codegen"
assert_contains "mise prepare --list" "schema.graphql"

# Test --only flag
assert_contains "mise prepare --dry-run --only codegen" "codegen"
assert_not_contains "mise prepare --dry-run --only codegen" "npm"

# Test --skip flag
assert_not_contains "mise prepare --dry-run --skip npm" "npm install"

# Test disable in config
cat >mise.toml <<'EOF'
[prepare]
disable = ["npm"]

[prepare.npm]

[prepare.codegen]
sources = ["schema.graphql"]
outputs = ["generated/"]
run = "echo codegen"
EOF

assert_not_contains "mise prepare --list" "npm"
assert_contains "mise prepare --list" "codegen"

# Test per-provider auto flag
cat >mise.toml <<'EOF'
[prepare.npm]
auto = true

[prepare.codegen]
auto = false
sources = ["schema.graphql"]
outputs = ["generated/"]
run = "echo codegen"
EOF

# Both should show in list
assert_contains "mise prepare --list" "npm"
assert_contains "mise prepare --list" "codegen"

# Test yarn provider
rm -f package-lock.json
cat >yarn.lock <<'EOF'
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
EOF

cat >mise.toml <<'EOF'
[prepare.yarn]
EOF

assert_contains "mise prepare --list" "yarn"
assert_contains "mise prepare --list" "yarn.lock"
assert_contains "mise prepare --dry-run" "yarn"

# Test pnpm provider
rm -f yarn.lock
cat >pnpm-lock.yaml <<'EOF'
lockfileVersion: '9.0'
EOF

cat >mise.toml <<'EOF'
[prepare.pnpm]
EOF

assert_contains "mise prepare --list" "pnpm"
assert_contains "mise prepare --list" "pnpm-lock.yaml"
assert_contains "mise prepare --dry-run" "pnpm"

# Test bun provider (binary lockfile)
rm -f pnpm-lock.yaml
touch bun.lockb

cat >mise.toml <<'EOF'
[prepare.bun]
EOF

assert_contains "mise prepare --list" "bun"
assert_contains "mise prepare --list" "bun.lockb"
assert_contains "mise prepare --dry-run" "bun"

# Test bun provider (text lockfile)
rm -f bun.lockb
cat >bun.lock <<'EOF'
# bun lockfile
EOF

assert_contains "mise prepare --list" "bun"
assert_contains "mise prepare --list" "bun.lock"

# Test go provider
rm -f bun.lock
cat >go.sum <<'EOF'
github.com/foo/bar v1.0.0 h1:abc
EOF
cat >go.mod <<'EOF'
module test
go 1.21
EOF
# Ensure go.sum (output) is older than go.mod (source) to trigger staleness
touch -t 202301010000 go.sum

cat >mise.toml <<'EOF'
[prepare.go]
EOF

assert_contains "mise prepare --list" "go"
assert_contains "mise prepare --list" "go.sum"
assert_contains "mise prepare --dry-run" "go"

# Test pip provider
rm -f go.sum go.mod
cat >requirements.txt <<'EOF'
requests==2.31.0
EOF

cat >mise.toml <<'EOF'
[prepare.pip]
EOF

assert_contains "mise prepare --list" "pip"
assert_contains "mise prepare --list" "requirements.txt"
assert_contains "mise prepare --dry-run" "pip"

# Test poetry provider
rm -f requirements.txt
cat >poetry.lock <<'EOF'
[[package]]
name = "requests"
version = "2.31.0"
EOF
cat >pyproject.toml <<'EOF'
[tool.poetry]
name = "test"
EOF

cat >mise.toml <<'EOF'
[prepare.poetry]
EOF

assert_contains "mise prepare --list" "poetry"
assert_contains "mise prepare --list" "poetry.lock"
assert_contains "mise prepare --dry-run" "poetry"

# Test uv provider
rm -f poetry.lock
cat >uv.lock <<'EOF'
version = 1
EOF

cat >mise.toml <<'EOF'
[prepare.uv]
EOF

assert_contains "mise prepare --list" "uv"
assert_contains "mise prepare --list" "uv.lock"
assert_contains "mise prepare --dry-run" "uv"

# Test bundler provider
rm -f uv.lock pyproject.toml
cat >Gemfile.lock <<'EOF'
GEM
  specs:
EOF
cat >Gemfile <<'EOF'
source 'https://rubygems.org'
EOF

cat >mise.toml <<'EOF'
[prepare.bundler]
EOF

assert_contains "mise prepare --list" "bundler"
assert_contains "mise prepare --list" "Gemfile.lock"
assert_contains "mise prepare --dry-run" "bundler"

# Test composer provider
rm -f Gemfile.lock Gemfile
cat >composer.lock <<'EOF'
{
    "_readme": ["This file locks the dependencies"]
}
EOF
cat >composer.json <<'EOF'
{}
EOF

cat >mise.toml <<'EOF'
[prepare.composer]
EOF

assert_contains "mise prepare --list" "composer"
assert_contains "mise prepare --list" "composer.lock"
assert_contains "mise prepare --dry-run" "composer"

# Test freshness detection with output directories
rm -f composer.lock composer.json

cat >mise.toml <<'EOF'
[prepare.custom_venv]
sources = ["requirements.txt"]
outputs = [".venv"]
run = "echo PREPARE_RAN"
EOF

cat >requirements.txt <<'EOF'
requests==2.31.0
EOF

# Test 1: No output directory exists → should be stale
rm -rf .venv
assert_contains "mise prepare --dry-run" "custom_venv"

# Test 2: Output exists and is newer than sources → should be fresh
# Use explicit timestamps: source=2023, output=2024
mkdir -p .venv
touch -t 202301010000 requirements.txt # Jan 1, 2023
touch -t 202401010000 .venv/pyvenv.cfg # Jan 1, 2024 (newer)
touch -t 202401010000 .venv            # Set directory mtime too (mkdir sets it to NOW)
# In dry-run mode, fresh providers produce no output, so check provider name is absent
assert_not_contains "mise prepare --dry-run" "custom_venv"

# Test 3: Sources updated after output → should be stale
# source=2025 (newer than output=2024)
touch -t 202501010000 requirements.txt # Jan 1, 2025
assert_contains "mise prepare --dry-run" "custom_venv"

# Clean up
rm -rf .venv requirements.txt
rm -f composer.lock composer.json package.json mise.toml schema.graphql

# Test touch_outputs: after a successful run, output mtime should be updated
cat >mise.toml <<'EOF'
[prepare.touch_test]
sources = ["src_file.txt"]
outputs = ["out_dir"]
run = "echo TOUCH_RAN"
EOF

touch src_file.txt
mkdir -p out_dir
# Make source newer than output so provider is stale
touch -t 202501010000 out_dir
touch -t 202601010000 src_file.txt

# First run: should execute (stale)
assert_contains "mise prepare --only touch_test" "Prepared: touch_test"

# Second run: outputs should now be fresh (mtime was touched after first run)
assert_contains "mise prepare --only touch_test" "up to date"

# Test touch_outputs = false: output mtime should NOT be updated
cat >mise.toml <<'EOF'
[prepare.notouch_test]
sources = ["src_file.txt"]
outputs = ["out_dir"]
run = "echo NOTOUCH_RAN"
touch_outputs = false
EOF

# Make source newer than output so provider is stale
touch -t 202501010000 out_dir
touch -t 202601010000 src_file.txt

# First run: should execute (stale)
assert_contains "mise prepare --only notouch_test" "Prepared: notouch_test"

# Second run: should still be stale since we didn't touch outputs
# (the "echo" command doesn't modify out_dir, and we disabled mtime update)
assert_contains "mise prepare --only notouch_test" "Prepared: notouch_test"

rm -rf src_file.txt out_dir

# Test that prepare providers are NOT inherited from parent config files
# (monorepo scenario: root defines [prepare.pnpm], subdirectory should not inherit it)
mkdir -p subproject

cat >mise.toml <<'EOF'
[prepare.custom_root]
sources = ["root_input.txt"]
outputs = ["root_output.txt"]
run = "echo root_ran"
EOF
touch root_input.txt

cat >subproject/mise.toml <<'EOF'
[prepare.custom_sub]
sources = ["sub_input.txt"]
outputs = ["sub_output.txt"]
run = "echo sub_ran"
EOF
touch subproject/sub_input.txt

# From root: only root provider should be visible
assert_contains "mise prepare --list" "custom_root"
assert_not_contains "mise prepare --list" "custom_sub"

# From subdirectory: only subdirectory provider should be visible, not inherited root
(
	cd subproject
	assert_contains "mise prepare --list" "custom_sub"
	assert_not_contains "mise prepare --list" "custom_root"
)

# Clean up previous test
rm -rf subproject root_input.txt root_output.txt mise.toml

# Test that monorepo subdirectory prepare steps run when using //subdir:task from root
export MISE_EXPERIMENTAL=1
mkdir -p subapp

cat >mise.toml <<'EOF'
experimental_monorepo_root = true

[monorepo]
config_roots = ["subapp"]
EOF

cat >subapp/mise.toml <<'EOF'
[prepare.subapp_deps]
auto = true
sources = ["input.txt"]
outputs = ["deps_marker"]
run = "touch deps_marker"

[tasks.check]
run = "test -f deps_marker && echo SUBAPP_PREPARE_OK || echo SUBAPP_PREPARE_MISSING"
EOF

touch subapp/input.txt

# Run the task from root using monorepo syntax
assert_contains "mise run //subapp:check" "SUBAPP_PREPARE_OK"

# Clean up
rm -rf subapp mise.toml
