version: '3'

# 1Password Secret Management Tasks
# Minimal set of tasks for secure secret management

tasks:
  default:
    desc: 'Show available 1Password tasks'
    aliases: [help]
    silent: true
    cmds:
      - |
        # Color codes
        GREEN='\033[0;32m'
        YELLOW='\033[0;33m'
        RED='\033[0;31m'
        BOLD='\033[1m'
        NC='\033[0m'

        echo -e "${BOLD}1Password Secret Management${NC}"
        echo ""
        echo -e "${YELLOW}ℹ️  Secrets loaded automatically by tasks that need them${NC}"
        echo ""
        echo "Command                            Alias     Description                              Examples"
        echo "───────────────────────────────────────────────────────────────────────────────────────────────────"
        echo -e "  ${GREEN}source \$(task op:export)${NC}         ${YELLOW}op:x${NC}      Load secrets into shell (interactive)"
        echo -e "  ${GREEN}source \$(task op:export:force)${NC}   ${YELLOW}op:xf${NC}     Force reload all secrets"
        echo -e "  ${GREEN}task op:verify${NC}                   ${YELLOW}op:ve${NC}     Verify required secrets are loaded"
        echo -e "  ${GREEN}task op:validate${NC}                 ${YELLOW}op:v${NC}      Validate all exported secrets"
        echo -e "  ${GREEN}task op:cleanup${NC}                  ${YELLOW}op:c${NC}      Clean up temp secret files"
        echo -e "  ${GREEN}task op:test${NC}                     ${YELLOW}op:t${NC}      Test GitHub token permissions"

  export:
    desc: 'Export secrets to temp file - Use: source $(task op:export)'
    aliases: [x]
    silent: true
    cmds:
      - |
        set -euo pipefail

        # Get unique temp directory for this repo
        REPO_PATH=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
        REPO_HASH=$(echo "$REPO_PATH" | shasum -a 256 | cut -d' ' -f1 | cut -c1-12)
        TEMP_DIR="/tmp/op-secrets-$REPO_HASH"
        TEMP_ENV="$TEMP_DIR/.env"

        # Check if already exported (idempotent)
        if [ -f "$TEMP_ENV" ]; then
          # Verify file is recent (< 1 hour old)
          if [ "$(uname)" = "Darwin" ]; then
            FILE_AGE=$(stat -f %m "$TEMP_ENV" 2>/dev/null || echo 0)
          else
            FILE_AGE=$(stat -c %Y "$TEMP_ENV" 2>/dev/null || echo 0)
          fi
          CURRENT_TIME=$(date +%s)
          AGE_SECONDS=$((CURRENT_TIME - FILE_AGE))

          if [ $AGE_SECONDS -lt 3600 ]; then
            # File is fresh, reuse it
            echo "$TEMP_ENV"
            exit 0
          fi
        fi

        # Check if 1Password CLI is available
        OP_AVAILABLE=false
        if command -v op &> /dev/null; then
          OP_AVAILABLE=true
        fi

        # Create secure temp directory
        mkdir -p "$TEMP_DIR"
        chmod 700 "$TEMP_DIR"

        # Create temp file with secure permissions
        touch "$TEMP_ENV"
        chmod 600 "$TEMP_ENV"

        # Clear file
        : > "$TEMP_ENV"

        # Track unresolved op:// references
        UNRESOLVED_REFS=""

        # Function to process an env file
        process_env_file() {
          local env_file="$1"
          [ -f "$env_file" ] || return 0

          while IFS='=' read -r key value || [ -n "$key" ]; do
            # Skip comments and empty lines
            [[ "$key" =~ ^#.*$ ]] || [[ -z "$key" ]] && continue

            # Remove any existing entry for this key (allows override)
            if [ -f "$TEMP_ENV" ]; then
              grep -v "^export $key=" "$TEMP_ENV" > "$TEMP_ENV.tmp" 2>/dev/null || true
              mv "$TEMP_ENV.tmp" "$TEMP_ENV" 2>/dev/null || true
            fi

            # Resolve op:// references via 1Password CLI
            if [[ "$value" =~ ^op:// ]]; then
              if [ "$OP_AVAILABLE" = true ]; then
                resolved=$(op read "$value" 2>/dev/null) || true
                if [ -n "$resolved" ]; then
                  echo "export $key='$resolved'" >> "$TEMP_ENV"
                else
                  # op available but resolution failed - track for error
                  UNRESOLVED_REFS="$UNRESOLVED_REFS\n  $key=$value"
                fi
              else
                # op not available - track unresolved reference
                UNRESOLVED_REFS="$UNRESOLVED_REFS\n  $key=$value"
              fi
            else
              # Plain text value, export as-is
              echo "export $key='$value'" >> "$TEMP_ENV"
            fi
          done < "$env_file"
        }

        # Process .env first, then .env.local (local overrides base)
        process_env_file ".env"
        process_env_file ".env.local"

        # Only error if there are unresolved op:// references
        if [ -n "$UNRESOLVED_REFS" ]; then
          if [ "$OP_AVAILABLE" = true ]; then
            echo "❌ Failed to resolve 1Password references:" >&2
          else
            echo "❌ 1Password CLI (op) not installed but op:// references found:" >&2
          fi
          echo -e "$UNRESOLVED_REFS" >&2
          echo "" >&2
          if [ "$OP_AVAILABLE" = false ]; then
            echo "Install 1Password CLI: https://developer.1password.com/docs/cli/get-started/" >&2
            echo "Or replace op:// references with plain values in .env.local" >&2
          else
            echo "Ensure 1Password is unlocked and references are valid" >&2
          fi
          exit 1
        fi

        # Output temp file path (for sourcing)
        echo "$TEMP_ENV"

        # Security: Auto-delete secrets file after 10 seconds
        # This runs in background so the task returns immediately
        # The file will be deleted even if the parent shell exits
        (sleep 10 && rm -f "$TEMP_ENV" 2>/dev/null) &
        disown 2>/dev/null || true

  export:force:
    desc: 'Force re-export secrets - Use: source $(task op:export:force)'
    aliases: [xf]
    silent: true
    cmds:
      - |
        # Delete existing temp file to force re-export
        REPO_PATH=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
        REPO_HASH=$(echo "$REPO_PATH" | shasum -a 256 | cut -d' ' -f1 | cut -c1-12)
        rm -f "/tmp/op-secrets-$REPO_HASH/.env"

        # Run normal export
        task op:export

  verify:
    desc: 'Verify required secrets are loaded (at least one AI key + required tokens)'
    aliases: [ve]
    silent: true
    cmds:
      - |
        set -euo pipefail

        # Color codes
        RED='\033[0;31m'
        GREEN='\033[0;32m'
        NC='\033[0m'

        failed=false

        # Check at least one AI API key is set and resolved
        ai_key_found=false
        for key in ANTHROPIC_API_KEY PERPLEXITY_API_KEY OPENAI_API_KEY; do
          value="${!key:-}"
          if [ -n "$value" ] && [[ ! "$value" =~ ^op:// ]]; then
            ai_key_found=true
            break
          fi
        done

        if [ "$ai_key_found" = false ]; then
          echo -e "${RED}❌ No AI API key found${NC}" >&2
          echo "   Required: At least one of ANTHROPIC_API_KEY, PERPLEXITY_API_KEY, or OPENAI_API_KEY" >&2
          echo "   Run: source \$(task op:export)" >&2
          failed=true
        fi

        # Check required tokens
        for key in GITHUB_TOKEN SEMGREP_APP_TOKEN; do
          value="${!key:-}"

          if [ -z "$value" ]; then
            echo -e "${RED}❌ $key: NOT SET${NC}" >&2
            failed=true
            continue
          fi

          if [[ "$value" =~ ^op:// ]]; then
            echo -e "${RED}❌ $key: NOT RESOLVED${NC}" >&2
            echo "   Run: source \$(task op:export)" >&2
            failed=true
          fi
        done

        if [ "$failed" = true ]; then
          exit 1
        fi

  cleanup:
    desc: 'Clean up exported secrets temp file'
    aliases: [c]
    silent: true
    cmds:
      - |
        REPO_PATH=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
        REPO_HASH=$(echo "$REPO_PATH" | shasum -a 256 | cut -d' ' -f1 | cut -c1-12)
        TEMP_DIR="/tmp/op-secrets-$REPO_HASH"

        if [ -d "$TEMP_DIR" ]; then
          rm -rf "$TEMP_DIR"
          echo "✓ Cleaned up secrets temp file"
        else
          echo "No temp files to clean"
        fi

  validate:
    desc: 'Validate exported environment variables (idempotent, silent if valid)'
    aliases: [v]
    silent: true
    cmds:
      - |
        # Read expected variables from .env and .env.local (combined, deduplicated)
        expected_vars=$(cat .env .env.local 2>/dev/null | grep -E "^[A-Z_]+=" | cut -d= -f1 | sort -u)

        if [ -z "$expected_vars" ]; then
          echo "❌ No .env file found or no variables defined" >&2
          exit 1
        fi

        # Track failures
        failed=false

        # Check each variable
        for var in $expected_vars; do
          value="${!var}"

          # Check if variable is set
          if [ -z "$value" ]; then
            echo "❌ $var: NOT SET" >&2
            failed=true
            continue
          fi

          # Check if still in op:// format (not exported)
          if [[ "$value" =~ ^op:// ]]; then
            echo "❌ $var: NOT RESOLVED (run: source \$(task op:export))" >&2
            failed=true
            continue
          fi
        done

        # Only exit with error if validation failed
        if [ "$failed" = true ]; then
          exit 1
        fi

  test:
    desc: 'Test GitHub token permissions using .env mapping'
    aliases: [t]
    silent: true
    cmds:
      - |
        echo "GitHub Token Permission Test"
        echo "============================"
        echo ""

        # Check gh CLI is installed
        if ! command -v gh &> /dev/null; then
          echo "❌ GitHub CLI not found. Install: brew install gh" >&2
          exit 1
        fi

        # Get GITHUB_TOKEN reference from .env
        github_token_ref=$(grep "^GITHUB_TOKEN=" .env | cut -d= -f2)
        if [ -z "$github_token_ref" ]; then
          echo "❌ GITHUB_TOKEN not found in .env" >&2
          exit 1
        fi

        # Resolve from 1Password using .env mapping
        if [[ "$github_token_ref" =~ ^op:// ]]; then
          export GITHUB_TOKEN=$(op read "$github_token_ref" 2>/dev/null)
          if [ $? -ne 0 ]; then
            echo "❌ Could not resolve GITHUB_TOKEN from 1Password" >&2
            echo "Hint: Ensure 1Password is unlocked" >&2
            exit 1
          fi
        else
          export GITHUB_TOKEN="$github_token_ref"
        fi

        export GH_TOKEN="$GITHUB_TOKEN"
        echo "Token loaded: ${GITHUB_TOKEN:0:10}..."
        echo ""

        # Get repository from git remote
        REPO=$(git config --get remote.origin.url | sed -E "s|^.*[:/]([^/]+/[^/]+)(.git)?$|\1|" | sed "s/\.git$//")
        echo "Testing: $REPO"
        echo ""

        # Test read permissions
        echo "Testing read permissions..."
        gh repo view "$REPO" --json name > /dev/null 2>&1 && echo "  ✅ Read repository" || { echo "  ❌ Read repository" >&2; exit 1; }
        gh issue list --repo "$REPO" --limit 1 > /dev/null 2>&1 && echo "  ✅ Read issues" || echo "  ⚠️  Read issues"
        gh pr list --repo "$REPO" --limit 1 > /dev/null 2>&1 && echo "  ✅ Read PRs" || echo "  ⚠️  Read PRs"
        gh run list --repo "$REPO" --limit 1 > /dev/null 2>&1 && echo "  ✅ Read workflows" || echo "  ⚠️  Read workflows"
        echo ""

        # Test write restrictions (should fail)
        echo "Testing write restrictions..."
        ! gh secret list --repo "$REPO" > /dev/null 2>&1 && echo "  ✅ Cannot read secrets" || { echo "  ❌ SECURITY: Can read secrets!" >&2; exit 1; }

        TEMP_FILE="temp-test-$(date +%s).md"
        if echo "test" | gh api "repos/$REPO/contents/$TEMP_FILE" --method PUT --field message="test" --field content="dGVzdA==" --silent > /dev/null 2>&1; then
          echo "  ❌ SECURITY: Can write files!" >&2
          gh api "repos/$REPO/contents/$TEMP_FILE" --method DELETE --field message="cleanup" --field sha="$(gh api "repos/$REPO/contents/$TEMP_FILE" --jq .sha)" --silent 2>&1 || true
          exit 1
        else
          echo "  ✅ Cannot write files"
        fi

        WORKFLOW=$(gh api "repos/$REPO/actions/workflows" --jq ".workflows[0].path" 2>/dev/null)
        if [ -n "$WORKFLOW" ]; then
          ! gh workflow run "$WORKFLOW" --repo "$REPO" > /dev/null 2>&1 && echo "  ✅ Cannot trigger workflows" || { echo "  ❌ SECURITY: Can trigger workflows!" >&2; exit 1; }
        else
          echo "  ⚠️  No workflows to test"
        fi

        echo ""
        echo "✅ All tests passed - token follows least privilege"
