#!/usr/bin/env bash
#
# php-security-lint - PHP/Laravel Security Linter
#
# A wrapper script to run the security linter via Docker.
# Install this script to /usr/local/bin/ for system-wide access.
#
# Usage:
#   php-security-lint [options] <path>
#   php-security-lint app/
#   php-security-lint -s high -f json .
#
# Installation:
#   Copy this script to /usr/local/bin/ after building the Docker image:
#   sudo cp php-security-lint /usr/local/bin/
#   sudo chmod +x /usr/local/bin/php-security-lint
#

set -e

# Global variable to track container ID for cleanup
CONTAINER_ID=""

# Cleanup function to stop container on interrupt
cleanup() {
    if [[ -n "$CONTAINER_ID" ]]; then
        docker stop "$CONTAINER_ID" >/dev/null 2>&1 || true
    fi
    echo ""
    exit 130
}

# Trap SIGINT (Ctrl+C) and SIGTERM
trap cleanup SIGINT SIGTERM

# Configuration
DOCKER_IMAGE="${PHP_SECURITY_LINT_IMAGE:-php-security-linter:latest}"
CONTAINER_TARGET="/target"

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# Show usage
show_usage() {
    cat << 'EOF'
PHP/Laravel Security Linter

Usage:
  php-security-lint [options] <path>

Examples:
  php-security-lint .                    # Scan current directory
  php-security-lint app/                 # Scan app directory
  php-security-lint -s high .            # Show only high+ severity
  php-security-lint -f json -o report.json .  # Output as JSON

Options:
  -f, --format <format>     Output format: text, json, html, sarif, markdown
  -s, --severity <level>    Minimum severity: low, medium, high, critical
  -o, --output <file>       Write output to file
  -l, --lang <lang>         Language: ja, en
  -c, --context [N]         Show N lines of code context (default: 3)
  -e, --exclude <pattern>   Exclude pattern (can be used multiple times)
  -i, --include <pattern>   Include pattern (overrides exclude)
  -d, --recursive-depth <n> Recursive analysis depth (default: 10)
  --include-vendor          Include vendor directory
  --include-tests           Include tests directory
  --no-colors               Disable colored output
  -q, --quiet               Suppress progress output
  --verbose                 Show detailed information
  -h, --help                Show this help message
  --version                 Show version

Environment Variables:
  PHP_SECURITY_LINT_IMAGE   Docker image to use (default: php-security-linter:latest)

EOF
}

# Check if Docker is available
check_docker() {
    if ! command -v docker &> /dev/null; then
        echo -e "${RED}Error: Docker is not installed or not in PATH${NC}" >&2
        echo "Please install Docker: https://docs.docker.com/get-docker/" >&2
        exit 1
    fi

    if ! docker info &> /dev/null; then
        echo -e "${RED}Error: Docker daemon is not running${NC}" >&2
        echo "Please start the Docker daemon" >&2
        exit 1
    fi
}

# Check if image exists, build if necessary
check_image() {
    if ! docker image inspect "$DOCKER_IMAGE" &> /dev/null; then
        echo -e "${YELLOW}Docker image '$DOCKER_IMAGE' not found.${NC}"

        # Check if we're in the project directory with Dockerfile
        if [[ -f "Dockerfile" ]] && grep -q "security-linter" "Dockerfile" 2>/dev/null; then
            echo "Building image from local Dockerfile..."
            docker build -t "$DOCKER_IMAGE" .
        else
            echo -e "${RED}Error: Docker image '$DOCKER_IMAGE' not found.${NC}" >&2
            echo "" >&2
            echo "To build the image, clone the repository and run:" >&2
            echo "  git clone https://opensource.rogarithm.net/rogarithm/php-security-linter.git" >&2
            echo "  cd php-security-linter" >&2
            echo "  docker build -t php-security-linter:latest ." >&2
            exit 1
        fi
    fi
}

# Parse arguments and find target path
parse_args() {
    local args=()
    local target_path=""
    local output_file=""
    local has_output=false

    while [[ $# -gt 0 ]]; do
        case "$1" in
            -h|--help)
                show_usage
                exit 0
                ;;
            --version)
                docker run --rm "$DOCKER_IMAGE" --version
                exit 0
                ;;
            -o|--output)
                has_output=true
                output_file="$2"
                args+=("$1" "$CONTAINER_TARGET/$(basename "$2")")
                shift 2
                ;;
            -f|--format|-s|--severity|-l|--lang|-e|--exclude|-i|--include|-d|--recursive-depth)
                args+=("$1" "$2")
                shift 2
                ;;
            -c|--context)
                # Handle optional numeric argument (must be a number, not a path)
                if [[ -n "$2" ]] && [[ "$2" =~ ^[0-9]+$ ]] && [[ ! "$2" =~ / ]]; then
                    args+=("$1" "$2")
                    shift 2
                else
                    args+=("$1")
                    shift
                fi
                ;;
            --include-vendor|--include-tests|--no-colors|--no-default-excludes|--show-excluded|-q|--quiet|--verbose)
                args+=("$1")
                shift
                ;;
            -*)
                args+=("$1")
                shift
                ;;
            *)
                # This is the target path
                if [[ -z "$target_path" ]]; then
                    target_path="$1"
                else
                    args+=("$1")
                fi
                shift
                ;;
        esac
    done

    # Default to current directory if no path specified
    if [[ -z "$target_path" ]]; then
        target_path="."
    fi

    # Convert to absolute path
    if [[ "$target_path" == "." ]]; then
        TARGET_HOST_PATH="$(pwd)"
        TARGET_CONTAINER_PATH="$CONTAINER_TARGET"
    elif [[ "$target_path" == /* ]]; then
        TARGET_HOST_PATH="$target_path"
        TARGET_CONTAINER_PATH="$CONTAINER_TARGET"
    else
        TARGET_HOST_PATH="$(pwd)"
        TARGET_CONTAINER_PATH="$CONTAINER_TARGET/$target_path"
    fi

    DOCKER_ARGS=("${args[@]}" "$TARGET_CONTAINER_PATH")
    OUTPUT_FILE="$output_file"
    HAS_OUTPUT="$has_output"
}

# Main execution
main() {
    check_docker
    check_image
    parse_args "$@"

    # Build docker run command
    local docker_cmd=(
        docker run --rm
        --init  # Proper signal handling (Ctrl+C)
    )

    # Allocate TTY and interactive mode if terminal is interactive
    if [[ -t 0 ]] && [[ -t 1 ]]; then
        docker_cmd+=(-it)
    elif [[ -t 1 ]]; then
        docker_cmd+=(-t)
    fi

    docker_cmd+=(-v "$TARGET_HOST_PATH:$CONTAINER_TARGET:ro")

    # If output file specified, we need write access to output directory
    if [[ "$HAS_OUTPUT" == "true" ]] && [[ -n "$OUTPUT_FILE" ]]; then
        local output_dir
        output_dir="$(dirname "$(pwd)/$OUTPUT_FILE")"
        mkdir -p "$output_dir"
        docker_cmd+=(-v "$output_dir:$CONTAINER_TARGET:rw")
    fi

    # Add image and arguments
    docker_cmd+=("$DOCKER_IMAGE" "${DOCKER_ARGS[@]}")

    # Execute
    "${docker_cmd[@]}"
    local exit_code=$?

    # Copy output file if specified
    if [[ "$HAS_OUTPUT" == "true" ]] && [[ -n "$OUTPUT_FILE" ]] && [[ -f "$OUTPUT_FILE" ]]; then
        echo -e "${GREEN}Report written to: $OUTPUT_FILE${NC}"
    fi

    exit $exit_code
}

main "$@"
