#!/bin/bash

# ==============================================================================
# CONFIGURATIONS AND LOCATED FILES
# ==============================================================================
SETTINGS_FILE="/var/efw/ddns/settings"
LOG_FILE="/var/log/messages"
CRON_DIR="/etc/cron.d"
LAST_CHANGE_FILE="/var/log/ddns_last_change"

# Captures the ID passed as a parameter (e.g., 1, 2, 3... "force" or "cron")
TARGET_ID="$1"

# ------------------------------------------------------------------------------
# FUNCTION TO GENERATE OR REMOVE THE CRON FILE WITH THE SERVICE NAME
# ------------------------------------------------------------------------------
generate_independent_cron_files() {
    # Silently removes any old scheduling
    /bin/rm -f $CRON_DIR/ddns-update-* 2>/dev/null || true

    # 'sed' removes all carriage return characters (\r) from the file before 'while' reads it
    local line_id=0
    while IFS=',' read -r interface provider var1 var2 var3 var4 var5 var6 var7 var8
    do
        ((line_id++))
        
        # Surgical cleanup of spaces
        interface=$(/bin/echo "$interface" | /usr/bin/tr -d ' ')
        provider=$(/bin/echo "$provider" | /usr/bin/tr -d ' ')
        status=$(/bin/echo "$var7" | /usr/bin/tr -d ' ')
        interval=$(/bin/echo "$var8" | /usr/bin/tr -d ' ')

        [ -z "$interface" ] && continue
        [[ "$interface" == \#* ]] && continue
        
        # Adjusted 'tr' to correctly substitute dots with underscores
        local clean_provider=$(/bin/echo "$provider" | /usr/bin/tr '.')
        clean_provider=$(/bin/echo "$provider" | /usr/bin/tr '.' '_')
        
        # If for some reason the cleanup fails, sets a safe default name
        if [ -z "$clean_provider" ]; then
            clean_provider="service_$line_id"
        fi
        
        local INDIVIDUAL_CRON="$CRON_DIR/ddns-update-$clean_provider"

        # If status is NOT "on", ensures its file does not exist and skips
        if [ "$status" != "on" ]; then
            /bin/rm -f "$INDIVIDUAL_CRON" 2>/dev/null || true
            continue
        fi

        # Adjusts default interval case it is empty
        if [ -z "$interval" ] || ! [[ "$interval" =~ ^[0-9]+$ ]]; then
            interval=60
        fi

        # Creation of the file header using cat
        /bin/cat > "$INDIVIDUAL_CRON" <<EOF
# INDEPENDENT DDNS SCHEDULER - $provider (ID $line_id)
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin

EOF

        # Defines whether to use the default minute scheduler or the multi-line second conveyor
        if [ "$interval" -ge 60 ]; then
            local minutes=$((interval / 60))
            /bin/echo "*/$minutes * * * * /usr/local/bin/ddns-clients $line_id >/dev/null 2>&1" >> "$INDIVIDUAL_CRON"
        else
            # Writes the first command (second 00)
            /bin/echo "* * * * * /usr/local/bin/ddns-clients $line_id >/dev/null 2>&1" >> "$INDIVIDUAL_CRON"
            
            # Writes the next executions in separate lines (multi-line) in the background
            local accumulated_sleep=0
            while [ $((accumulated_sleep + interval)) -lt 60 ]
            do
                accumulated_sleep=$((accumulated_sleep + interval))
                /bin/echo "* * * * * (sleep $accumulated_sleep ; /usr/local/bin/ddns-clients $line_id >/dev/null 2>&1) &" >> "$INDIVIDUAL_CRON"
            done
        fi

        # Applies secure permission required by the cron daemon
        /bin/chmod 644 "$INDIVIDUAL_CRON" 2>/dev/null
        
    # ATTENTION HERE: 'sed' cleans the file at runtime for the while loop scope
    done < <(/bin/sed 's/\r//g' "$SETTINGS_FILE")

    # Restarts fcron service to refresh the general table
    /etc/init.d/fcron restart >/dev/null 2>&1 || true
}

# ------------------------------------------------------------------------------
# FUNCTION TO PROCESS A SPECIFIC INTERFACE/PROVIDER
# ------------------------------------------------------------------------------
process_update() {
    local RUN_IFACE="$1"
    local RUN_PROVIDER="$2"
    local RUN_VAR1="$3"
    local RUN_VAR2="$4"
    local RUN_VAR5="$5"
    local RUN_VAR6="$6"

    local CURRENT_IP=""
    # Formato de data idêntico ao syslog
    local LOG_PREFIX=$(/bin/date "+%b %d %H:%M:%S openfw ddns[$$]:")

    # --- 1ª FASE: Duas tentativas no ifconfig.me usando a interface ---
    if [ -z "$CURRENT_IP" ]; then CURRENT_IP=$(/usr/bin/curl -4 -s --interface "$RUN_IFACE" https://ifconfig.me); fi
    if [ -z "$CURRENT_IP" ]; then CURRENT_IP=$(/usr/bin/curl -4 -s --interface "$RUN_IFACE" https://ifconfig.me); fi

    # Se falhou no ifconfig.me, gera log de aviso
    if [ -z "$CURRENT_IP" ]; then
        /bin/echo "$LOG_PREFIX [WARNING] [$RUN_IFACE] Failed to connect to ifconfig.me. Trying fallback provider." >> "$LOG_FILE"
    fi

    # --- 2ª FASE: Duas tentativas no icanhazip.com usando a interface ---
    if [ -z "$CURRENT_IP" ]; then CURRENT_IP=$(/usr/bin/curl -4 -s --interface "$RUN_IFACE" https://icanhazip.com | /usr/bin/tr -d '\r\n '); fi
    if [ -z "$CURRENT_IP" ]; then CURRENT_IP=$(/usr/bin/curl -4 -s --interface "$RUN_IFACE" https://icanhazip.com | /usr/bin/tr -d '\r\n '); fi

    # Se também falhou no icanhazip.com, gera log informando o uso do link default
    if [ -z "$CURRENT_IP" ]; then
        /bin/echo "$LOG_PREFIX [WARNING] [$RUN_IFACE] Failed to connect to icanhazip.com. Switching to default route (no interface restriction)." >> "$LOG_FILE"
    fi

    # --- 3ª FASE (FALLBACK): Se os 4 testes falharam, roda no modo padrão (sem interface) ---
    if [ -z "$CURRENT_IP" ]; then
        CURRENT_IP=$(/usr/bin/curl -4 -s https://ifconfig.me)
        
        if [ -z "$CURRENT_IP" ]; then
            CURRENT_IP=$(/usr/bin/curl -4 -s https://icanhazip.com | /usr/bin/tr -d '\r\n ')
        fi
        
        # Desativa o parâmetro de interface para as requisições de atualização da API dos providers
        local IFACE_PARAM=""
    else
        local IFACE_PARAM="--interface $RUN_IFACE"
    fi

    if [ -z "$CURRENT_IP" ] ; then
        /bin/echo "$LOG_PREFIX [ERROR] Could not obtain IP for interface $RUN_IFACE (even via default route)" >> "$LOG_FILE"
        return
    fi

    local CACHE_FILE="/tmp/ddns_cache_${RUN_IFACE}_${RUN_PROVIDER}"
    local OLD_IP=""
    [ -f "$CACHE_FILE" ] && OLD_IP=$(/bin/cat "$CACHE_FILE")

    if [ "$CURRENT_IP" == "$OLD_IP" ]; then
        return
    fi

    # --------------------------------------------------------------------------
    # GRAVAÇÃO MULTI-INTERFACE NO LOG DA WEB
    # --------------------------------------------------------------------------
    local log_date=$(/bin/date '+%b %d %H:%M:%S')
    local nova_linha_metadata="LAST_UPDATE=\"$log_date\" INTERFACE=\"$RUN_IFACE\" PROVIDER=\"$RUN_PROVIDER\" OLD_IP=\"$OLD_IP\" NEW_IP=\"$CURRENT_IP\""

    /bin/touch "$LAST_CHANGE_FILE"
    if /bin/grep -q "INTERFACE=\"$RUN_IFACE\"" "$LAST_CHANGE_FILE" && /bin/grep -q "PROVIDER=\"$RUN_PROVIDER\"" "$LAST_CHANGE_FILE"; then
        /bin/sed -i "/INTERFACE=\"$RUN_IFACE\"/ { /PROVIDER=\"$RUN_PROVIDER\"/ d; }" "$LAST_CHANGE_FILE"
        /bin/echo "$nova_linha_metadata" >> "$LAST_CHANGE_FILE"
    else
        /bin/echo "$nova_linha_metadata" >> "$LAST_CHANGE_FILE"
    fi
    # --------------------------------------------------------------------------

    /bin/echo "$LOG_PREFIX [NOTICE] [$RUN_IFACE] IP changed from $OLD_IP to $CURRENT_IP. Updating providers." >> "$LOG_FILE"

    case "$RUN_PROVIDER" in
        "cloudflare")
            local ZONE_ID=$(/bin/echo "$RUN_VAR1" | /usr/bin/tr -d '\r\n ')
            local SUBDOMAIN=$(/bin/echo "$RUN_VAR2" | /usr/bin/tr -d '\r\n ')
            local TOKEN=$(/bin/echo "$RUN_VAR6" | /usr/bin/tr -d '\r\n ')
            
            local RECORD_ID=$(/usr/bin/curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=A&name=$SUBDOMAIN" \
                -H "Authorization: Bearer $TOKEN" \
                -H "Content-Type: application/json" | /bin/grep -o '"id":"[^"]*' | /usr/bin/head -n 1 | /usr/bin/cut -d'"' -f4)

            if [ -z "$RECORD_ID" ]; then
                /bin/echo "$LOG_PREFIX [ERROR] [$RUN_IFACE] Record ID not found for $SUBDOMAIN." >> "$LOG_FILE"
                return
            fi
            
            local PAYLOAD="{\"type\":\"A\",\"name\":\"$SUBDOMAIN\",\"content\":\"$CURRENT_IP\",\"ttl\":120,\"proxied\":false}"
            local RESPONSE=$(/usr/bin/curl -4 -s $IFACE_PARAM -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
                -H "Authorization: Bearer $TOKEN" \
                -H "Content-Type: application/json" \
                --data "$PAYLOAD")
            
            if /bin/echo "$RESPONSE" | /bin/grep -q '"success":true'; then
                /bin/echo "$LOG_PREFIX [SUCCESS] [$RUN_IFACE] Cloudflare ($SUBDOMAIN) updated to $CURRENT_IP." >> "$LOG_FILE"
                /bin/echo "$CURRENT_IP" > "$CACHE_FILE"
            else
                /bin/echo "$LOG_PREFIX [ERROR] [$RUN_IFACE] Cloudflare failure: $RESPONSE" >> "$LOG_FILE"
            fi
            ;;

        "duckdns.org"|"duckdns")
            local DOMAIN=$(/bin/echo "$RUN_VAR1" | /usr/bin/tr -d '\r\n ')
            local TOKEN=$(/bin/echo "$RUN_VAR6" | /usr/bin/tr -d '\r\n ')
            
            local RESPONSE=$(/usr/bin/curl -4 -s $IFACE_PARAM "https://www.duckdns.org/update?domains=$DOMAIN&token=$TOKEN&ip=$CURRENT_IP")
            
            if [ "$RESPONSE" = "OK" ]; then
                /bin/echo "$LOG_PREFIX [SUCCESS] [$RUN_IFACE] DuckDNS ($DOMAIN.duckdns.org) updated to $CURRENT_IP." >> "$LOG_FILE"
                /bin/echo "$CURRENT_IP" > "$CACHE_FILE"
            else
                /bin/echo "$LOG_PREFIX [ERROR] [$RUN_IFACE] DuckDNS failure: $RESPONSE" >> "$LOG_FILE"
            fi
            ;;

        "no-ip.com"|"noip")
            local HOSTNAME=$(/bin/echo "$RUN_VAR1" | /usr/bin/tr -d '\r\n ')
            local DOMAIN=$(/bin/echo "$RUN_VAR2" | /usr/bin/tr -d '\r\n ')
            local USERNAME=$(/bin/echo "$RUN_VAR5" | /usr/bin/tr -d '\r\n ')
            local PASSWORD=$(/bin/echo "$RUN_VAR6" | /usr/bin/tr -d '\r\n ')
            
            local FULL_HOST="$HOSTNAME"
            if [ -n "$DOMAIN" ] && [ "$DOMAIN" != "-" ]; then
                FULL_HOST="${HOSTNAME}.${DOMAIN}"
            fi

            local RESPONSE=$(/usr/bin/curl -4 -s $IFACE_PARAM -u "$USERNAME:$PASSWORD" -A "Endian DDNS Client/1.0 vitor.b@gmail.com" "https://dynupdate.no-ip.com/nic/update?hostname=$FULL_HOST&myip=$CURRENT_IP")
            
            if /bin/echo "$RESPONSE" | /bin/grep -qE "nochg|good"; then
                /bin/echo "$LOG_PREFIX [SUCCESS] [$RUN_IFACE] No-IP ($FULL_HOST) updated to $CURRENT_IP." >> "$LOG_FILE"
                /bin/echo "$CURRENT_IP" > "$CACHE_FILE"
            else
                /bin/echo "$LOG_PREFIX [ERROR] [$RUN_IFACE] No-IP failure: $RESPONSE" >> "$LOG_FILE"
            fi
            ;;

        "freemyip.com"|"freemyip")
            local FULL_DOMAIN=$(/bin/echo "$RUN_VAR1" | /usr/bin/tr -d '\r\n ')
            local TOKEN=$(/bin/echo "$RUN_VAR6" | /usr/bin/tr -d '\r\n ')

            local RESPONSE=$(/usr/bin/curl -4 -s $IFACE_PARAM "https://freemyip.com/update?token=$TOKEN&domain=$FULL_DOMAIN&myip=$CURRENT_IP")
            
            if /bin/echo "$RESPONSE" | /bin/grep -q "OK"; then
                /bin/echo "$LOG_PREFIX [SUCCESS] [$RUN_IFACE] FreeMyIP ($FULL_DOMAIN) updated to $CURRENT_IP." >> "$LOG_FILE"
                /bin/echo "$CURRENT_IP" > "$CACHE_FILE"
            else
                /bin/echo "$LOG_PREFIX [ERROR] [$RUN_IFACE] FreeMyIP failure: $RESPONSE" >> "$LOG_FILE"
            fi
            ;;
    esac
}

# ==============================================================================
# SCRIPT EXECUTION FLOW
# ==============================================================================

if [ "$TARGET_ID" == "cron" ]; then
    if [ -f "$SETTINGS_FILE" ]; then
        generate_independent_cron_files
    fi
    exit 0
fi

if [ "$TARGET_ID" == "force" ]; then
    /bin/rm -f /tmp/ddns_cache_*
    TARGET_ID=""
    if [ -f "$SETTINGS_FILE" ]; then
        generate_independent_cron_files
    fi
fi

if [ -z "$TARGET_ID" ]; then
    if [ -f "$SETTINGS_FILE" ]; then
        generate_independent_cron_files
    fi
fi

if [ -f "$SETTINGS_FILE" ]; then
    line_id=0
    while IFS=',' read -r interface provider var1 var2 var3 var4 var5 var6 var7 var8
    do
        ((line_id++))
        
        if [ -n "$TARGET_ID" ] && [ "$line_id" -ne "$TARGET_ID" ]; then
            continue
        fi

        interface=$(/bin/echo "$interface" | /usr/bin/tr -d '\r\n ')
        provider=$(/bin/echo "$provider" | /usr/bin/tr -d '\r\n ')
        var1=$(/bin/echo "$var1" | /usr/bin/tr -d '\r\n ')
        var2=$(/bin/echo "$var2" | /usr/bin/tr -d '\r\n ')
        var5=$(/bin/echo "$var5" | /usr/bin/tr -d '\r\n ')
        var6=$(/bin/echo "$var6" | /usr/bin/tr -d '\r\n ')
        status=$(/bin/echo "$var7" | /usr/bin/tr -d '\r\n ')
        
        if [ "$status" == "on" ] && [ -n "$interface" ]; then
            process_update "$interface" "$provider" "$var1" "$var2" "$var5" "$var6"
        fi
    done < "$SETTINGS_FILE"
fi