#!/bin/bash

# Import utility scripts
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/utils/logging.sh"
source "$SCRIPT_DIR/utils/os_detection.sh"
source "$SCRIPT_DIR/utils/install_config.sh"
source "$SCRIPT_DIR/utils/python_tools.sh"

# Version
VERSION="1.0.0"

# Total number of stages
TOTAL_STAGES=6
CURRENT_STAGE=0
STAGE_NAME=""

# Trap ctrl-c and call cleanup
trap cleanup INT

# Cleanup function to restore terminal settings
cleanup() {
    echo -e "\n${YELLOW}Setup interrupted.${NC}"
    exit 1
}

# Display title and logo
display_header() {
    local title="$1"
    
    echo ""
    echo -e "${CYAN}"
    echo ' ███████╗███████╗ ██████╗ ██████╗ ███╗   ██╗██████╗       ███╗   ███╗███████╗'
    echo ' ██╔════╝██╔════╝██╔════╝██╔═══██╗████╗  ██║██╔══██╗      ████╗ ████║██╔════╝'
    echo ' ███████╗█████╗  ██║     ██║   ██║██╔██╗ ██║██║  ██║█████╗██╔████╔██║█████╗  '
    echo ' ╚════██║██╔══╝  ██║     ██║   ██║██║╚██╗██║██║  ██║╚════╝██║╚██╔╝██║██╔══╝  '
    echo ' ███████║███████╗╚██████╗╚██████╔╝██║ ╚████║██████╔╝      ██║ ╚═╝ ██║███████╗'
    echo ' ╚══════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝  ╚═══╝╚═════╝       ╚═╝     ╚═╝╚══════╝'
    echo -e "${NC}"
    echo -e "${BOLD}Second-Me Setup Script v${VERSION}${NC}"
    echo -e "${GRAY}$(date)${NC}\n"
    
    if [ -n "$title" ]; then
        echo -e "${CYAN}====== $title ======${NC}"
        echo ""
    fi
}

# Display stage start
display_stage() {
    local stage_num=$1
    local stage_name=$2
    CURRENT_STAGE=$stage_num
    STAGE_NAME=$stage_name
    
    echo ""
    echo -e "${CYAN}====== Stage $stage_num/$TOTAL_STAGES: $stage_name ======${NC}"
    echo ""
}

# Setup and configure package managers (npm)
check_npm() {
    log_step "Checking npm installation..."
    
    # Check if npm is already installed
    if ! command -v npm &>/dev/null; then
        log_error "npm not found - please install npm manually"
        
        # Get system identification and show installation recommendations
        local system_id=$(get_system_id)
        get_npm_recommendation "$system_id"
        
        return 1
    fi

    log_success "npm check passed"
    return 0
}

# Check Node.js installation
check_node() {
    log_step "Checking Node.js installation..."
    
    local node_cmd=""
    
    # Check for node command
    if command -v node &>/dev/null; then
        node_cmd="node"
    # Also check for nodejs command as it's used on some Linux distributions
    elif command -v nodejs &>/dev/null; then
        node_cmd="nodejs"
    else
        log_error "Node.js is not installed, please install Node.js manually"
        
        # Get system identification and show installation recommendations
        local system_id=$(get_system_id)
        get_node_recommendation "$system_id"
        
        return 1
    fi
    
    # Check version (if needed)
    local version=$($node_cmd --version 2>&1 | sed 's/v//')
    log_success "Node.js check passed, using $node_cmd version $version"
    return 0
}

# Check if command exists
check_command() {
    if ! command -v $1 &> /dev/null; then
        return 1
    fi
    return 0
}

# Helper function to install dependencies
install_python_dependency() {
    # Install Python packages using Poetry
    log_step "Installing Python packages using Poetry"
    
    # Check if pyproject.toml exists
    if [ ! -f "pyproject.toml" ]; then
        log_error "Missing pyproject.toml file"
        return 1
    fi
    
    # Update lockfile and install dependencies
    log_info "Updating Poetry lockfile..."
    if ! poetry lock --no-cache; then
        log_error "Failed to update Poetry lockfile"
        return 1
    fi
    
    # Install dependencies
    log_info "Using Poetry to install dependencies..."
    if ! poetry install --no-root --no-interaction; then
        log_error "Failed to install dependencies using Poetry"
        return 1
    fi
    
    # Verify key packages are installed using Poetry's own environment
    log_info "Verifying key packages using Poetry environment..."
    local required_packages=("flask" "chromadb" "langchain")
    for pkg in "${required_packages[@]}"; do
        if ! poetry run python -c "import $pkg" 2>/dev/null; then
            log_error "Package '$pkg' is not installed correctly in Poetry environment"
            return 1
        else
            log_info "Package '$pkg' is installed correctly in Poetry environment"
        fi
    done
    
    # Get and save the Poetry environment path
    local poetry_env_path=$(poetry env info -p 2>/dev/null)
    if [ -n "$poetry_env_path" ]; then
        log_info "Poetry virtual environment is located at: $poetry_env_path"
        # Create an activation script for convenience
        create_poetry_activate_script "$poetry_env_path"
    fi

    log_success "Python environment setup completed"
    log_info "------------------------------------------------------------------------------"
    log_info "To use this Python environment, you can:"
    log_info "1. Run 'poetry shell' to open a new shell with the virtual environment activated"
    log_info "2. Run 'source .poetry-venv/activate' to activate the environment in your current shell"
    log_info "3. Use 'poetry run python script.py' to run a single command without activating the environment"
    log_info "------------------------------------------------------------------------------"
    return 0
}

# Create a convenient activation script for the Poetry environment
create_poetry_activate_script() {
    local env_path="$1"
    local activate_dir=".poetry-venv"
    local activate_script="$activate_dir/activate"
    
    # Create directory if it doesn't exist
    mkdir -p "$activate_dir"
    
    # Create activation script
    cat > "$activate_script" << EOF
#!/bin/bash
# Activation script for Poetry virtual environment

# Source the actual virtual environment activate script
source "$env_path/bin/activate"

# Print confirmation message
echo "Poetry virtual environment activated: $env_path"
echo "Use 'deactivate' command to exit this environment"
EOF
    
    # Make script executable
    chmod +x "$activate_script"
    log_info "Created Poetry environment activation script at: $activate_script"
}

install_graphrag() {
    log_step "Installing graphrag"
    
    # Check the current graphrag version in Poetry environment
    log_step "Checking graphrag version in Poetry environment"
    GRAPHRAG_VERSION=$(poetry run pip show graphrag 2>/dev/null | grep "Version:" | cut -d " " -f2)
    GRAPHRAG_TARGET="1.2.1.dev27"
    GRAPHRAG_LOCAL_PATH="dependencies/graphrag-${GRAPHRAG_TARGET}.tar.gz"

    if [ "$GRAPHRAG_VERSION" != "$GRAPHRAG_TARGET" ]; then
        log_info "Installing correct version of graphrag in Poetry environment..."
        if [ -f "$GRAPHRAG_LOCAL_PATH" ]; then
            log_info "Installing graphrag from local file using Poetry..."
            if ! poetry run pip install --force-reinstall "$GRAPHRAG_LOCAL_PATH"; then
                log_error "Failed to install graphrag from local file"
                return 1
            fi
        else
            log_error "Local graphrag package not found at: $GRAPHRAG_LOCAL_PATH"
            log_error "Please ensure the graphrag package exists in the dependencies directory"
            return 1
        fi
        log_success "Graphrag installed successfully"
    else
        log_success "Graphrag version is correct, skipping installation"
    fi
    
    return 0
}

# Build llama.cpp
build_llama() {
    log_section "BUILDING LLAMA.CPP"
    
    LLAMA_LOCAL_ZIP="dependencies/llama.cpp.zip"
    
    # Check if llama.cpp directory exists
    if [ ! -d "llama.cpp" ]; then
        log_info "Setting up llama.cpp..."
        
        if [ -f "$LLAMA_LOCAL_ZIP" ]; then
            log_info "Using local llama.cpp archive..."
            if ! unzip -q "$LLAMA_LOCAL_ZIP"; then
                log_error "Failed to extract local llama.cpp archive"
                return 1
            fi
        else
            log_error "Local llama.cpp archive not found at: $LLAMA_LOCAL_ZIP"
            log_error "Please ensure the llama.cpp.zip file exists in the dependencies directory"
            return 1
        fi
    else
        log_info "Found existing llama.cpp directory"
    fi
    
    # Check if llama.cpp has been successfully compiled
    if [ -f "llama.cpp/build/bin/llama-server" ]; then
        log_info "Found existing llama-server build"
        # Check if executable file can be run and get version info
        if version_output=$(./llama.cpp/build/bin/llama-server --version 2>&1) && [[ $version_output == version:* ]]; then
            log_success "Existing llama-server build is working properly (${version_output}), skipping compilation"
            return 0
        else
            log_warning "Existing build seems broken or incompatible, will recompile..."
        fi
    fi
    
    # Enter llama.cpp directory and build
    cd llama.cpp
    
    # Clean previous build
    if [ -d "build" ]; then
        log_info "Cleaning previous build..."
        rm -rf build
    fi
    
    # Create and enter build directory
    log_info "Creating build directory..."
    mkdir -p build && cd build
    
    # Configure CMake
    log_info "Configuring CMake..."
    if ! cmake ..; then
        log_error "CMake configuration failed"
        cd ../..
        return 1
    fi
    
    # Build project
    log_info "Building project..."
    if ! cmake --build . --config Release; then
        log_error "Build failed"
        cd ../..
        return 1
    fi
    
    # Check build result
    if [ ! -f "bin/llama-server" ]; then
        log_error "Build failed: llama-server executable not found"
        log_error "Expected at: bin/llama-server"
        cd ../..
        return 1
    fi
    
    log_success "Found llama-server at: bin/llama-server"
    cd ../..
    log_section "LLAMA.CPP BUILD COMPLETE"
}

# Set up frontend environment
build_frontend() {
    log_section "SETTING UP FRONTEND"
    
    FRONTEND_DIR="lpm_frontend"
    
    # Enter frontend directory
    cd "$FRONTEND_DIR" || {
        log_error "Failed to enter frontend directory: $FRONTEND_DIR"
        log_error "Please ensure the directory exists and you have permission to access it."
        return 1
    }
    
    # Check if dependencies have been installed
    if [ -d "node_modules" ]; then
        log_info "Found existing node_modules, checking for updates..."
        if [ -f "package-lock.json" ]; then
            log_info "Using existing package-lock.json..."
            # Run npm install even if package-lock.json exists to ensure dependencies are complete
            log_info "Running npm install to ensure dependencies are complete..."
            if ! npm install; then
                log_error "Failed to install frontend dependencies with existing package-lock.json"
                log_error "Try removing node_modules directory and package-lock.json, then run setup again"
                cd ..
                return 1
            fi
        else
            log_info "Installing dependencies..."
            if ! npm install; then
                log_error "Failed to install frontend dependencies"
                log_error "Check your npm configuration and network connection"
                log_error "You can try running 'npm install' manually in the $FRONTEND_DIR directory"
                cd ..
                return 1
            fi
        fi
    else
        log_info "Installing dependencies..."
        if ! npm install; then
            log_error "Failed to install frontend dependencies"
            log_error "Check your npm configuration and network connection"
            log_error "You can try running 'npm install' manually in the $FRONTEND_DIR directory"
            cd ..
            return 1
        fi
    fi
    
    # Verify that the installation was successful
    if [ ! -d "node_modules" ]; then
        log_error "node_modules directory not found after npm install"
        log_error "Frontend dependencies installation failed"
        cd ..
        return 1
    fi
    
    log_success "Frontend dependencies installed successfully"
    cd ..
    log_section "FRONTEND SETUP COMPLETE"
}

# Show help information
show_help() {
    echo -e "${BOLD}Second-Me Setup Script v${VERSION}${NC}"
    echo -e "Usage: $0 [options] [command]"
    echo
    echo -e "Commands:"
    echo -e "  python\t\tSetup Python environment only"
    echo -e "  llama\t\t\tBuild llama.cpp only"
    echo -e "  frontend\t\tSetup frontend project only"
    echo -e "  (no command)\t\tPerform full installation"
    echo
    echo -e "Options:"
    echo -e "  --help\t\tShow this help information"
    echo -e "  --require-confirmation\tRequire confirmation when warnings are present"
    echo
    echo -e "Examples:"
    echo -e "  $0 \t\t\tPerform full installation"
    echo -e "  $0 python\t\tSetup Python environment only"
    echo -e "  $0 --require-confirmation\tRequire confirmation when warnings are present"
    echo
    echo -e "For a complete list of all available commands, run:"
    echo -e "  make help"
}

# Check system requirements
check_system_requirements() {
    log_step "Checking system requirements"
    
    # Detect system type
    local system_type=$(uname -s)
    log_info "Detected system type: $system_type"
    
    # Only check macOS version if on Mac
    if [[ "$system_type" == "Darwin" ]]; then
        local macos_version=$(sw_vers -productVersion)
        log_info "Detected macOS version: $macos_version"
        
        local major_version=$(echo "$macos_version" | cut -d. -f1)
        if [[ "$major_version" -lt 14 ]]; then
            log_error "This script requires macOS 14 (Sonoma) or later. Your version: $macos_version"
            return 1
        fi
    fi

    log_success "System requirements check passed"
    return 0
}

# Check required configuration files
check_config_files() {
    log_step "Checking necessary configuration files"
    
    # Check for .env file
    if [[ ! -f ".env" ]]; then
        log_error "Missing .env file"
        return 1
    fi
    
    log_success "All necessary configuration files are present"
    return 0
}

# Check directory permissions
check_directory_permissions() {
    log_step "Checking directory permissions"
    local errors=0
    local directories=("." "./scripts" "./run" "./logs")
    
    for dir in "${directories[@]}"; do
        if [[ ! -w "$dir" ]]; then
            log_error "Directory without write permission: $dir"
            errors=$((errors + 1))
        fi
    done
    
    if [[ $errors -eq 0 ]]; then
        log_success "Directory permissions check passed"
        return 0
    else
        return 1
    fi
}

# Check for potential conflicts
check_potential_conflicts() {
    log_info "Checking for potential conflicts"

    # System requirements check
    if ! check_system_requirements; then
        log_error "System requirements check failed"
        exit 1
    fi
    
    # Configuration files check
    if ! check_config_files; then
        log_error "Configuration files check failed"
        exit 1
    fi
    
    # Directory permissions check
    if ! check_directory_permissions; then
        log_error "Directory permissions check failed"
        exit 1
    fi

    if ! check_python; then
        log_error "python check failed, please install python first"
        exit 1
    fi
    
    if ! check_node; then
        log_error "Node.js check failed"
        exit 1
    fi
    
    if ! check_npm; then
        log_error "npm check failed"
        exit 1
    fi
    
    if ! check_cmake; then
        log_error "cmake check and installation failed"
        exit 1
    fi

    if ! check_poetry; then
        log_error "poetry check failed, please install poetry first"
        exit 1
    fi
    
    return 0
}

check_python() {
    log_step "Checking for python installation"
    
    # Get the appropriate Python command
    local python_cmd=$(get_python_command)
    
    if [ -z "$python_cmd" ]; then
        log_error "python is not installed, please install python manually"
        
        # Get system identification and show installation recommendations
        local system_id=$(get_system_id)
        get_python_recommendation "$system_id"
        
        return 1
    fi
    
    # version > 3.12
    local version=$($python_cmd --version 2>&1 | cut -d ' ' -f 2)
    if [[ "$version" < "3.12" ]]; then
        log_error "python version $version is not supported, please install python 3.12 or higher"
        return 1
    fi
    
    log_success "python check passed, using $python_cmd version $version"
    return 0
}

check_poetry() {
    log_step "Checking for poetry installation"
    
    if ! command -v poetry &>/dev/null; then
        log_error "poetry is not installed, please install poetry manually"
        
        # Get system identification and show installation recommendations
        local system_id=$(get_system_id)
        get_poetry_recommendation "$system_id"
        
        return 1
    fi
    
    log_success "poetry check passed"
    return 0
}

# Check and install cmake if not present
check_cmake() {
    log_step "Checking for cmake installation"
    
    if ! command -v cmake &>/dev/null; then
        log_warning "cmake is not installed, please install cmake manually"
        
        # Get system identification and show installation recommendations
        local system_id=$(get_system_id)
        get_cmake_recommendation "$system_id"
        
        return 1
    fi
    
    log_success "cmake check passed"
    return 0
}

# Check if SQLite is installed and available
check_sqlite() {
    log_step "Checking SQLite"
    
    if ! check_command "sqlite3"; then
        log_warning "SQLite3 is not installed or not in your PATH"
        
        log_error "Please install SQLite before continuing, database operations require this dependency"

        # Get system identification and show installation recommendations
        local system_id=$(get_system_id)
        get_sqlite_recommendation "$system_id"
        
        return 1
    fi
    
    # SQLite is installed
    local version=$(sqlite3 --version | awk '{print $1}')
    log_success "SQLite check passed, version $version"
    return 0
}

# Parse command line arguments
parse_args() {
    REQUIRE_CONFIRMATION=false
    
    while [[ $# -gt 0 ]]; do
        case $1 in
            --require-confirmation)
                REQUIRE_CONFIRMATION=true
                shift
                ;;
            python|llama|frontend)
                COMPONENT="$1"
                shift
                ;;
            *)
                log_error "Unknown argument: $1"
                show_help
                exit 1
                ;;
        esac
    done
}


# Main function
main() {
    # Display welcome message
    display_header "Second-Me Complete Installation"
    
    # Parse command line arguments
    parse_args "$@"
    
    # All pre-installation checks
    log_section "Running pre-installation checks"
    
    # 1. Basic tools check (most fundamental)
    if ! check_potential_conflicts; then
        log_error "Basic tools check failed"
        exit 1
    fi
    
    # Check SQLite installation
    if ! check_sqlite; then
        log_error "SQLite check failed"
        exit 1
    fi
    
    # Start installation process
    log_section "Starting installation"
    
    if ! install_python_dependency; then
        log_error "Failed to install python dependencies"
        exit 1
    fi

    if ! install_graphrag; then
        log_error "Failed to install graphrag"
        exit 1
    fi

    # 3. Build llama.cpp
    if ! build_llama; then
        exit 1
    fi
    
    # 4. Build frontend
    if ! build_frontend; then
        exit 1
    fi

    log_success "Installation complete!"
    return 0
}

# Start execution
main "$@"
