#!/bin/bash
set -euo pipefail

# ============================================================================
# Mosaic Stack Doctor
# ============================================================================
# Diagnostic and repair tool for Mosaic Stack installations.
# Run without arguments for interactive mode, or use flags for CI/CD.

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"

# Source library files
# shellcheck source=../lib/platform.sh
source "$SCRIPT_DIR/../lib/platform.sh"
# shellcheck source=../lib/dependencies.sh
source "$SCRIPT_DIR/../lib/dependencies.sh"
# shellcheck source=../lib/docker.sh
source "$SCRIPT_DIR/../lib/docker.sh"
# shellcheck source=../lib/validation.sh
source "$SCRIPT_DIR/../lib/validation.sh"

# ============================================================================
# Configuration
# ============================================================================

FIX_MODE=false
JSON_OUTPUT=false
VERBOSE=false
ENV_FILE="$PROJECT_ROOT/.env"
COMPOSE_FILE="$PROJECT_ROOT/docker-compose.yml"
MODE=""

# ============================================================================
# Help
# ============================================================================

print_usage() {
    cat << EOF
Mosaic Stack Doctor - Diagnostic and repair tool

USAGE:
    ./scripts/commands/doctor.sh [OPTIONS]

OPTIONS:
    -h, --help          Show this help message
    --fix               Attempt to automatically fix issues
    --json              Output results in JSON format
    --verbose           Show detailed output
    --env FILE          Path to .env file (default: .env)
    --compose FILE      Path to docker-compose.yml (default: docker-compose.yml)
    --mode MODE         Deployment mode: docker or native (auto-detected)

EXIT CODES:
    0   All checks passed
    1   Some checks failed
    2   Critical failure

EXAMPLES:
    # Run all checks
    ./scripts/commands/doctor.sh

    # Attempt automatic fixes
    ./scripts/commands/doctor.sh --fix

    # JSON output for CI/CD
    ./scripts/commands/doctor.sh --json

    # Verbose output
    ./scripts/commands/doctor.sh --verbose

EOF
}

# ============================================================================
# Argument Parsing
# ============================================================================

parse_arguments() {
    while [[ $# -gt 0 ]]; do
        case "$1" in
            -h|--help)
                print_usage
                exit 0
                ;;
            --fix)
                FIX_MODE=true
                shift
                ;;
            --json)
                JSON_OUTPUT=true
                shift
                ;;
            --verbose)
                VERBOSE=true
                shift
                ;;
            --env)
                ENV_FILE="$2"
                shift 2
                ;;
            --compose)
                COMPOSE_FILE="$2"
                shift 2
                ;;
            --mode)
                MODE="$2"
                shift 2
                ;;
            *)
                echo "Unknown option: $1"
                exit 1
                ;;
        esac
    done
    
    # Auto-detect mode if not specified
    if [[ -z "$MODE" ]]; then
        if [[ -f "$COMPOSE_FILE" ]] && command -v docker &>/dev/null && docker info &>/dev/null; then
            MODE="docker"
        else
            MODE="native"
        fi
    fi
}

# ============================================================================
# JSON Output Helpers
# ============================================================================

json_start() {
    if [[ "$JSON_OUTPUT" == true ]]; then
        echo "{"
        echo '  "timestamp": "'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'",'
        echo '  "version": "1.0.0",'
        echo '  "mode": "'$MODE'",'
        echo '  "checks": ['
    fi
}

json_end() {
    local errors="$1"
    local warnings="$2"
    
    if [[ "$JSON_OUTPUT" == true ]]; then
        echo ""
        echo "  ],"
        echo '  "summary": {'
        echo '    "errors": '$errors','
        echo '    "warnings": '$warnings','
        echo '    "status": "'$([ "$errors" -gt 0 ] && echo "failed" || ([ "$warnings" -gt 0 ] && echo "warning" || echo "passed"))'"'
        echo '  }'
        echo "}"
    fi
}

json_check() {
    local name="$1"
    local status="$2"
    local message="$3"
    local first="${4:-true}"
    
    if [[ "$JSON_OUTPUT" == true ]]; then
        [[ "$first" != "true" ]] && echo ","
        echo -n '    {"name": "'$name'", "status": "'$status'", "message": "'$message'"}'
    fi
}

# ============================================================================
# Fix Functions
# ============================================================================

fix_env_permissions() {
    echo -e "${WARN}→${NC} Fixing .env permissions..."
    chmod 600 "$ENV_FILE"
    echo -e "${SUCCESS}✓${NC} Fixed"
}

fix_docker_permissions() {
    echo -e "${WARN}→${NC} Adding user to docker group..."
    maybe_sudo usermod -aG docker "$USER"
    echo -e "${SUCCESS}✓${NC} User added to docker group"
    echo -e "${INFO}ℹ${NC} Run 'newgrp docker' or log out/in for changes to take effect"
}

start_docker_daemon() {
    echo -e "${WARN}→${NC} Starting Docker daemon..."
    maybe_sudo systemctl start docker
    sleep 3
    if docker info &>/dev/null; then
        echo -e "${SUCCESS}✓${NC} Docker started"
    else
        echo -e "${ERROR}✗${NC} Failed to start Docker"
        return 1
    fi
}

restart_containers() {
    echo -e "${WARN}→${NC} Restarting containers..."
    docker_compose_down "$COMPOSE_FILE" "$ENV_FILE"
    docker_compose_up "$COMPOSE_FILE" "$ENV_FILE"
    echo -e "${SUCCESS}✓${NC} Containers restarted"
}

generate_missing_secrets() {
    echo -e "${WARN}→${NC} Generating missing secrets..."
    
    # Load existing env
    if [[ -f "$ENV_FILE" ]]; then
        set -a
        # shellcheck source=/dev/null
        source "$ENV_FILE" 2>/dev/null || true
        set +a
    fi
    
    local updated=false
    
    # Check each secret
    if [[ -z "${JWT_SECRET:-}" ]] || is_placeholder "${JWT_SECRET:-}"; then
        JWT_SECRET=$(openssl rand -base64 32)
        echo "JWT_SECRET=$JWT_SECRET" >> "$ENV_FILE"
        updated=true
    fi
    
    if [[ -z "${BETTER_AUTH_SECRET:-}" ]] || is_placeholder "${BETTER_AUTH_SECRET:-}"; then
        BETTER_AUTH_SECRET=$(openssl rand -base64 32)
        echo "BETTER_AUTH_SECRET=$BETTER_AUTH_SECRET" >> "$ENV_FILE"
        updated=true
    fi
    
    if [[ -z "${ENCRYPTION_KEY:-}" ]] || is_placeholder "${ENCRYPTION_KEY:-}"; then
        ENCRYPTION_KEY=$(openssl rand -hex 32)
        echo "ENCRYPTION_KEY=$ENCRYPTION_KEY" >> "$ENV_FILE"
        updated=true
    fi
    
    if [[ -z "${POSTGRES_PASSWORD:-}" ]] || is_placeholder "${POSTGRES_PASSWORD:-}"; then
        POSTGRES_PASSWORD=$(openssl rand -base64 24 | tr -d '/+=' | head -c 32)
        # Update DATABASE_URL too
        DATABASE_URL="postgresql://mosaic:${POSTGRES_PASSWORD}@postgres:5432/mosaic"
        sed -i "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$POSTGRES_PASSWORD|" "$ENV_FILE" 2>/dev/null || \
            echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" >> "$ENV_FILE"
        sed -i "s|^DATABASE_URL=.*|DATABASE_URL=$DATABASE_URL|" "$ENV_FILE" 2>/dev/null || \
            echo "DATABASE_URL=$DATABASE_URL" >> "$ENV_FILE"
        updated=true
    fi
    
    if [[ "$updated" == true ]]; then
        echo -e "${SUCCESS}✓${NC} Secrets generated"
    else
        echo -e "${INFO}ℹ${NC} All secrets already set"
    fi
}

# ============================================================================
# Check Functions
# ============================================================================

run_checks() {
    local errors=0
    local warnings=0
    local first=true
    
    json_start
    
    # System requirements
    echo -e "${BOLD}━━━ System Requirements ━━━${NC}"
    check_system_requirements 2048 10
    local result=$?
    [[ $result -eq $CHECK_FAIL ]] && ((errors++))
    [[ $result -eq $CHECK_WARN ]] && ((warnings++))
    json_check "system_requirements" "$( [[ $result -eq 0 ]] && echo "pass" || ([[ $result -eq 1 ]] && echo "warn" || echo "fail") )" "RAM and disk check" "$first"
    first=false
    echo ""
    
    # Environment file
    echo -e "${BOLD}━━━ Environment File ━━━${NC}"
    check_env_file "$ENV_FILE"
    result=$?
    [[ $result -eq $CHECK_FAIL ]] && ((errors++))
    [[ $result -eq $CHECK_WARN ]] && ((warnings++))
    json_check "env_file" "$( [[ $result -eq 0 ]] && echo "pass" || ([[ $result -eq 1 ]] && echo "warn" || echo "fail") )" ".env file exists" "$first"
    echo ""
    
    # Required variables
    check_required_env "$ENV_FILE"
    result=$?
    [[ $result -eq $CHECK_FAIL ]] && ((errors++))
    [[ $result -eq $CHECK_WARN ]] && ((warnings++))
    json_check "required_env" "$( [[ $result -eq 0 ]] && echo "pass" || ([[ $result -eq 1 ]] && echo "warn" || echo "fail") )" "Required environment variables" "$first"
    echo ""
    
    # Secret strength
    check_secrets "$ENV_FILE"
    result=$?
    [[ $result -eq $CHECK_FAIL ]] && ((errors++))
    [[ $result -eq $CHECK_WARN ]] && ((warnings++))
    json_check "secrets" "$( [[ $result -eq 0 ]] && echo "pass" || ([[ $result -eq 1 ]] && echo "warn" || echo "fail") ")" "Secret strength and validity" "$first"
    echo ""
    
    # File permissions
    check_env_permissions "$ENV_FILE"
    result=$?
    [[ $result -eq $CHECK_FAIL ]] && ((errors++))
    [[ $result -eq $CHECK_WARN ]] && ((warnings++))
    json_check "env_permissions" "$( [[ $result -eq 0 ]] && echo "pass" || ([[ $result -eq 1 ]] && echo "warn" || echo "fail") ")" ".env file permissions" "$first"
    echo ""
    
    if [[ "$MODE" == "docker" ]]; then
        # Docker checks
        echo -e "${BOLD}━━━ Docker ━━━${NC}"
        
        check_docker
        result=$?
        [[ $result -ne 0 ]] && ((errors++))
        json_check "docker" "$( [[ $result -eq 0 ]] && echo "pass" || "fail")" "Docker availability" "$first"
        echo ""
        
        if [[ $result -eq 0 ]]; then
            check_docker_compose
            result=$?
            [[ $result -ne 0 ]] && ((errors++))
            json_check "docker_compose" "$( [[ $result -eq 0 ]] && echo "pass" || "fail")" "Docker Compose availability" "$first"
            echo ""
            
            # Container status
            check_docker_containers "$COMPOSE_FILE"
            result=$?
            [[ $result -eq $CHECK_FAIL ]] && ((errors++))
            [[ $result -eq $CHECK_WARN ]] && ((warnings++))
            json_check "containers" "$( [[ $result -eq 0 ]] && echo "pass" || ([[ $result -eq 1 ]] && echo "warn" || echo "fail") ")" "Container status" "$first"
            echo ""
            
            # Container health
            check_container_health
            result=$?
            [[ $result -eq $CHECK_FAIL ]] && ((errors++))
            [[ $result -eq $CHECK_WARN ]] && ((warnings++))
            json_check "container_health" "$( [[ $result -eq 0 ]] && echo "pass" || ([[ $result -eq 1 ]] && echo "warn" || echo "fail") ")" "Container health checks" "$first"
            echo ""
            
            # Database
            check_database_connection
            result=$?
            [[ $result -eq $CHECK_FAIL ]] && ((errors++))
            [[ $result -eq $CHECK_WARN ]] && ((warnings++))
            json_check "database" "$( [[ $result -eq 0 ]] && echo "pass" || ([[ $result -eq 1 ]] && echo "warn" || echo "fail") ")" "Database connectivity" "$first"
            echo ""
            
            # Valkey
            check_valkey_connection
            result=$?
            [[ $result -eq $CHECK_FAIL ]] && ((errors++))
            [[ $result -eq $CHECK_WARN ]] && ((warnings++))
            json_check "cache" "$( [[ $result -eq 0 ]] && echo "pass" || ([[ $result -eq 1 ]] && echo "warn" || echo "fail") ")" "Valkey/Redis connectivity" "$first"
            echo ""
            
            # API
            check_api_health
            result=$?
            [[ $result -eq $CHECK_FAIL ]] && ((errors++))
            [[ $result -eq $CHECK_WARN ]] && ((warnings++))
            json_check "api" "$( [[ $result -eq 0 ]] && echo "pass" || ([[ $result -eq 1 ]] && echo "warn" || echo "fail") ")" "API health endpoint" "$first"
            echo ""
            
            # Web
            check_web_health
            result=$?
            [[ $result -eq $CHECK_FAIL ]] && ((errors++))
            [[ $result -eq $CHECK_WARN ]] && ((warnings++))
            json_check "web" "$( [[ $result -eq 0 ]] && echo "pass" || ([[ $result -eq 1 ]] && echo "warn" || echo "fail") ")" "Web frontend" "$first"
            echo ""
        fi
    else
        # Native mode checks
        echo -e "${BOLD}━━━ Native Dependencies ━━━${NC}"
        
        check_node
        result=$?
        [[ $result -ne 0 ]] && ((errors++))
        json_check "nodejs" "$( [[ $result -eq 0 ]] && echo "pass" || "fail")" "Node.js" "$first"
        echo ""
        
        check_pnpm
        result=$?
        [[ $result -ne 0 ]] && ((errors++))
        json_check "pnpm" "$( [[ $result -eq 0 ]] && echo "pass" || "fail")" "pnpm" "$first"
        echo ""
        
        check_postgres
        result=$?
        [[ $result -ne 0 ]] && ((warnings++))
        json_check "postgresql" "$( [[ $result -eq 0 ]] && echo "pass" || "warn")" "PostgreSQL" "$first"
        echo ""
    fi
    
    json_end $errors $warnings
    
    return $([ $errors -eq 0 ] && echo 0 || echo 1)
}

# ============================================================================
# Interactive Fix Mode
# ============================================================================

interactive_fix() {
    local issues=()
    
    # Collect issues
    if [[ ! -f "$ENV_FILE" ]]; then
        issues+=("env_missing:Missing .env file")
    fi
    
    if [[ -f "$ENV_FILE" ]]; then
        # Check permissions
        local perms
        perms=$(stat -c "%a" "$ENV_FILE" 2>/dev/null || stat -f "%OLp" "$ENV_FILE" 2>/dev/null)
        if [[ "$perms" =~ [0-7][0-7][4-7]$ ]]; then
            issues+=("env_perms:.env is world-readable")
        fi
        
        # Check secrets
        set -a
        # shellcheck source=/dev/null
        source "$ENV_FILE" 2>/dev/null || true
        set +a
        
        if [[ -z "${JWT_SECRET:-}" ]] || is_placeholder "${JWT_SECRET:-}"; then
            issues+=("secrets:Missing or invalid secrets")
        fi
    fi
    
    if [[ "$MODE" == "docker" ]]; then
        if ! docker info &>/dev/null; then
            local docker_result
            docker_result=$(docker info 2>&1)
            if [[ "$docker_result" =~ "permission denied" ]]; then
                issues+=("docker_perms:Docker permission denied")
            elif [[ "$docker_result" =~ "Cannot connect to the Docker daemon" ]]; then
                issues+=("docker_daemon:Docker daemon not running")
            fi
        fi
    fi
    
    if [[ ${#issues[@]} -eq 0 ]]; then
        echo -e "${SUCCESS}✓${NC} No fixable issues found"
        return 0
    fi
    
    echo -e "${BOLD}Found ${#issues[@]} fixable issue(s):${NC}"
    echo ""
    
    for issue in "${issues[@]}"; do
        local code="${issue%%:*}"
        local desc="${issue#*:}"
        echo "  - $desc"
    done
    
    echo ""
    read -r -p "Fix these issues? [Y/n]: " fix_them
    case "$fix_them" in
        n|N)
            echo -e "${INFO}ℹ${NC} Skipping fixes"
            return 0
            ;;
    esac
    
    # Apply fixes
    for issue in "${issues[@]}"; do
        local code="${issue%%:*}"
        
        case "$code" in
            env_perms)
                fix_env_permissions
                ;;
            docker_perms)
                fix_docker_permissions
                ;;
            docker_daemon)
                start_docker_daemon
                ;;
            secrets)
                generate_missing_secrets
                ;;
            *)
                echo -e "${WARN}⚠${NC} Unknown issue: $code"
                ;;
        esac
    done
    
    echo ""
    echo -e "${SUCCESS}✓${NC} Fixes applied"
}

# ============================================================================
# Main
# ============================================================================

main() {
    parse_arguments "$@"
    
    # Configure verbose mode
    if [[ "$VERBOSE" == true ]]; then
        set -x
    fi
    
    # Show banner (unless JSON mode)
    if [[ "$JSON_OUTPUT" != true ]]; then
        echo ""
        echo -e "${BOLD}════════════════════════════════════════════════════════════${NC}"
        echo -e "${BOLD}  Mosaic Stack Doctor${NC}"
        echo -e "${BOLD}════════════════════════════════════════════════════════════${NC}"
        echo ""
        echo -e "  Mode:     ${INFO}$MODE${NC}"
        echo -e "  Env:      ${INFO}$ENV_FILE${NC}"
        echo -e "  Compose:  ${INFO}$COMPOSE_FILE${NC}"
        echo ""
    fi
    
    # Run checks
    run_checks
    local check_result=$?
    
    # Fix mode
    if [[ "$FIX_MODE" == true && "$JSON_OUTPUT" != true && $check_result -ne 0 ]]; then
        echo ""
        echo -e "${BOLD}━━━ Fix Mode ━━━${NC}"
        echo ""
        interactive_fix
    fi
    
    # Summary (unless JSON mode)
    if [[ "$JSON_OUTPUT" != true ]]; then
        echo ""
        echo -e "${BOLD}════════════════════════════════════════════════════════════${NC}"
        
        if [[ $check_result -eq 0 ]]; then
            echo -e "${SUCCESS}✓ All checks passed${NC}"
        else
            echo -e "${WARN}⚠ Some checks failed${NC}"
            echo ""
            echo "Run with --fix to attempt automatic repairs"
        fi
        
        echo -e "${BOLD}════════════════════════════════════════════════════════════${NC}"
    fi
    
    exit $([ $check_result -eq 0 ] && echo 0 || echo 1)
}

# Run
main "$@"
