#!/bin/bash
# ==============================================================================================
# ============================= Gitea SSH Setup ================================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Generates an SSH keypair for Gitea authentication and registers the public
# key with Gitea via the API. Run after the Gitea container is up — typically
# called during onboard setup or when re-keying a server.
#
# Idempotent: skips key generation if key already exists and skips registration
# if the same public key is already registered in Gitea. Use --force to
# regenerate and re-register.
#
# ==============================================================================================
# OPERATIONAL MODEL
# ==============================================================================================
#
#   1. Resolve Gitea API endpoint
#        Container running locally → use container IP + GITEA_HTTP_PORT
#        Container not local       → fall back to GITEA_DOMAIN
#
#   2. Validate GITEA_API_TOKEN is set
#        Token needed for API calls — create once in Gitea UI (see CONFIGURATION)
#
#   3. Generate SSH keypair (ed25519) at GITEA_SSH_KEY
#        Skipped if key already exists — use --force to regenerate
#
#   4. Check if public key already registered in Gitea
#        Compares fingerprints — skips POST if already registered
#
#   5. Register public key via POST /api/v1/user/keys
#        Title: "unraid-{host_id}" (e.g. unraid-host1)
#
#   6. Test SSH connection to Gitea
#        ssh -i GITEA_SSH_KEY -p SSH_PORT git@<gitea_host>
#        Success confirms key is accepted and auth works end-to-end
#
#   7. Write ~/.ssh/config Host entry for gitea
#        Skipped if entry already exists — use --force to overwrite
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Separate Key From the Rsync Key
#   Gitea authentication uses its own keypair rather than reusing the rsync automation key.
#   The two have different lifecycles and different blast radii — re-keying Gitea should
#   never break cross-host rsync, and revoking a partner's rsync access should not lock this
#   host out of its own repository.
#
# Idempotent Registration
#   Both generation and registration are skipped when already satisfied, and registration is
#   matched on the public key itself rather than on a label. A key registered under a
#   different title is still the same key, and re-registering it would leave duplicate
#   entries accumulating in Gitea on every re-run.
#
# Locate, Do Not Assume
#   The API endpoint is resolved at runtime — container-local first, then GITEA_DOMAIN. Same
#   reasoning as git_pull_execute.sh: Gitea's address changes with container restarts and
#   migrations, so hardcoding it guarantees an eventual break.
#
# Force Is Explicit
#   Regeneration invalidates the key already registered in Gitea, so it requires --force. A
#   bare re-run can never cost this host its repository access.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# Root Required
#   ssh-keygen and ~/.ssh/ operations require consistent permissions.
#
# Single Instance Lock
#   acquire_lock prevents concurrent runs.
#
# Token Validated Before Any API Call
#   If HOST*_GITEA_API_TOKEN is empty, exits with a clear message pointing to
#   Gitea Settings → Applications → Generate Token (scope: write:user).
#
# Key Never Clobbered Without --force
#   Existing key at GITEA_SSH_KEY is left untouched unless --force is passed.
#   Prevents accidentally invalidating working auth.
#
# Duplicate Registration Safe
#   Checks existing keys before POST — will not create duplicate entries in Gitea.
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# host*.conf
#
#   HOST*_GITEA_API_TOKEN
#       Personal access token for the Gitea user who owns the repository.
#       Create in Gitea: Settings → Applications → Generate Token → scope: write:user
#       Aliased by detect_hosts() → GITEA_API_TOKEN.
#
# master.conf
#
#   GITEA_CONTAINER         Container name for local IP detection (default: "Gitea")
#   GITEA_DOMAIN            Domain fallback if container is remote or not running
#   GITEA_SSH_KEY           Path for the SSH keypair (default: /root/.ssh/unraid_gitea)
#   SSH_PORT                Gitea SSH port (default: 221)
#   GITEA_HTTP_PORT         Gitea API port (default: 3000)
#
# ==============================================================================================
# STATE FILES
# ==============================================================================================
#
#   ~/.ssh/config           Updated with Host entry for gitea on successful setup
#   GITEA_SSH_KEY           Private key (permissions: 600)
#   GITEA_SSH_KEY.pub       Public key — registered with Gitea
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# gitea_ssh_setup.sh
#     Generate key if missing, register with Gitea, test connection.
#
# gitea_ssh_setup.sh --force
#     Regenerate key and re-register even if already set up.
#
# gitea_ssh_setup.sh --status
#     Show key state, fingerprint, registration status, SSH test. No changes.
#
# gitea_ssh_setup.sh --dry-run
#     Show what would be done without generating, registering, or writing files.
#
# gitea_ssh_setup.sh --log
#     Verbose output for each step.
#
# ==============================================================================================

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPTS_ROOT="$SCRIPT_DIR/.."

source "$SCRIPTS_ROOT/load_config.sh"

# ── Parse --force before parse_args ───────────────────────────────────────────────────────────
FORCE=false
FILTERED_ARGS=()

for arg in "$@"; do
    case "$arg" in
        --force) FORCE=true ;;
        *)       FILTERED_ARGS+=("$arg") ;;
    esac
done

parse_args "${FILTERED_ARGS[@]}"

# ==============================================================================================
# ━━━ Setup ━━━
# ==============================================================================================
if [[ "$EUID" -ne 0 ]]; then
    error "Must be run as root"
    exit 1
fi

platform_require_cmd \
    "/usr/local/emhttp/plugins/dynamix/scripts/notify" \
    "" "" \
    "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"

acquire_lock

detect_hosts

[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
[[ "$FORCE"   == true ]] && warn "FORCE mode — existing key and registration will be replaced"

log "Identity: $MY_ID ($LOCAL_SERVER_NAME)"
log "SSH key:  $GITEA_SSH_KEY"
log "SSH port: $SSH_PORT"

# ==============================================================================================
# ━━━ Resolve Gitea API Endpoint ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_GEAR Gitea API Endpoint ━━━"

GITEA_HOST=""
GITEA_API=""

# Try local container first
CONTAINER_IP=$(docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \
    "$GITEA_CONTAINER" 2>/dev/null | awk 'NF{print $1; exit}')

if [[ -n "$CONTAINER_IP" ]]; then
    GITEA_HOST="$CONTAINER_IP"
    GITEA_API="http://${CONTAINER_IP}:${GITEA_HTTP_PORT}/api/v1"
    log "Gitea container running locally: $CONTAINER_IP:$GITEA_HTTP_PORT"
elif [[ -n "$GITEA_DOMAIN" ]]; then
    GITEA_HOST="$GITEA_DOMAIN"
    GITEA_API="https://${GITEA_DOMAIN}/api/v1"
    warn "Gitea container not running locally — using domain: $GITEA_DOMAIN"
else
    error "Cannot resolve Gitea API endpoint"
    error "Gitea container ($GITEA_CONTAINER) is not running and GITEA_DOMAIN is not set in master.conf"
    error "Start the Gitea container first, or set GITEA_DOMAIN"
    exit 1
fi

log "API endpoint: $GITEA_API"

# ==============================================================================================
# ━━━ Validate API Token ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_SHIELD API Token ━━━"

# Alias: detect_hosts() maps HOST*_GITEA_API_TOKEN → GITEA_API_TOKEN
if [[ -z "${GITEA_API_TOKEN:-}" ]]; then
    error "HOST${MY_ID#HOST}_GITEA_API_TOKEN is not set in host${MY_ID#HOST}.conf"
    error ""
    error "Create a token in Gitea:"
    error "  1. Log into Gitea"
    error "  2. Top-right profile → Settings → Applications"
    error "  3. Generate New Token → name: unraid-setup → scope: write:user"
    error "  4. Copy the token (shown once) → paste into HOST${MY_ID#HOST}_GITEA_API_TOKEN in host${MY_ID#HOST}.conf"
    exit 1
fi

# Test token against the API
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
    -H "Authorization: token $GITEA_API_TOKEN" \
    "$GITEA_API/user" 2>/dev/null)

if [[ "$HTTP_CODE" == "200" ]]; then
    echo "API token valid ✅"
elif [[ "$HTTP_CODE" == "401" ]]; then
    error "API token rejected (HTTP 401) — token may be expired or have wrong scope"
    error "Regenerate the token in Gitea: Settings → Applications → Generate Token → scope: write:user"
    exit 1
else
    error "Gitea API not reachable (HTTP $HTTP_CODE) at $GITEA_API"
    error "Check that the Gitea container is healthy and the port is correct (GITEA_HTTP_PORT=$GITEA_HTTP_PORT)"
    exit 1
fi

# ── Status mode exits here after full state display ───────────────────────────────────────────
if [[ "$SHOW_STATUS" == true ]]; then
    echo ""
    echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
    echo "$ICON_HOST  Identity:     $MY_ID ($LOCAL_SERVER_NAME)"
    echo "$ICON_GEAR  API endpoint: $GITEA_API"
    echo ""

    if [[ -f "${GITEA_SSH_KEY}.pub" ]]; then
        FINGERPRINT=$(ssh-keygen -lf "${GITEA_SSH_KEY}.pub" 2>/dev/null | awk '{print $2}')
        echo "  $ICON_GEAR  Key file:    ${GITEA_SSH_KEY}.pub"
        echo "  $ICON_GEAR  Fingerprint: $FINGERPRINT"

        # Check registration
        REGISTERED_KEYS=$(curl -s \
            -H "Authorization: token $GITEA_API_TOKEN" \
            "$GITEA_API/user/keys" 2>/dev/null)
        LOCAL_PUB=$(awk '{print $1" "$2}' "${GITEA_SSH_KEY}.pub")
        if echo "$REGISTERED_KEYS" | grep -qF "$LOCAL_PUB"; then
            echo "  $ICON_GEAR  Registered:  YES ✅"
        else
            echo "  $ICON_GEAR  Registered:  NO — run without --status to register"
        fi
    else
        echo "  $ICON_GEAR  Key file: NOT FOUND at $GITEA_SSH_KEY"
    fi

    # SSH connection test
    echo ""
    SSH_OUTPUT=$(ssh -i "$GITEA_SSH_KEY" -p "$SSH_PORT" \
        -o StrictHostKeyChecking=no -o ConnectTimeout=5 \
        git@"$GITEA_HOST" 2>&1 || true)
    if echo "$SSH_OUTPUT" | grep -q "successfully authenticated"; then
        echo "  $ICON_DONE  SSH test: connected ✅"
        echo "  $ICON_INFO  $(echo "$SSH_OUTPUT" | grep "successfully authenticated")"
    else
        echo "  $ICON_ERROR SSH test: failed"
        echo "  $ICON_INFO  $SSH_OUTPUT"
    fi

    echo "━━━━━━━━━━━━━━━━━━━━━━━"
    exit 0
fi

START=$(date +%s)
SETUP_SUCCESS=true

# ==============================================================================================
# ━━━ Generate SSH Keypair ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_GEAR SSH Keypair ━━━"

KEY_TITLE="unraid-$(echo "$MY_ID" | tr '[:upper:]' '[:lower:]')"
KEY_COMMENT="$KEY_TITLE@$(hostname)"

if [[ -f "$GITEA_SSH_KEY" ]] && [[ "$FORCE" == false ]]; then
    FINGERPRINT=$(ssh-keygen -lf "${GITEA_SSH_KEY}.pub" 2>/dev/null | awk '{print $2}')
    log "Key already exists — skipping generation (use --force to regenerate)"
    log "  Path:        $GITEA_SSH_KEY"
    log "  Fingerprint: $FINGERPRINT"
else
    if [[ "$DRY_RUN" == true ]]; then
        warn "DRY RUN — would generate ed25519 key at $GITEA_SSH_KEY"
        warn "DRY RUN — key comment: $KEY_COMMENT"
    else
        mkdir -p "$(dirname "$GITEA_SSH_KEY")"
        chmod 700 "$(dirname "$GITEA_SSH_KEY")"

        # Remove existing if --force
        [[ "$FORCE" == true ]] && rm -f "$GITEA_SSH_KEY" "${GITEA_SSH_KEY}.pub"

        if ssh-keygen -t ed25519 -f "$GITEA_SSH_KEY" -C "$KEY_COMMENT" -N "" -q; then
            chmod 600 "$GITEA_SSH_KEY"
            chmod 644 "${GITEA_SSH_KEY}.pub"
            FINGERPRINT=$(ssh-keygen -lf "${GITEA_SSH_KEY}.pub" 2>/dev/null | awk '{print $2}')
            warn "SSH key generated ✅"
            log "  Path:        $GITEA_SSH_KEY"
            log "  Fingerprint: $FINGERPRINT"
        else
            error "Failed to generate SSH key"
            SETUP_SUCCESS=false
        fi
    fi
fi

# ==============================================================================================
# ━━━ Register Public Key with Gitea ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_LINK Register Key with Gitea ━━━"

if [[ "$DRY_RUN" == true ]]; then
    warn "DRY RUN — would POST public key to $GITEA_API/user/keys"
    warn "DRY RUN — key title: $KEY_TITLE"
elif [[ "$SETUP_SUCCESS" == true ]]; then
    LOCAL_PUB=$(cat "${GITEA_SSH_KEY}.pub")
    LOCAL_PUB_SHORT=$(awk '{print $1" "$2}' "${GITEA_SSH_KEY}.pub")

    # Check if already registered
    REGISTERED_KEYS=$(curl -s \
        -H "Authorization: token $GITEA_API_TOKEN" \
        "$GITEA_API/user/keys" 2>/dev/null)

    if [[ "$FORCE" == false ]] && echo "$REGISTERED_KEYS" | grep -qF "$LOCAL_PUB_SHORT"; then
        log "Public key already registered in Gitea — skipping (use --force to re-register)"
    else
        # If --force, delete the existing key with this title first
        if [[ "$FORCE" == true ]]; then
            EXISTING_ID=$(echo "$REGISTERED_KEYS" | \
                python3 -c "import json,sys; keys=json.load(sys.stdin); [print(k['id']) for k in keys if k.get('title','')=='$KEY_TITLE']" 2>/dev/null)
            if [[ -n "$EXISTING_ID" ]]; then
                curl -s -X DELETE \
                    -H "Authorization: token $GITEA_API_TOKEN" \
                    "$GITEA_API/user/keys/$EXISTING_ID" >/dev/null 2>&1
                log "Removed existing key '$KEY_TITLE' (ID: $EXISTING_ID)"
            fi
        fi

        RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \
            -H "Authorization: token $GITEA_API_TOKEN" \
            -H "Content-Type: application/json" \
            -d "{\"key\":$(echo "$LOCAL_PUB" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().strip()))'),\"read_only\":false,\"title\":\"$KEY_TITLE\"}" \
            "$GITEA_API/user/keys" 2>/dev/null)

        HTTP_CODE=$(echo "$RESPONSE" | tail -1)
        BODY=$(echo "$RESPONSE" | head -n -1)

        if [[ "$HTTP_CODE" == "201" ]]; then
            KEY_ID=$(echo "$BODY" | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])" 2>/dev/null)
            warn "Public key registered in Gitea ✅ (ID: $KEY_ID, title: $KEY_TITLE)"
        elif [[ "$HTTP_CODE" == "422" ]]; then
            warn "Key already registered under a different title — skipping"
            log "Response: $BODY"
        else
            error "Failed to register key (HTTP $HTTP_CODE)"
            log "Response: $BODY"
            SETUP_SUCCESS=false
        fi
    fi
fi

# ==============================================================================================
# ━━━ SSH Connection Test ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_GEAR SSH Connection Test ━━━"

if [[ "$DRY_RUN" == true ]]; then
    warn "DRY RUN — would test: ssh -i $GITEA_SSH_KEY -p $SSH_PORT git@$GITEA_HOST"
else
    SSH_OUTPUT=$(ssh -i "$GITEA_SSH_KEY" -p "$SSH_PORT" \
        -o StrictHostKeyChecking=no \
        -o ConnectTimeout=10 \
        git@"$GITEA_HOST" 2>&1 || true)

    if echo "$SSH_OUTPUT" | grep -q "successfully authenticated"; then
        AUTHED_AS=$(echo "$SSH_OUTPUT" | grep "successfully authenticated" | sed 's/.*authenticated as //')
        warn "SSH connection successful ✅ — authenticated as $AUTHED_AS"
    else
        error "SSH connection test failed"
        log "Output: $SSH_OUTPUT"
        warn "Key may not yet be active — Gitea sometimes takes a moment to pick up new keys"
        warn "Run --status in 30 seconds to recheck"
        SETUP_SUCCESS=false
    fi
fi

# ==============================================================================================
# ━━━ SSH Config Entry ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_GEAR SSH Config Entry ━━━"

SSH_CONFIG="$HOME/.ssh/config"
ENTRY_MARKER="Host gitea-${MY_ID,,}"

if [[ "$DRY_RUN" == true ]]; then
    warn "DRY RUN — would add Host entry to $SSH_CONFIG:"
    warn "  Host gitea-${MY_ID,,}"
    warn "    HostName $GITEA_HOST"
    warn "    Port $SSH_PORT"
    warn "    User git"
    warn "    IdentityFile $GITEA_SSH_KEY"
elif grep -qF "$ENTRY_MARKER" "$SSH_CONFIG" 2>/dev/null && [[ "$FORCE" == false ]]; then
    log "SSH config entry already exists — skipping (use --force to update)"
else
    if [[ "$FORCE" == true ]]; then
        # Remove old entry block if present
        if grep -qF "$ENTRY_MARKER" "$SSH_CONFIG" 2>/dev/null; then
            python3 - "$SSH_CONFIG" "$ENTRY_MARKER" <<'PYEOF'
import sys
path, marker = sys.argv[1], sys.argv[2]
with open(path) as f:
    lines = f.readlines()
out, skip = [], False
for line in lines:
    if line.strip() == marker:
        skip = True
    elif skip and line.startswith('Host '):
        skip = False
    if not skip:
        out.append(line)
with open(path, 'w') as f:
    f.writelines(out)
PYEOF
            log "Removed old SSH config entry"
        fi
    fi

    mkdir -p "$(dirname "$SSH_CONFIG")"
    {
        echo ""
        echo "Host gitea-${MY_ID,,}"
        echo "    HostName $GITEA_HOST"
        echo "    Port $SSH_PORT"
        echo "    User git"
        echo "    IdentityFile $GITEA_SSH_KEY"
    } >> "$SSH_CONFIG"
    chmod 600 "$SSH_CONFIG"
    echo "SSH config entry added: Host gitea-${MY_ID,,} ✅"
fi

END=$(date +%s)

# ==============================================================================================
# ━━━ Summary ━━━
# ==============================================================================================
echo ""
echo "━━━━━ $ICON_SUMMARY GITEA SSH SETUP SUMMARY ━━━━━"
echo "$ICON_HOST  Identity:     $MY_ID ($LOCAL_SERVER_NAME)"
echo "$ICON_GEAR  Key:          $GITEA_SSH_KEY"
echo "$ICON_LINK  Gitea:        $GITEA_HOST:$SSH_PORT"
echo "$ICON_GEAR  Key title:    $KEY_TITLE"
echo "$ICON_TIME  Duration:     $(format_duration $(( END - START )))"
echo ""

if [[ "$DRY_RUN" == true ]]; then
    warn "DRY RUN — no changes made"
elif [[ "$SETUP_SUCCESS" == true ]]; then
    echo "$ICON_DONE Status: done ✅"
    echo ""
    echo "Git remote push:"
    echo "  GIT_SSH_COMMAND='ssh -i $GITEA_SSH_KEY -p $SSH_PORT' git push"
else
    echo "$ICON_ERROR Status: SETUP HAD ERRORS — check output above"
    notify "Gitea SSH setup errors on $(hostname) ($MY_ID)" \
        "Gitea SSH Setup" "warning"
    exit 1
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
