#!/usr/bin/env bash
# Tests for deploy-skill.sh config injection functions
# Run: bash bin/tests/test-deploy-skill.sh

set -uo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PASSED=0
FAILED=0

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color

# Extract inject_nix_config function
eval "$(sed -n '/^inject_nix_config()/,/^}/p' "$SCRIPT_DIR/../deploy-skill.sh")"

# Extract inject_home_file function
eval "$(sed -n '/^inject_home_file()/,/^}/p' "$SCRIPT_DIR/../deploy-skill.sh")"

# Test helpers
assert_contains() {
    local description="$1"
    local pattern="$2"
    local file="$3"

    if grep -q "$pattern" "$file" 2>/dev/null; then
        echo -e "${GREEN}PASS${NC}: $description"
        ((PASSED++))
    else
        echo -e "${RED}FAIL${NC}: $description"
        echo "  Pattern not found: '$pattern'"
        echo "  File contents:"
        cat "$file" | sed 's/^/    /'
        ((FAILED++))
    fi
}

assert_not_contains() {
    local description="$1"
    local pattern="$2"
    local file="$3"

    if ! grep -q "$pattern" "$file" 2>/dev/null; then
        echo -e "${GREEN}PASS${NC}: $description"
        ((PASSED++))
    else
        echo -e "${RED}FAIL${NC}: $description"
        echo "  Pattern found but should not be: '$pattern'"
        ((FAILED++))
    fi
}

assert_count() {
    local description="$1"
    local expected="$2"
    local pattern="$3"
    local file="$4"

    local actual
    actual=$(grep -c "$pattern" "$file" 2>/dev/null || echo "0")

    if [[ "$actual" == "$expected" ]]; then
        echo -e "${GREEN}PASS${NC}: $description"
        ((PASSED++))
    else
        echo -e "${RED}FAIL${NC}: $description"
        echo "  Expected count: $expected, Actual: $actual"
        echo "  Pattern: '$pattern'"
        ((FAILED++))
    fi
}

assert_last_line() {
    local description="$1"
    local expected="$2"
    local file="$3"

    local actual
    actual=$(tail -n 1 "$file")

    if [[ "$actual" == "$expected" ]]; then
        echo -e "${GREEN}PASS${NC}: $description"
        ((PASSED++))
    else
        echo -e "${RED}FAIL${NC}: $description"
        echo "  Expected last line: '$expected'"
        echo "  Actual last line: '$actual'"
        ((FAILED++))
    fi
}

assert_output_contains() {
    local description="$1"
    local pattern="$2"
    local output="$3"

    if echo "$output" | grep -q "$pattern"; then
        echo -e "${GREEN}PASS${NC}: $description"
        ((PASSED++))
    else
        echo -e "${RED}FAIL${NC}: $description"
        echo "  Pattern not found: '$pattern'"
        echo "  Output was: $output"
        ((FAILED++))
    fi
}

# Setup: create a minimal Nix file
setup_nix_file() {
    local content="$1"
    local test_file
    test_file=$(mktemp)
    echo -e "$content" > "$test_file"
    echo "$test_file"
}

cleanup() {
    rm -f "$1"
}

echo "=== Deploy Skill Config Injection Tests ==="
echo ""

# --- Basic Injection ---
echo "## Basic Injection"

# Test: Config injected before closing brace
test_file=$(setup_nix_file "{ config, pkgs, ... }:
{
  home.packages = [ pkgs.git ];
}")

inject_nix_config "$test_file" "  # Added config
  home.file.\"test\" = { source = ./test; };" "home.file.\"test\"" > /dev/null

assert_contains "Config block injected" "home.file.\"test\"" "$test_file"
assert_last_line "Closing brace preserved at end" "}" "$test_file"
assert_contains "Original content preserved" "home.packages" "$test_file"

cleanup "$test_file"

# --- Idempotency ---
echo ""
echo "## Idempotency"

# Test: Running twice doesn't duplicate
test_file=$(setup_nix_file "{ config, pkgs, ... }:
{
  home.packages = [ ];
}")

inject_nix_config "$test_file" "  home.file.\"skill-a\" = { source = ./a; };" "skill-a" > /dev/null
inject_nix_config "$test_file" "  home.file.\"skill-a\" = { source = ./a; };" "skill-a" > /dev/null

assert_count "Config not duplicated" "1" "skill-a" "$test_file"

cleanup "$test_file"

# Test: Different configs can be added
test_file=$(setup_nix_file "{ config, pkgs, ... }:
{
}")

inject_nix_config "$test_file" "  home.file.\"skill-b\" = {};" "skill-b" > /dev/null
inject_nix_config "$test_file" "  home.file.\"skill-c\" = {};" "skill-c" > /dev/null

assert_contains "First config present" "skill-b" "$test_file"
assert_contains "Second config present" "skill-c" "$test_file"
assert_count "Each config appears once" "1" "skill-b" "$test_file"
assert_count "Each config appears once (c)" "1" "skill-c" "$test_file"

cleanup "$test_file"

# --- File Not Found ---
echo ""
echo "## File Not Found Handling"

output=$(inject_nix_config "/nonexistent/path/file.nix" "config" "marker" 2>&1)
assert_output_contains "Skips missing file gracefully" "skipping" "$output"

# --- Brace Structure ---
echo ""
echo "## Brace Structure Preservation"

# Test: Complex nested structure
test_file=$(setup_nix_file "{ config, pkgs, ... }:
{
  programs.git = {
    enable = true;
    userName = \"test\";
  };

  home.packages = with pkgs; [
    ripgrep
    fd
  ];
}")

inject_nix_config "$test_file" "
  # New skill
  home.file.\".skill\" = {
    source = ./skill;
    recursive = true;
  };" ".skill" > /dev/null

assert_last_line "Closing brace still at end after complex inject" "}" "$test_file"
assert_contains "Nested braces preserved" "programs.git" "$test_file"
assert_contains "New config added" ".skill" "$test_file"

cleanup "$test_file"

# --- inject_home_file Wrapper ---
echo ""
echo "## inject_home_file Wrapper"

test_file=$(setup_nix_file "{ config, pkgs, ... }:
{
}")

inject_home_file "$test_file" \
    ".claude/skills/my-skill" \
    "../claude/skills/my-skill" \
    "recursive = true;" \
    "my-skill" > /dev/null

assert_contains "Home path in config" ".claude/skills/my-skill" "$test_file"
assert_contains "Source path in config" "../claude/skills/my-skill" "$test_file"
assert_contains "Extra props included" "recursive = true" "$test_file"
assert_contains "Comment included" "# Skill: my-skill" "$test_file"

cleanup "$test_file"

# Test: inject_home_file idempotency
test_file=$(setup_nix_file "{ config, pkgs, ... }:
{
}")

inject_home_file "$test_file" ".test/path" "./source" "" "test" > /dev/null
inject_home_file "$test_file" ".test/path" "./source" "" "test" > /dev/null

assert_count "inject_home_file idempotent" "1" ".test/path" "$test_file"

cleanup "$test_file"

# --- Already Present Detection ---
echo ""
echo "## Already Present Detection"

test_file=$(setup_nix_file "{ config, pkgs, ... }:
{
  # Existing skill
  home.file.\".claude/skills/existing\" = {
    source = ./existing;
  };
}")

output=$(inject_nix_config "$test_file" "new config" ".claude/skills/existing" 2>&1)
assert_output_contains "Detects existing config" "already present" "$output"

cleanup "$test_file"

# --- Empty Extra Props ---
echo ""
echo "## Edge Cases"

# Test: Empty extra props
test_file=$(setup_nix_file "{ config, pkgs, ... }:
{
}")

inject_home_file "$test_file" ".simple/path" "./src" "" "simple" > /dev/null

assert_contains "Works with empty extra props" ".simple/path" "$test_file"

cleanup "$test_file"

# Test: Single line file (edge case - script assumes multi-line)
test_file=$(setup_nix_file "{}")

inject_nix_config "$test_file" "  config = true;" "config" > /dev/null

assert_contains "Works with minimal file" "config = true" "$test_file"
# Note: Single-line "{}" becomes last line since head -n -1 returns empty
# This is acceptable - real Nix files are always multi-line
assert_last_line "Original content preserved as last line" "{}" "$test_file"

cleanup "$test_file"

# --- Summary ---
echo ""
echo "=== Summary ==="
echo -e "Passed: ${GREEN}$PASSED${NC}"
echo -e "Failed: ${RED}$FAILED${NC}"

if [[ $FAILED -gt 0 ]]; then
    exit 1
fi
