#!/bin/bash

# Rebuild firewall rules from database (single source of truth)
firewallRebuildFromDatabase()
{
    if [[ $CFG_REQUIREMENT_DATABASE != "true" ]]; then
        isNotice "Database not available, cannot rebuild firewall"
    fi
    
    # Determine firewall type
    local firewall_type=""
    if [[ "$CFG_DOCKER_INSTALL_TYPE" == "rooted" ]] && command -v ufw-docker &> /dev/null; then
        firewall_type="ufw-docker"
        isSuccessful "Using UFW-Docker for Rooted install"
    elif command -v ufw &> /dev/null; then
        firewall_type="ufw"
        isSuccessful "Using UFW for Rootless"
    else
        isNotice "No firewall management available"
    fi
    
    # Check if Traefik is installed and running
    local traefik_available=false
    if [[ -d "$containers_dir/traefik" ]] && runFileOp docker ps --format "table {{.Names}}" | grep -q "traefik"; then
        traefik_available=true
        isSuccessful "Traefik detected - respecting traefik_managed flags"
    else
        isSuccessful "Traefik not detected - all ports will get firewall access"
    fi
    
    # Get all ports that need firewall rules from database. nr3 pulls the
    # `port` row (resource_value = ext:int:access:proto) so we can honor the
    # per-port access field — `private` ports are published to the host but
    # must never get a UFW allow rule.
    local ports_query="SELECT DISTINCT nr1.app_name, nr1.resource_value, nr1.service_name, nr2.resource_value as traefik_managed, nr3.resource_value as port_mapping
                      FROM network_resources nr1
                      LEFT JOIN network_resources nr2 ON nr2.app_name = nr1.app_name
                           AND nr2.service_name = nr1.service_name
                           AND nr2.resource_type = 'traefik_managed'
                      LEFT JOIN network_resources nr3 ON nr3.app_name = nr1.app_name
                           AND nr3.service_name = nr1.service_name
                           AND nr3.resource_type = 'port'
                      WHERE nr1.resource_type = 'port_tag_external'
                      AND nr1.status = 'active'"

    local total_added=0
    local total_failed=0

    # IFS='|' (not tr-to-space) so an empty middle column — e.g. a NULL
    # traefik_managed — doesn't shift the remaining fields.
    while IFS='|' read -r app_name port_value service_name traefik_managed port_mapping; do
        if [[ -n "$app_name" && -n "$port_value" ]]; then
            # Verify this app actually exists
            if [[ -d "$containers_dir/$app_name" ]]; then
                local port_spec="${port_value}/tcp"

                # Per-port fields from the mapping ext:int:access:proto.
                # access (3rd) gates whether to open it; internal (2nd) is the
                # container port ufw-docker needs (see ufw-docker branch below).
                local port_access="" port_internal=""
                if [[ -n "$port_mapping" ]]; then
                    port_access="$(echo "$port_mapping" | cut -d':' -f3)"
                    port_internal="$(echo "$port_mapping" | cut -d':' -f2)"
                fi

                # Determine if this port needs firewall access
                local needs_firewall=false

                if [[ "$traefik_managed" == "false" ]]; then
                    needs_firewall=true
                elif [[ -z "$traefik_managed" ]]; then
                    # NULL value means traefik_managed not set
                    if [[ "$traefik_available" == "false" ]]; then
                        # Traefik not available, so open port
                        needs_firewall=true
                    fi
                else
                    if [[ "$traefik_available" == "false" ]]; then
                        needs_firewall=true
                    fi
                fi

                # `private` access overrides everything — the port stays bound
                # on the host but is never allowed through the firewall.
                if [[ "$port_access" == "private" ]]; then
                    needs_firewall=false
                fi

                if [[ "$needs_firewall" == "true" ]]; then
                    local result rc
                    if [[ "$firewall_type" == "ufw-docker" ]]; then
                        # Rooted: ufw-docker matches the container's INTERNAL
                        # (published) port, not the host-facing external port.
                        # Feeding it the external port fails with "cannot find
                        # the published port", so no route rule is created and
                        # the container is unreachable from other hosts — host
                        # access still works via docker-proxy, which masks it.
                        result=$(runSystem ufw-docker allow "${service_name:-$app_name}" "${port_internal:-$port_value}/tcp" 2>&1)
                    else
                        # Rootless: container ports are published on the host,
                        # so the external port is the one to open.
                        result=$(runSystem ufw allow "$port_spec" comment "LibrePortal" 2>&1)
                    fi
                    # Capture rc separately: `local x=$(...)` would clobber $?
                    # with the exit of `local`, hiding the command's real status.
                    rc=$?

                    if [[ $rc -eq 0 ]]; then
                        ((total_added++))
                        isSuccessful "Added: $app_name → $port_spec"
                    else
                        ((total_failed++))
                        isNotice "Failed: $app_name → $port_spec ($result)"
                    fi
                else
                    if [[ "$port_access" == "private" ]]; then
                        isNotice "Skipping: $app_name → $port_spec (access=private — host-bound, not firewalled open)"
                    else
                        isNotice "Skipping: $app_name → $port_spec (Traefik managed or no firewall needed)"
                    fi
                fi
            else
                isNotice "Skipped: $app_name (app not found)"
            fi
        fi
    done <<< "$(sqlite3 "$docker_dir/$db_file" "$ports_query" 2>/dev/null)"

    isSuccessful "Firewall rebuild complete: $total_added added, $total_failed failed"
}
