#!/bin/bash

# LibrePortal WebUI Services Config Generator
# Generates apps-services.json from database
webuiGenerateAppsServicesConfig() {
    local testing_mode="$1"
    local output_file="${containers_dir}libreportal/frontend/data/apps/generated/apps-services.json"
    local first_service=true
    local service_count=0
    
    isNotice "Generating apps-services.json from database..."

    runFileOp mkdir -p "$(dirname "$output_file")"

    # Create temp file first, then atomic move
    local temp_file="$(mktemp)"

    # Create header
    cat > "$temp_file" << 'EOF'
{
  "services": [
    
EOF
    
    if [[ ! -f "$docker_dir/$db_file" ]]; then
        echo "Warning: Database not found at $docker_dir/$db_file" 
        # Write empty array and close
        echo "  ]" >> "$temp_file"
        echo "}" >> "$temp_file"
        runFileWrite "$output_file" < "$temp_file"; rm -f "$temp_file"
    fi
    
    # Get all installed apps
    local installed_apps=$(sqlite3 "$docker_dir/$db_file" "SELECT name FROM apps WHERE status = 1 ORDER BY name;" 2>/dev/null)
    
    if [[ -z "$installed_apps" ]]; then
        echo "No installed apps found" 
        # Write empty array and close
        echo "  ]" >> "$temp_file"
        echo "}" >> "$temp_file"
        runFileWrite "$output_file" < "$temp_file"; rm -f "$temp_file"
    fi
    
    # Process each app
    while IFS= read -r app_name; do
        # Get app installation date
        local install_date=$(sqlite3 "$docker_dir/$db_file" "SELECT install_date, install_time FROM apps WHERE name = '$app_name';" 2>/dev/null)
        local installed_date="${install_date}|"

        # Get all Docker services for this app
        local docker_services=$(sqlite3 "$docker_dir/$db_file" "SELECT service_name, resource_value FROM network_resources WHERE app_name = '$app_name' AND resource_type = 'ip' AND status = 'active' ORDER BY service_name;" 2>/dev/null)
        
        if [[ -n "$docker_services" ]]; then
            while IFS='|' read -r docker_service_name docker_service_ip; do
                # Get all ports for this service
                local service_ports=$(sqlite3 "$docker_dir/$db_file" "SELECT service_name, resource_value FROM network_resources WHERE app_name = '$app_name' AND resource_type = 'port' AND parent_service = '$docker_service_name' AND status = 'active' ORDER BY service_name;" 2>/dev/null)
                
                if [[ -n "$service_ports" ]]; then
                    while IFS='|' read -r port_service_name port_mapping; do
                        # Parse port mapping: external:internal:access:protocol
                        local external_port=$(echo "$port_mapping" | cut -d':' -f1)
                        local internal_port=$(echo "$port_mapping" | cut -d':' -f2)
                        local access_type=$(echo "$port_mapping" | cut -d':' -f3)
                        local protocol=$(echo "$port_mapping" | cut -d':' -f4)
                        
                        # Get server IP (from system config or default)
                        local server_ip=""
                        if [[ -f "${containers_dir}libreportal/config/generated/configs.json" ]]; then
                            server_ip=$(grep -o '"CFG_SERVER_IP":[[:space:]]*"[^"]*"' "${containers_dir}libreportal/config/generated/configs.json" | cut -d'"' -f4)
                        fi
                        [[ -z "$server_ip" ]] && server_ip="localhost"
                        
                        # Determine if Traefik managed (check if service has Traefik labels)
                        local traefik_managed="false"
                        # For now, assume public access means Traefik managed
                        [[ "$access_type" == "public" ]] && traefik_managed="true"
                        
                        # Per-port URL path (10th col of CFG_<APP>_PORT_N).
                        # Set when the matching port row is found below; left
                        # empty for ports without a web UI (DNS, etc.).
                        local port_url_path=""
                        
                        # Get button config from app config file
                        local button_enabled="false"
                        local button_text=""
                        local login_required="false"
                        local app_config_file="${install_containers_dir}/${app_name}/${app_name}.config"
                        if [[ -f "$app_config_file" ]]; then
                            # Look for button config in port configuration
                            while IFS='=' read -r var_name var_value || [[ -n "$var_name" ]]; do
                                [[ -z "$var_name" || "$var_name" =~ ^[[:space:]]*# ]] && continue
                                [[ "$var_name" =~ ^CFG_${app_name^^}_PORT_ ]] || continue

                                # Remove quotes from value
                                var_value="${var_value#\"}"
                                var_value="${var_value%\"}"

                                # 10-col: service|name|external:internal|access|protocol|login|traefik|button_enabled|button_text|url_path
                                # 9-col legacy:  service|name|external:internal|access|protocol|login|traefik|button_enabled|button_text
                                # 8-col legacy (no login):
                                #         service|name|external:internal|access|protocol|traefik|button_enabled|button_text
                                local field_count=$(awk -F'|' '{print NF}' <<< "$var_value")
                                local config_service=$(echo "$var_value" | cut -d'|' -f1)
                                local config_name=$(echo "$var_value" | cut -d'|' -f2)
                                local config_login_required="false"
                                local config_button_enabled
                                local config_button_text
                                local config_url_path=""
                                if [[ "$field_count" -ge 10 ]]; then
                                    config_login_required=$(echo "$var_value" | cut -d'|' -f6)
                                    config_button_enabled=$(echo "$var_value" | cut -d'|' -f8)
                                    config_button_text=$(echo "$var_value" | cut -d'|' -f9)
                                    config_url_path=$(echo "$var_value" | cut -d'|' -f10)
                                elif [[ "$field_count" -ge 9 ]]; then
                                    config_login_required=$(echo "$var_value" | cut -d'|' -f6)
                                    config_button_enabled=$(echo "$var_value" | cut -d'|' -f8)
                                    config_button_text=$(echo "$var_value" | cut -d'|' -f9)
                                else
                                    config_button_enabled=$(echo "$var_value" | cut -d'|' -f7)
                                    config_button_text=$(echo "$var_value" | cut -d'|' -f8)
                                fi

                                # Construct full service name from config (appname_servicename format used in database)
                                local config_full_service_name="${app_name}_${config_name}"

                                # Case-insensitive matching for service names
                                if [[ "${config_service,,}" == "${docker_service_name,,}" && "${config_full_service_name,,}" == "${port_service_name,,}" ]]; then
                                    button_enabled="$config_button_enabled"
                                    button_text="$config_button_text"
                                    login_required="$config_login_required"
                                    port_url_path="$config_url_path"
                                fi
                            done < "$app_config_file"
                        fi

                        # Default button text if not set
                        [[ -z "$button_text" ]] && button_text="${port_service_name^} Interface"

                        # Multi-button support: button_text + port_url_path may
                        # carry comma-separated parallel arrays. The first pair
                        # remains the "primary" (kept in externalURL/buttonText
                        # for back-compat); the full list goes into a `links[]`
                        # array the frontend can render as multiple Open buttons.
                        local IFS_BAK="$IFS"
                        IFS=',' read -ra _btn_labels <<< "$button_text"
                        IFS=',' read -ra _btn_paths  <<< "$port_url_path"
                        IFS="$IFS_BAK"

                        # Normalize each path: empty stays empty; non-empty
                        # gets a leading slash if missing so authors can write
                        # either "admin/" or "/admin/" interchangeably.
                        local _link_count=${#_btn_labels[@]}
                        local _i
                        for ((_i=0; _i<_link_count; _i++)); do
                            local _p="${_btn_paths[$_i]:-}"
                            if [[ -n "$_p" && "$_p" != /* ]]; then
                                _btn_paths[$_i]="/$_p"
                            fi
                        done

                        # Primary URL = first pair (kept for back-compat).
                        local _primary_path="${_btn_paths[0]:-}"
                        local _primary_label="${_btn_labels[0]:-$button_text}"
                        local external_url="http://${server_ip}:${external_port}${_primary_path}"
                        local internal_url="http://${docker_service_ip}:${internal_port}${_primary_path}"

                        # Build links[] JSON array — one entry per button.
                        local _links_json=""
                        for ((_i=0; _i<_link_count; _i++)); do
                            local _l="${_btn_labels[$_i]}"
                            local _p="${_btn_paths[$_i]:-}"
                            [[ $_i -gt 0 ]] && _links_json+=","
                            _links_json+=$'\n          {'
                            _links_json+=$'\n            '"\"label\": \"$_l\","
                            _links_json+=$'\n            '"\"externalURL\": \"http://${server_ip}:${external_port}${_p}\","
                            _links_json+=$'\n            '"\"internalURL\": \"http://${docker_service_ip}:${internal_port}${_p}\""
                            _links_json+=$'\n          }'
                        done

                        # Add separator comma for non-first services
                        if [[ "$first_service" == false ]]; then
                            echo "    }," >> "$temp_file"
                        fi
                        first_service=false
                        service_count=$((service_count + 1))

                        # Write JSON entry
                        {
                            echo "    {"
                            echo "      \"app\": \"$app_name\","
                            echo "      \"installed\": \"$installed_date\","
                            echo "      \"serviceName\": \"$docker_service_name\","
                            echo "      \"serviceIP\": \"$docker_service_ip\","
                            echo "      \"name\": \"$port_service_name\","
                            echo "      \"externalPort\": $external_port,"
                            echo "      \"internalPort\": $internal_port,"
                            echo "      \"access\": \"$access_type\","
                            echo "      \"protocol\": \"$protocol\","
                            echo "      \"traefikManaged\": $traefik_managed,"
                            echo "      \"serverIP\": \"$server_ip\","
                            echo "      \"externalURL\": \"$external_url\","
                            echo "      \"internalURL\": \"$internal_url\","
                            echo "      \"links\": [${_links_json}"
                            echo "        ],"
                            echo "      \"buttonEnabled\": $button_enabled,"
                            echo "      \"buttonText\": \"$_primary_label\","
                            echo "      \"loginRequired\": $login_required"
                        } >> "$temp_file"
                    done <<< "$service_ports"
                fi
            done <<< "$docker_services"
        fi
    done <<< "$installed_apps"

    # Host-installed apps live outside the Docker network_resources DB.
    # Walk container configs for HOST_INSTALL=true + HOST_SERVICES, confirm
    # the package is present via dpkg, then emit one synthetic service entry
    # per systemd unit. Transport=systemd tells the frontend the logs come
    # from a host log file (tail -F over the bind-mounted /host/var/log).
    if [[ -d "$install_containers_dir" ]]; then
        for app_dir in "$install_containers_dir"/*; do
            [[ -d "$app_dir" ]] || continue
            local host_app_name=$(basename "$app_dir")
            [[ "$host_app_name" == "template" ]] && continue
            local cfg_file="$app_dir/$host_app_name.config"
            [[ -f "$cfg_file" ]] || continue
            local app_upper="${host_app_name^^}"; app_upper="${app_upper//-/_}"
            local host_install host_package host_services host_log_map
            host_install=$(grep -E "^CFG_${app_upper}_HOST_INSTALL=" "$cfg_file" | cut -d'=' -f2 | tr -d '\r"')
            [[ "$host_install" == "true" ]] || continue
            host_package=$(grep -E "^CFG_${app_upper}_HOST_PACKAGE=" "$cfg_file" | cut -d'=' -f2 | tr -d '\r"')
            host_services=$(grep -E "^CFG_${app_upper}_HOST_SERVICES=" "$cfg_file" | cut -d'=' -f2 | tr -d '\r"')
            host_log_map=$(grep -E "^CFG_${app_upper}_HOST_LOG_FILES=" "$cfg_file" | cut -d'=' -f2 | tr -d '\r"')
            [[ -n "$host_package" ]] || continue
            dpkg-query -W -f='${Status}' "$host_package" 2>/dev/null | grep -q "install ok installed" || continue
            [[ -n "$host_services" ]] || continue

            IFS=',' read -ra _units <<< "$host_services"
            for _unit in "${_units[@]}"; do
                _unit="${_unit//[[:space:]]/}"
                [[ -z "$_unit" ]] && continue
                local _status="inactive"
                systemctl is-active --quiet "$_unit" 2>/dev/null && _status="active"
                local _display_name="${_unit%.service}"
                # Pluck the matching log file for this unit from the
                # comma-list of <unit>|<path> pairs. Empty = no log file
                # known; backend will surface that to the client.
                local _log_host="" _log_container=""
                if [[ -n "$host_log_map" ]]; then
                    IFS=',' read -ra _pairs <<< "$host_log_map"
                    for _p in "${_pairs[@]}"; do
                        local _u="${_p%%|*}"
                        local _f="${_p#*|}"
                        if [[ "$_u" == "$_unit" ]]; then
                            _log_host="$_f"
                            # /var/log/X -> /host/var/log/X (matches the
                            # bind-mount in libreportal's compose).
                            _log_container="/host${_f}"
                            break
                        fi
                    done
                fi
                if [[ "$first_service" == false ]]; then
                    echo "    }," >> "$temp_file"
                fi
                first_service=false
                service_count=$((service_count + 1))
                {
                    echo "    {"
                    echo "      \"app\": \"$host_app_name\","
                    echo "      \"installed\": \"\","
                    echo "      \"serviceName\": \"$_display_name\","
                    echo "      \"name\": \"$_display_name\","
                    echo "      \"transport\": \"systemd\","
                    echo "      \"unit\": \"$_unit\","
                    echo "      \"status\": \"$_status\","
                    [[ -n "$_log_container" ]] && echo "      \"logFile\": \"$_log_container\","
                    echo "      \"buttonEnabled\": false,"
                    echo "      \"buttonText\": \"\","
                    echo "      \"loginRequired\": false"
                } >> "$temp_file"
            done
        done
    fi

    # Close last object and array
    if [[ "$first_service" == false ]]; then
        echo "    }" >> "$temp_file"
    else
        # No services found, write empty entry
        echo "  ]" >> "$temp_file"
        echo "}" >> "$temp_file"
        runFileWrite "$output_file" < "$temp_file"; rm -f "$temp_file"
        echo "No services found to generate"
    fi
    
    echo "  ]" >> "$temp_file"
    echo "}" >> "$temp_file"
    
    # Atomic move to final location
    if [ $? -eq 0 ]; then
        runFileWrite "$output_file" < "$temp_file"; rm -f "$temp_file"
        # Set proper ownership for web UI access
    else
        rm -f "$temp_file" 2>/dev/null
    fi
    
    # Output summary
    if [[ "$testing_mode" == "test" ]]; then
        echo "apps-services.json test generation completed!" 
        echo "Output file: $output_file" 
        echo "Total services generated: $service_count" 
    else
        isSuccessful "Generation of apps-services.json completed!"
    fi
}
