#!/usr/bin/env bash
# =============================================================================
# onx-pdns-txt-record-add — Idempotent TXT record upsert + SOA bump + notify
# v92 Agent 4
# =============================================================================
#
# PanelDnsPublisher tarafından çağrılır — panel'in kendi domain'i için
# DKIM/SPF/DMARC TXT kayıtlarını PowerDNS gmysql backend'ine atomik yazar.
#
# Idempotency stratejisi:
#   - replace=true (default): aynı (name, type=TXT) tüm record'ları SİL, yenisini insert
#   - replace=false: aynı (name, type, content) zaten varsa SKIP, yoksa insert
#
# Stdin JSON:
#   {
#     "zone":    "onoxsoft.com.tr",                  // REQUIRED
#     "name":    "default._domainkey",               // REQUIRED (relative veya FQDN)
#     "value":   "v=DKIM1; k=rsa; p=MIIBIjAN...",    // REQUIRED (max 8192 char)
#     "ttl":     3600,                               // default: 3600
#     "replace": true                                // default: true
#   }
#
#   Not: "name" relative ise zone otomatik append edilir:
#     "default._domainkey" + zone "onoxsoft.com.tr"
#         => "default._domainkey.onoxsoft.com.tr"
#   Apex için "@" veya zone'un kendisi ("onoxsoft.com.tr") kabul edilir.
#
# Stdout JSON:
#   {
#     "ok": true, "zone": "...", "name": "...", "type": "TXT",
#     "value": "...", "replaced": true, "previous_count": 1,
#     "soa_old": "2026052701", "soa_new": "2026052702",
#     "notified": true
#   }
#
# Exit codes:
#   0 = ok
#   1 = invalid input
#   2 = preflight fail (pdns DB unreachable, zone yok)
#   3 = execution fail (DB write hatası)
# =============================================================================

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=_lib/common.sh
source "${SCRIPT_DIR}/_lib/common.sh"

require_root
require_cmd mysql
require_cmd jq
onx_json_input

# ─── Input parse + validate ──────────────────────────────────────────────────
ZONE="$(onx_json_field zone)"
NAME="$(onx_json_field name)"
VALUE="$(onx_json_field value)"
TTL="$(onx_json_field ttl '3600')"
REPLACE="$(onx_json_get_bool "$INPUT" replace 'true')"

[[ -z "$ZONE" ]]  && onx_die 1 "zone zorunlu"
[[ -z "$NAME" ]]  && onx_die 1 "name zorunlu"
[[ -z "$VALUE" ]] && onx_die 1 "value zorunlu"
onx_validate_domain "$ZONE"
[[ "$TTL" =~ ^[0-9]+$ ]] || onx_die 1 "Geçersiz ttl: '${TTL}'"
[[ ${#VALUE} -le 8192 ]] || onx_die 1 "value çok uzun (max 8192 char): ${#VALUE}"

ZONE_LOWER="${ZONE,,}"

# FQDN normalization
if [[ "$NAME" == "@" || "$NAME" == "$ZONE_LOWER" ]]; then
    FQDN="$ZONE_LOWER"
elif [[ "$NAME" == *".${ZONE_LOWER}" || "$NAME" == "$ZONE_LOWER" ]]; then
    FQDN="${NAME,,}"
else
    FQDN="${NAME,,}.${ZONE_LOWER}"
fi

# RFC 1035 TXT value — chunks of 255 + outer quoting. PowerDNS auth otomatik
# chunk/quote ekler ama content kolonu PEM-safe olmalı. Tek satır + quote olmadan
# yaz; bind-style quote ekleme: 255+ karakterler için outer "..." ekle.
# Pratik: PowerDNS records.content kolonuna TIRNAKSIZ yazılır (PDNS internal
# chunking yapar). Sadece backslash + tek tırnak escape — content INSERT'inde
# SQL injection önlemi.
VALUE_SQL="${VALUE//\\/\\\\}"
VALUE_SQL="${VALUE_SQL//\'/\\\'}"

onx_log "pdns-txt-record-add: zone=${ZONE_LOWER} name=${FQDN} replace=${REPLACE} ttl=${TTL} value_len=${#VALUE}"

# ─── Preflight: PDNS DB ──────────────────────────────────────────────────────
PING_RESULT="$(mysql_exec "${ONX_PDNS_DB}" "SELECT 1;" 2>&1)"
if ! echo "$PING_RESULT" | grep -q '^1$'; then
    onx_die 2 "PowerDNS DB erişilemiyor (db=${ONX_PDNS_DB}): ${PING_RESULT}"
fi

# ─── Zone lookup ─────────────────────────────────────────────────────────────
DOMAIN_ID="$(mysql_exec "${ONX_PDNS_DB}" \
    "SELECT id FROM domains WHERE name='${ZONE_LOWER}' LIMIT 1;" 2>/dev/null | tail -1)"

if [[ -z "$DOMAIN_ID" || ! "$DOMAIN_ID" =~ ^[0-9]+$ ]]; then
    onx_die 2 "Zone PowerDNS'te bulunamadı: ${ZONE_LOWER} (önce pdns-zone-create veya zone-add çalıştır)"
fi

# ─── Mevcut TXT (aynı name) sorgula ──────────────────────────────────────────
PREVIOUS_COUNT="$(mysql_exec "${ONX_PDNS_DB}" \
    "SELECT COUNT(*) FROM records WHERE domain_id=${DOMAIN_ID} AND name='${FQDN}' AND type='TXT';" \
    2>/dev/null | tail -1)"
PREVIOUS_COUNT="${PREVIOUS_COUNT:-0}"

REPLACED=false
SKIPPED=false

if [[ "$REPLACE" == "true" ]]; then
    # ── Replace mode: aynı name+type TÜM record'ları sil, yenisini insert ──
    if [[ "$PREVIOUS_COUNT" -gt 0 ]]; then
        mysql_exec "${ONX_PDNS_DB}" \
            "DELETE FROM records WHERE domain_id=${DOMAIN_ID} AND name='${FQDN}' AND type='TXT';" \
            || onx_die 3 "TXT record DELETE başarısız"
        REPLACED=true
    fi

    mysql_exec_stdin "${ONX_PDNS_DB}" <<INS \
        || onx_die 3 "TXT record INSERT başarısız"
INSERT INTO records (domain_id, name, type, content, ttl, prio, disabled, auth)
VALUES (${DOMAIN_ID}, '${FQDN}', 'TXT', '${VALUE_SQL}', ${TTL}, 0, 0, 1);
INS
else
    # ── Idempotent mode: aynı content varsa skip, yoksa append ──
    EXISTING="$(mysql_exec "${ONX_PDNS_DB}" \
        "SELECT id FROM records WHERE domain_id=${DOMAIN_ID} AND name='${FQDN}' AND type='TXT' AND content='${VALUE_SQL}' LIMIT 1;" \
        2>/dev/null | tail -1)"
    if [[ -n "$EXISTING" && "$EXISTING" =~ ^[0-9]+$ ]]; then
        SKIPPED=true
    else
        mysql_exec_stdin "${ONX_PDNS_DB}" <<INS \
            || onx_die 3 "TXT record INSERT başarısız"
INSERT INTO records (domain_id, name, type, content, ttl, prio, disabled, auth)
VALUES (${DOMAIN_ID}, '${FQDN}', 'TXT', '${VALUE_SQL}', ${TTL}, 0, 0, 1);
INS
    fi
fi

# ─── SOA serial bump (skip değilse) ──────────────────────────────────────────
# feedback_onoxsoft_pdns_soa_regex.md: MariaDB REGEXP_REPLACE backref `\\1`
# bash heredoc'unda `$1` literal kalıyor → zone SERVFAIL veriyor. Bu yüzden
# SOA'yı READ + awk parse + RECONSTRUCT + UPDATE pattern'ı (v87 zone-create
# ile aynı) kullanıyoruz — regex sub YOK.
SOA_OLD=""
SOA_NEW=""

if [[ "$SKIPPED" != "true" ]]; then
    SOA_ROW="$(mysql_exec "${ONX_PDNS_DB}" \
        "SELECT id, content FROM records WHERE domain_id=${DOMAIN_ID} AND type='SOA' LIMIT 1;" \
        2>/dev/null | tail -1)"

    if [[ -n "$SOA_ROW" ]]; then
        SOA_ID="$(echo "$SOA_ROW" | awk '{print $1}')"
        SOA_CONTENT="$(echo "$SOA_ROW" | cut -f2-)"  # mysql --batch tab-separator

        if [[ -n "$SOA_ID" && "$SOA_ID" =~ ^[0-9]+$ && -n "$SOA_CONTENT" ]]; then
            SOA_NS="$(echo "$SOA_CONTENT"     | awk '{print $1}')"
            SOA_ADMIN="$(echo "$SOA_CONTENT"  | awk '{print $2}')"
            SOA_OLD="$(echo "$SOA_CONTENT"    | awk '{print $3}')"
            SOA_REFRESH="$(echo "$SOA_CONTENT"| awk '{print $4}')"
            SOA_RETRY="$(echo "$SOA_CONTENT"  | awk '{print $5}')"
            SOA_EXPIRE="$(echo "$SOA_CONTENT" | awk '{print $6}')"
            SOA_MINTTL="$(echo "$SOA_CONTENT" | awk '{print $7}')"

            # Sane defaults eksik field için
            [[ -z "$SOA_NS" ]]      && SOA_NS="ns3.onoxsoft.com.tr"
            [[ -z "$SOA_ADMIN" ]]   && SOA_ADMIN="hostmaster.${ZONE_LOWER}"
            [[ -z "$SOA_REFRESH" ]] && SOA_REFRESH="7200"
            [[ -z "$SOA_RETRY" ]]   && SOA_RETRY="3600"
            [[ -z "$SOA_EXPIRE" ]]  && SOA_EXPIRE="1209600"
            [[ -z "$SOA_MINTTL" ]]  && SOA_MINTTL="3600"

            # YYYYMMDDxx — eski serial bugünse +1 bump, değilse yeni gün başla
            TODAY_PREFIX="$(date +%Y%m%d)"
            if [[ "$SOA_OLD" =~ ^${TODAY_PREFIX}([0-9]{2})$ ]]; then
                OLD_INC="${BASH_REMATCH[1]}"
                # Leading-zero kaldır (08 → 8, 8+1 = 9 → 09)
                NEW_INC=$((10#$OLD_INC + 1))
                if [[ $NEW_INC -gt 99 ]]; then NEW_INC=99; fi
                SOA_NEW="${TODAY_PREFIX}$(printf '%02d' "$NEW_INC")"
            else
                SOA_NEW="${TODAY_PREFIX}01"
            fi

            NEW_SOA_CONTENT="${SOA_NS} ${SOA_ADMIN} ${SOA_NEW} ${SOA_REFRESH} ${SOA_RETRY} ${SOA_EXPIRE} ${SOA_MINTTL}"

            mysql_exec "${ONX_PDNS_DB}" \
                "UPDATE records SET content='${NEW_SOA_CONTENT}' WHERE id=${SOA_ID};" \
                || onx_die 3 "SOA UPDATE başarısız"

            # domains.notified_serial sync (slave AXFR için)
            mysql_exec "${ONX_PDNS_DB}" \
                "UPDATE domains SET notified_serial=${SOA_NEW}, last_check=UNIX_TIMESTAMP() WHERE id=${DOMAIN_ID};" \
                2>/dev/null || true
        fi
    fi
fi

# ─── pdns_control notify (Master mode için; Native'de no-op) ─────────────────
NOTIFIED=false
if command -v pdns_control >/dev/null 2>&1; then
    if pdns_control notify "${ZONE_LOWER}" >/dev/null 2>&1; then
        NOTIFIED=true
    fi
fi

# ─── Output ──────────────────────────────────────────────────────────────────
jq -nc \
    --arg z "${ZONE_LOWER}" \
    --arg n "${FQDN}" \
    --arg v "${VALUE}" \
    --arg so "${SOA_OLD}" \
    --arg sn "${SOA_NEW}" \
    --argjson pc "${PREVIOUS_COUNT}" \
    --argjson rp "${REPLACED}" \
    --argjson sk "${SKIPPED}" \
    --argjson nt "${NOTIFIED}" \
    '{
        ok: true,
        zone: $z,
        name: $n,
        type: "TXT",
        value: $v,
        replaced: $rp,
        skipped: $sk,
        previous_count: $pc,
        soa_old: (if $so == "" then null else $so end),
        soa_new: (if $sn == "" then null else $sn end),
        notified: $nt
    }'

exit 0
