#!/bin/bash

# Category : Networking
# Description : AdGuard - DNS based Ad Blocking (c/u/s/r/i):

installAdguard()
{
    local config_variables="$1"

    if [[ "$adguard" == *[cCtTuUsSrRiI]* ]]; then
        dockerConfigSetupToContainer silent adguard;
        local app_name=$CFG_ADGUARD_APP_NAME
    	initializeAppVariables $app_name;
    fi

    if [[ "$adguard" == *[cC]* ]]; then
        editAppConfig $app_name;
    fi

    if [[ "$adguard" == *[uU]* ]]; then
        dockerUninstallApp $app_name;
    fi

    if [[ "$adguard" == *[sS]* ]]; then
        dockerComposeDown $app_name;
    fi

    if [[ "$adguard" == *[rR]* ]]; then
        dockerComposeRestart $app_name;
    fi

    if [[ "$adguard" == *[iI]* ]]; then
        isHeader "Install $app_name"

		((menu_number++))
        echo ""
        echo "---- $menu_number. Setting up install folder and config file for $app_name."
        echo ""

        dockerConfigSetupToContainer "loud" "$app_name" "install" "$config_variables";
        isSuccessful "Install folders and Config files have been setup for $app_name."

		((menu_number++))
        echo ""
        echo "---- $menu_number. Setting up the $app_name docker-compose.yml file."
        echo ""

        dockerComposeSetupFile $app_name;

        monitoringToggleAppConfig "$app_name" "docker-compose.yml";

		((menu_number++))
        echo ""
        echo "---- $menu_number. Updating file permissions before starting."
        echo ""

		fixPermissionsBeforeStart $app_name;

		((menu_number++))
        echo ""
        echo "---- $menu_number. Running the docker-compose.yml to install and start $app_name"
        echo ""

		dockerComposeUpdateAndStartApp $app_name install;

		((menu_number++))
        echo ""
        echo "---- $menu_number. Completing AdGuardHome initial setup automatically"
        echo ""

        # The legacy `$usedport1` variable is no longer populated by the
        # current install pipeline; the resolved host port is stored in the
        # PORTS_TAG_1 docker-compose tag (format `external:internal`). Pull
        # it from there so the curl + URL printout actually point somewhere.
        local adguard_compose_file="$containers_dir$app_name/docker-compose.yml"
        local adguard_port_pair
        adguard_port_pair=$(tagsManagerGetTagContent "$adguard_compose_file" "PORTS_TAG_1")
        local adguard_admin_port="${adguard_port_pair%%:*}"

        if [[ -n "$public_ip_v4" && -n "$adguard_admin_port" ]]; then
            echo "    External : http://$public_ip_v4:$adguard_admin_port/"
        fi
        if [[ -n "$host_setup" ]]; then
            echo "    Hostname : http://$host_setup/"
        fi
        echo ""

        # AdGuardHome ships a setup wizard that normally needs five clicks in a
        # browser before the daemon writes its config file. Same wizard is
        # exposed as an HTTP API (POST /control/install/configure), so we
        # drive it from here and skip the manual interaction. We pre-poll the
        # admin endpoint until the container is up, then send the form, then
        # let the existing post-install sed edits run against the freshly
        # written AdGuardHome.yaml.
        local adguard_setup_url="http://127.0.0.1:${adguard_admin_port}"
        local adguard_attempts=0
        local adguard_max_attempts=60
        while ((adguard_attempts < adguard_max_attempts)); do
            if curl -fsS -o /dev/null --max-time 2 "${adguard_setup_url}/control/status" 2>/dev/null \
               || curl -fsS -o /dev/null --max-time 2 "${adguard_setup_url}/control/install/get_addresses" 2>/dev/null; then
                break
            fi
            sleep 2
            ((adguard_attempts++))
        done

        if ((adguard_attempts >= adguard_max_attempts)); then
            isError "AdGuardHome admin endpoint did not respond on $adguard_setup_url within $((adguard_max_attempts * 2))s — open the URL and complete setup manually, then re-run the installer to apply the post-setup tweaks."
        else
            local adguard_user="${CFG_ADGUARD_USER:-admin}"
            local adguard_pass="${CFG_ADGUARD_PASSWORD:-}"
            if [[ -z "$adguard_pass" ]]; then
                adguard_pass=$(generateRandomPassword)
                updateConfigOption "CFG_ADGUARD_PASSWORD" "$adguard_pass" >/dev/null 2>&1 || true
                isNotice "Generated a random AdGuardHome admin password and saved it to CFG_ADGUARD_PASSWORD."
            fi

            # Internal container ports are fixed (3000 admin, 53 DNS); host
            # mapping is what `usedport1` etc. handle.
            local adguard_payload
            adguard_payload=$(cat <<JSON
{
  "web": { "ip": "0.0.0.0", "port": 3000, "autofix": false },
  "dns": { "ip": "0.0.0.0", "port": 53, "autofix": false },
  "username": "${adguard_user}",
  "password": "${adguard_pass}"
}
JSON
)
            if curl -fsS -X POST \
                 -H 'Content-Type: application/json' \
                 --data "$adguard_payload" \
                 --max-time 15 \
                 "${adguard_setup_url}/control/install/configure" >/dev/null 2>&1; then
                isSuccessful "AdGuardHome admin setup completed automatically (user: $adguard_user)."
            else
                # 422/403 here typically means setup was already done on a
                # previous install; the post-setup tweaks below are still
                # safe to run against the existing yaml.
                isNotice "AdGuardHome /control/install/configure rejected the request — assuming it's already configured. If this is a fresh install, complete setup manually at $adguard_setup_url."
            fi
            # Small breather so AdGuardHome finishes flushing AdGuardHome.yaml
            # to disk before the sed edits below touch it.
            #sleep 3
        fi

        #result=$(sudo sed -i "s/address: 0.0.0.0:80/address: 0.0.0.0:${usedport2}/g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
        #checkSuccess "Changing port 80 to $usedport2 for Admin Panel"

        #result=$(sudo sed -i "s/port: 53/port: ${usedport3}/g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
        #checkSuccess "Changing port 53 to $usedport3 for DNS Port"

        #result=$(sudo sed -i "s/port_https: 443/port_https: ${usedport4}/g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
        #checkSuccess "Changing port 443 to $usedport4 for DNS Port"

        #result=$(sudo sed -i "s/port_dns_over_tls: 853/port_dns_over_tls: ${usedport5}/g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
        #checkSuccess "Changing port 853 to $usedport5 for port_dns_over_tls"

        #result=$(sudo sed -i "s/port_dns_over_quic: 853/port_dns_over_quic: ${usedport5}/g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
        #checkSuccess "Changing port 853 to $usedport5 for port_dns_over_quic"

        # NOTE: We deliberately do *not* force `tls.enabled: true` here.
        # That section configures encrypted DNS (DoT/DoH/DoQ) and AdGuardHome
        # crash-loops on startup with `[fatal] creating dns server: parsing
        # tls key pair: tls: failed to find any PEM data in certificate input`
        # if `enabled: true` is set without a real certificate pair pointed
        # at by `certificate_path` / `private_key_path`. The admin user can
        # opt into encrypted DNS from Settings → Encryption once they've
        # provided a cert.

        if [[ $public == "true" ]]; then
            result=$(runFileOp sed -i "s|allow_unencrypted_doh: false|allow_unencrypted_doh: true|g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
            checkSuccess "Setting allow_unencrypted_doh to false for Traefik"
        fi

        result=$(runFileOp sed -i "s|anonymize_client_ip: false: false|anonymize_client_ip: true|g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
        checkSuccess "Setting anonymize_client_ip to true for privacy reasons"

        # Force the admin web bind back to 0.0.0.0:3000 inside the container.
        # The docker-compose mapping is `<host_port>:3000`, so the container
        # MUST listen on 3000 internally for the host port to reach it. After
        # the install API call AdGuardHome sometimes ends up bound to 0.0.0.0:80
        # (its build-time default) instead of the port we sent — which is
        # exactly what causes "unable to connect" on the host port.
        local adguard_yaml="$containers_dir$app_name/conf/AdGuardHome.yaml"
        if [[ -f "$adguard_yaml" ]]; then
            # New schema (v0.107+): single `address: 0.0.0.0:NN` line under `http:`.
            runFileOp sed -i 's|^\(\s*address:\s*\)0\.0\.0\.0:[0-9]\+|\10.0.0.0:3000|' "$adguard_yaml"
            # Old schema fallback: separate `bind_host:` / `bind_port:` keys.
            runFileOp sed -i 's|^\(\s*bind_host:\s*\).*|\10.0.0.0|' "$adguard_yaml"
            runFileOp sed -i 's|^\(\s*bind_port:\s*\)[0-9]\+|\13000|' "$adguard_yaml"
            checkSuccess "Pinned AdGuardHome admin bind to 0.0.0.0:3000 (matches the compose port mapping)."
        fi

        dockerComposeRestart "$app_name";

        # Health-check after the restart so the user finds out *here* if
        # AdGuardHome didn't come back up cleanly, rather than later when
        # they try to open the URL and just see "unable to connect".
        #
        # Drop `-f` and accept any HTTP status code: now that the admin
        # account is configured, `/control/status` returns 401 to an
        # unauthenticated request — which is fine, it means the server is
        # up and answering. We only care whether the connection succeeded
        # at all, not what the response body says. `-w '%{http_code}'`
        # gives us a 3-digit code on success and an empty string on a
        # connection failure / timeout.
        local adguard_health_attempts=0
        while ((adguard_health_attempts < 20)); do
            local adguard_health_code
            adguard_health_code=$(curl -sS -o /dev/null --max-time 2 \
                -w '%{http_code}' "${adguard_setup_url}/control/status" 2>/dev/null)
            if [[ "$adguard_health_code" =~ ^[1-5][0-9][0-9]$ ]]; then
                isSuccessful "AdGuardHome admin UI is reachable on $adguard_setup_url (HTTP $adguard_health_code)"
                break
            fi
            sleep 1
            ((adguard_health_attempts++))
        done
        if ((adguard_health_attempts >= 20)); then
            isError "AdGuardHome admin UI did not respond after restart on $adguard_setup_url. Check the container logs (\`docker logs adguard-service\`) and the conf/AdGuardHome.yaml bind address."
        fi

        ((menu_number++))
        echo ""
        echo "---- $menu_number. Running Application specific updates (if required)"
        echo ""

        appUpdateSpecifics $app_name;

		((menu_number++))
        echo ""
        echo "---- $menu_number. Running Headscale setup (if required)"
        echo ""

		setupHeadscale $app_name;

		((menu_number++))
		echo ""
        echo "---- $menu_number. Adding $app_name to the Apps Database table."
        echo ""

		databaseInstallApp $app_name;

        ((menu_number++))
        echo ""
        echo "---- $menu_number. Updating the WebUI config file."
        echo ""

        webuiContainerSetup $app_name install;

		((menu_number++))
		echo ""
		echo "---- $menu_number. Refreshing monitoring integration."
		echo ""

		monitoringRefreshAll;

		((menu_number++))
        echo ""
        echo "---- $menu_number. You can find $app_name files at $containers_dir$app_name"
        echo ""
        echo "    You can now navigate to your $app_name service using any of the options below : "
        echo ""

        # Same final-summary call shape as wireguard / vaultwarden. Pass the
        # admin user/password we just configured so the user sees the
        # credentials exactly once, at the end of the install.
        menuShowFinalMessages "$app_name" "${CFG_ADGUARD_USER:-admin}" "$CFG_ADGUARD_PASSWORD";

		menu_number=0
        #sleep 3s
        cd
    fi
    adguard=n
}
