#!/usr/bin/env bash
#
# onx-default-address-set — Postfix catch-all (Default Address) yönetimi.
#
# Bir domain için tanımsız adreslere gelen mailler için 4 mode:
#   fail     — Postfix natural bounce (entry kaldır)
#   forward  — Hepsini bir adrese yönlendir
#   local    — Belirli bir mailbox'a teslim et
#   discard  — Sessizce sil (transport: discard)
#
# Backend: /etc/postfix/onoxsoft-mailing-lists (catch-all + alias entries shared).
# Discard için ek: /etc/postfix/onoxsoft-transport içinde discard sentinel.
#
# Stdin (JSON):
#   {"action":"set","domain":"example.com","mode":"fail|forward|local|discard",
#    "forward_to":"user@target.com" (forward için),
#    "local_mailbox":"info@example.com" (local için)}
#   {"action":"remove","domain":"example.com"}
#
# Stdout (JSON):
#   {"ok":true,"domain":"...","mode":"...","virtual_entry":"...","transport_entry":"...",
#    "postfix_reloaded":true}
#
# Exit: 0=ok, 1=invalid input, 2=preflight, 3=exec fail

set -uo pipefail

INPUT=$(cat 2>/dev/null || echo '{}')
ACTION=$(echo "$INPUT" | jq -r '.action // "set"')
DOMAIN=$(echo "$INPUT" | jq -r '.domain // ""')
MODE=$(echo "$INPUT" | jq -r '.mode // "fail"')
FORWARD_TO=$(echo "$INPUT" | jq -r '.forward_to // ""')
LOCAL_MAILBOX=$(echo "$INPUT" | jq -r '.local_mailbox // ""')

VIRTUAL_FILE="/etc/postfix/onoxsoft-mailing-lists"
TRANSPORT_FILE="/etc/postfix/onoxsoft-transport"
DISCARD_SENTINEL="discard-catchall@onoxsoft.local"

# Preflight
if ! command -v postmap >/dev/null 2>&1; then
    echo '{"error":"postmap (postfix) yüklü değil"}' >&2
    exit 2
fi

# Hash dosyalarını garantile
for f in "$VIRTUAL_FILE" "$TRANSPORT_FILE"; do
    if [[ ! -f "$f" ]]; then
        touch "$f"; chown root:root "$f"; chmod 644 "$f"
    fi
done

# Discard sentinel transport entry (idempotent — sadece bir kez ekle)
ensure_discard_transport() {
    local DOMAIN_ESC
    DOMAIN_ESC=$(echo "$DISCARD_SENTINEL" | sed 's/[]\/$*.^[]/\\&/g')
    if ! grep -qE "^${DOMAIN_ESC}[[:space:]]" "$TRANSPORT_FILE"; then
        echo "${DISCARD_SENTINEL}    discard:" >> "$TRANSPORT_FILE"
        postmap "$TRANSPORT_FILE" 2>/dev/null
    fi
}

reload_postfix() {
    local reloaded="false"
    if systemctl is-active --quiet postfix 2>/dev/null; then
        if systemctl reload postfix 2>/dev/null; then
            reloaded="true"
        fi
    fi
    echo "$reloaded"
}

# Domain validation
validate_domain() {
    [[ "$1" =~ ^[a-zA-Z0-9]([a-zA-Z0-9.-]*[a-zA-Z0-9])?\.[a-zA-Z]{2,}$ ]]
}

validate_email() {
    [[ "$1" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]
}

if [[ -z "$DOMAIN" ]] || ! validate_domain "$DOMAIN"; then
    jq -nc --arg d "$DOMAIN" '{ok:false,error:"geçersiz domain",domain:$d}' >&2
    exit 1
fi

CATCHALL="@${DOMAIN}"
CATCHALL_ESC=$(echo "@${DOMAIN}" | sed 's/[]\/$*.^[]/\\&/g')

case "$ACTION" in
    set)
        if [[ ! "$MODE" =~ ^(fail|forward|local|discard)$ ]]; then
            jq -nc --arg m "$MODE" '{ok:false,error:"geçersiz mode (fail|forward|local|discard)",mode:$m}' >&2
            exit 1
        fi

        # Mevcut entry'yi virtual_alias dosyasından sil
        sed -i "/^${CATCHALL_ESC}[[:space:]]/d" "$VIRTUAL_FILE"

        VIRTUAL_ENTRY=""
        TRANSPORT_ENTRY=""

        case "$MODE" in
            fail)
                # No entry — Postfix natural bounce
                ;;
            forward)
                if [[ -z "$FORWARD_TO" ]] || ! validate_email "$FORWARD_TO"; then
                    jq -nc --arg t "$FORWARD_TO" '{ok:false,error:"forward_to geçersiz email",forward_to:$t}' >&2
                    exit 1
                fi
                VIRTUAL_ENTRY="${CATCHALL}    ${FORWARD_TO}"
                echo "$VIRTUAL_ENTRY" >> "$VIRTUAL_FILE"
                ;;
            local)
                if [[ -z "$LOCAL_MAILBOX" ]] || ! validate_email "$LOCAL_MAILBOX"; then
                    jq -nc --arg l "$LOCAL_MAILBOX" '{ok:false,error:"local_mailbox geçersiz email",local_mailbox:$l}' >&2
                    exit 1
                fi
                VIRTUAL_ENTRY="${CATCHALL}    ${LOCAL_MAILBOX}"
                echo "$VIRTUAL_ENTRY" >> "$VIRTUAL_FILE"
                ;;
            discard)
                # Catch-all → discard sentinel; transport sentinel → discard:
                ensure_discard_transport
                VIRTUAL_ENTRY="${CATCHALL}    ${DISCARD_SENTINEL}"
                TRANSPORT_ENTRY="${DISCARD_SENTINEL}    discard:"
                echo "$VIRTUAL_ENTRY" >> "$VIRTUAL_FILE"
                ;;
        esac

        # Postmap rebuild
        if ! postmap "$VIRTUAL_FILE" 2>/dev/null; then
            jq -nc --arg f "$VIRTUAL_FILE" '{ok:false,error:"postmap virtual fail",file:$f}' >&2
            exit 3
        fi

        RELOADED=$(reload_postfix)

        jq -nc \
            --arg domain "$DOMAIN" \
            --arg mode "$MODE" \
            --arg ve "$VIRTUAL_ENTRY" \
            --arg te "$TRANSPORT_ENTRY" \
            --argjson reloaded "$RELOADED" \
            '{ok:true, action:"set", domain:$domain, mode:$mode, virtual_entry:$ve, transport_entry:$te, postfix_reloaded:$reloaded}'
        ;;

    remove)
        sed -i "/^${CATCHALL_ESC}[[:space:]]/d" "$VIRTUAL_FILE"

        if ! postmap "$VIRTUAL_FILE" 2>/dev/null; then
            jq -nc --arg f "$VIRTUAL_FILE" '{ok:false,error:"postmap virtual fail after remove",file:$f}' >&2
            exit 3
        fi

        RELOADED=$(reload_postfix)

        jq -nc --arg domain "$DOMAIN" --argjson reloaded "$RELOADED" \
            '{ok:true, action:"remove", domain:$domain, postfix_reloaded:$reloaded}'
        ;;

    list)
        # @domain entry'lerini virtual_alias'tan filtrele
        ENTRIES_JSON="["
        FIRST=1
        while IFS= read -r line; do
            [[ -z "$line" ]] && continue
            [[ "$line" =~ ^# ]] && continue
            [[ "$line" =~ ^@ ]] || continue  # sadece @domain entries
            ADDR=$(echo "$line" | awk '{print $1}')
            DEST=$(echo "$line" | awk '{$1=""; print $0}' | sed 's/^[[:space:]]*//')
            [[ $FIRST -eq 0 ]] && ENTRIES_JSON+=","
            FIRST=0
            ENTRIES_JSON+=$(jq -nc --arg a "$ADDR" --arg d "$DEST" '{catchall:$a,destination:$d}')
        done < "$VIRTUAL_FILE"
        ENTRIES_JSON+="]"

        jq -nc --argjson list "$ENTRIES_JSON" '{ok:true, action:"list", catchalls:$list}'
        ;;

    *)
        jq -nc --arg a "$ACTION" '{ok:false,error:"bilinmeyen action — set|remove|list",action:$a}' >&2
        exit 1
        ;;
esac

exit 0
