#!/usr/bin/env bash
#═══════════════════════════════════════════════════════════════════════════════
# setup-conduit.sh — Conduit Matrix Server на Ubuntu (Oracle Cloud Always Free)
#
# Що робить:
#   Крок 1: бінарник Conduit (статичний musl з GitLab або свій)
#   Крок 2: системний користувач, /etc/matrix-conduit/conduit.toml, служба systemd
#   Крок 3: nginx як зворотний проксі + сертифікат Let's Encrypt + порти в iptables
#
# Використання:
#   chmod +x setup-conduit.sh
#   sudo ./setup-conduit.sh
#
# Інструкція: https://softomania.org.ua/100001-yak-pidnyati-sviy-matrix-server-conduit-bezkoshtovno-na-oracle-cloud.html
# Версія: 2026-10-12 · Conduit v0.10.15 (перевірено на Oracle A1 aarch64, Ubuntu 22.04)
#═══════════════════════════════════════════════════════════════════════════════
set -euo pipefail

RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'

log_info()  { echo -e "${GREEN}[INFO]${NC}  $*"; }
log_warn()  { echo -e "${YELLOW}[WARN]${NC}  $*"; }
log_error() { echo -e "${RED}[ERROR]${NC} $*"; }
log_step()  { echo -e "\n${CYAN}${BOLD}══════ $* ══════${NC}\n"; }

# ─── Налаштування ─────────────────────────────────────────────────────────────
CONDUIT_VERSION="v0.10.15"
CONDUIT_BIN="/usr/local/bin/matrix-conduit"
CONDUIT_CONFIG_DIR="/etc/matrix-conduit"
CONDUIT_CONFIG="${CONDUIT_CONFIG_DIR}/conduit.toml"
CONDUIT_DATA="/var/lib/matrix-conduit"
CONDUIT_USER="conduit"
CONDUIT_PORT="6167"

DOMAIN=""
SERVER_NAME=""
REG_TOKEN=""
INSTALL_METHOD=""   # download | upload
BINARY_PATH=""
ENABLE_FEDERATION="false"

# ─── Допоміжні ────────────────────────────────────────────────────────────────
check_root() {
    if [[ $EUID -ne 0 ]]; then
        log_error "Запускайте від root: sudo ./setup-conduit.sh"
        exit 1
    fi
}

check_ubuntu() {
    if ! grep -qi 'ubuntu' /etc/os-release 2>/dev/null; then
        log_warn "Скрипт перевірено на Ubuntu 22.04/24.04. На іншій системі кроки можуть відрізнятися."
        read -rp "Продовжити? [y/N]: " ans
        [[ "$ans" =~ ^[Yy]$ ]] || exit 0
    fi
}

ask() {
    local prompt="$1" default="${2:-}" result
    if [[ -n "$default" ]]; then
        read -rp "$(echo -e "${BOLD}${prompt}${NC} [${default}]: ")" result
        echo "${result:-$default}"
    else
        while true; do
            read -rp "$(echo -e "${BOLD}${prompt}${NC}: ")" result
            [[ -n "$result" ]] && break
            log_warn "Значення не може бути порожнім"
        done
        echo "$result"
    fi
}

ask_yn() {
    local prompt="$1" result
    read -rp "$(echo -e "${BOLD}${prompt}${NC} [y/N]: ")" result
    [[ "$result" =~ ^[Yy]$ ]]
}

get_arch() {
    case "$(uname -m)" in
        x86_64)  echo "x86_64-unknown-linux-musl" ;;
        aarch64) echo "aarch64-unknown-linux-musl" ;;
        *)
            log_error "Непідтримувана архітектура: $(uname -m) (потрібно x86_64 або aarch64)"
            exit 1
            ;;
    esac
}

gen_token() { openssl rand -hex 24; }

# ═══════════════════════════════════════════════════════════════════════════════
# Крок 0: параметри
# ═══════════════════════════════════════════════════════════════════════════════
collect_params() {
    log_step "Крок 0: Параметри встановлення"

    echo -e "${BOLD}Спосіб встановлення Conduit:${NC}"
    echo "  1) Завантажити готовий статичний бінарник з GitLab (рекомендовано)"
    echo "  2) Використати свій бінарник (зібраний на іншій машині)"
    echo ""
    local method
    while true; do
        read -rp "Оберіть [1/2]: " method
        case "$method" in
            1) INSTALL_METHOD="download"; break ;;
            2) INSTALL_METHOD="upload";   break ;;
            *) log_warn "Введіть 1 або 2" ;;
        esac
    done

    if [[ "$INSTALL_METHOD" == "upload" ]]; then
        while true; do
            BINARY_PATH=$(ask "Шлях до бінарника Conduit" "/home/ubuntu/conduit")
            if [[ -f "$BINARY_PATH" ]] && file "$BINARY_PATH" | grep -q "ELF"; then
                log_info "Бінарник знайдено: $(file "$BINARY_PATH" | cut -d: -f2)"
                break
            fi
            log_error "Файлу немає або це не ELF-бінарник: ${BINARY_PATH}"
        done
    fi

    echo ""
    echo -e "${BOLD}Домен:${NC} стане server_name — частиною кожного ID (@user:домен). Змінити після першого запуску НЕ МОЖНА."
    echo "  Приклад: matrix.example.org"
    DOMAIN=$(ask "Домен (FQDN)" "")
    SERVER_NAME="$DOMAIN"

    echo ""
    REG_TOKEN=$(ask "Registration token (пароль для реєстрації нових користувачів)" "$(gen_token)")

    echo ""
    if ask_yn "Увімкнути федерацію (спілкування з matrix.org та іншими серверами)?"; then
        ENABLE_FEDERATION="true"
    fi

    echo ""
    log_step "Зведення"
    echo -e "  Спосіб:            ${CYAN}${INSTALL_METHOD}${NC}"
    [[ "$INSTALL_METHOD" == "upload" ]] && echo -e "  Бінарник:          ${CYAN}${BINARY_PATH}${NC}"
    echo -e "  Домен/server_name: ${CYAN}${DOMAIN}${NC}"
    echo -e "  Registration:      ${CYAN}${REG_TOKEN}${NC}"
    echo -e "  Федерація:         ${CYAN}${ENABLE_FEDERATION}${NC}"
    echo ""
    if ! ask_yn "Усе правильно? Починаємо встановлення?"; then
        log_info "Скасовано."
        exit 0
    fi
}

# ═══════════════════════════════════════════════════════════════════════════════
# Крок 1: бінарник
# ═══════════════════════════════════════════════════════════════════════════════
install_conduit_binary() {
    log_step "Крок 1: Бінарник Conduit"

    if [[ "$INSTALL_METHOD" == "download" ]]; then
        local arch url
        arch=$(get_arch)
        url="https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/master/raw/${arch}?job=artifacts"
        log_info "Архітектура: $(uname -m) → ${arch}"
        log_info "Завантажую Conduit (гілка master = стабільна, musl)…"
        if ! wget -q --show-progress -O /tmp/matrix-conduit "$url"; then
            log_error "Не вдалося завантажити. Спробуйте вручну:"
            log_error "  wget -O /tmp/matrix-conduit '${url}'"
            exit 1
        fi
        install -m 0755 /tmp/matrix-conduit "$CONDUIT_BIN"
        rm -f /tmp/matrix-conduit
    else
        log_info "Копіюю ${BINARY_PATH} → ${CONDUIT_BIN}"
        install -m 0755 "$BINARY_PATH" "$CONDUIT_BIN"
    fi

    if "$CONDUIT_BIN" --help &>/dev/null; then
        log_info "✅ Бінарник працює: ${CONDUIT_BIN}"
    else
        log_error "Бінарник не запускається (несумісна архітектура або glibc). Спробуйте musl-версію (варіант 1)."
        exit 1
    fi
}

# ═══════════════════════════════════════════════════════════════════════════════
# Крок 2: користувач, конфіг, systemd
# ═══════════════════════════════════════════════════════════════════════════════
setup_conduit_system() {
    log_step "Крок 2: Налаштування системи"

    if id "$CONDUIT_USER" &>/dev/null; then
        log_info "Користувач ${CONDUIT_USER} вже є"
    else
        log_info "Створюю системного користувача ${CONDUIT_USER}…"
        adduser --system "$CONDUIT_USER" --group --disabled-login --no-create-home
    fi

    mkdir -p "$CONDUIT_CONFIG_DIR" "$CONDUIT_DATA"
    chown -R "${CONDUIT_USER}:${CONDUIT_USER}" "$CONDUIT_DATA"
    chmod 700 "$CONDUIT_DATA"

    log_info "Пишу конфіг: ${CONDUIT_CONFIG}"
    cat > "$CONDUIT_CONFIG" <<TOML
# Conduit ${CONDUIT_VERSION} — згенеровано setup-conduit.sh $(date -u +"%Y-%m-%d %H:%M UTC")
# Документація: https://docs.conduit.rs/configuration.html

[global]

# Ім’я сервера — частина кожного ID. НЕ МОЖНА міняти після першого запуску!
server_name = "${SERVER_NAME}"

# База даних (rocksdb рекомендовано; альтернатива — sqlite)
database_backend = "rocksdb"
database_path = "${CONDUIT_DATA}/"

# Мережа: слухаємо лише локально, назовні дивиться nginx
port = ${CONDUIT_PORT}
address = "127.0.0.1"

# Максимальний розмір запиту (завантаження файлів), байт
max_request_size = 20_000_000

# Реєстрація лише з токеном — без нього на відкритий сервер набіжать боти
allow_registration = true
registration_token = "${REG_TOKEN}"

# Федерація з іншими Matrix-серверами
allow_federation = ${ENABLE_FEDERATION}
trusted_servers = ["matrix.org"]

allow_check_for_updates = true

# Делегування: клієнти й федерація йдуть на цей домен через 443 (порт 8448 не обов’язковий)
[global.well_known]
client = "https://${DOMAIN}"
server = "${DOMAIN}:443"
TOML
    chown root:root "$CONDUIT_CONFIG_DIR"
    chmod 755 "$CONDUIT_CONFIG_DIR"
    chmod 644 "$CONDUIT_CONFIG"

    log_info "Створюю службу systemd…"
    cat > /etc/systemd/system/conduit.service <<UNIT
[Unit]
Description=Conduit Matrix Server (${CONDUIT_VERSION})
After=network.target

[Service]
Environment="CONDUIT_CONFIG=${CONDUIT_CONFIG}"
User=${CONDUIT_USER}
Group=${CONDUIT_USER}
Restart=always
RestartSec=3
ExecStart=${CONDUIT_BIN}
LimitNOFILE=65536

# Захист: файлова система лише для читання, крім каталогу даних
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=${CONDUIT_DATA}
PrivateTmp=true
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target
UNIT

    systemctl daemon-reload
    log_info "Запускаю Conduit…"
    systemctl enable --now conduit
    sleep 2

    if systemctl is-active --quiet conduit; then
        log_info "✅ Conduit запущено"
        if curl -sf "http://127.0.0.1:${CONDUIT_PORT}/_matrix/client/versions" > /dev/null; then
            log_info "✅ API відповідає на http://127.0.0.1:${CONDUIT_PORT}"
        else
            log_warn "API ще не відповідає — перший запуск ініціалізує базу, зачекайте 5–10 с"
        fi
    else
        log_error "Conduit не запустився. Логи: journalctl -u conduit -n 30 --no-pager"
        exit 1
    fi
}

# ═══════════════════════════════════════════════════════════════════════════════
# Крок 3: nginx + Let's Encrypt + iptables
# ═══════════════════════════════════════════════════════════════════════════════
setup_nginx_ssl() {
    log_step "Крок 3: nginx + Let's Encrypt"

    log_info "Встановлюю nginx і certbot…"
    apt-get update -qq
    apt-get install -y -qq nginx certbot python3-certbot-nginx > /dev/null

    # Oracle-образи мають власні правила iptables з фінальним reject — дозволи вставляємо ВИЩЕ (на позицію 6).
    # UFW на Ubuntu-образі Oracle не вмикайте: машина може не завантажитися (docs.oracle.com, Essential Firewall Rules).
    log_info "Відкриваю порти в iptables…"
    local ports=(80 443)
    [[ "$ENABLE_FEDERATION" == "true" ]] && ports+=(8448)
    for port in "${ports[@]}"; do
        if ! iptables -C INPUT -m state --state NEW -p tcp --dport "$port" -j ACCEPT 2>/dev/null; then
            iptables -I INPUT 6 -m state --state NEW -p tcp --dport "$port" -j ACCEPT
            log_info "  відкрито ${port}/tcp"
        else
            log_info "  ${port}/tcp уже відкрито"
        fi
    done
    if ! command -v netfilter-persistent &>/dev/null; then
        DEBIAN_FRONTEND=noninteractive apt-get install -y -qq iptables-persistent > /dev/null 2>&1 || true
    fi
    netfilter-persistent save 2>/dev/null || log_warn "netfilter-persistent save не вдалося — правила не переживуть перезавантаження"

    log_info "Перевіряю DNS для ${DOMAIN}…"
    local resolved_ip my_ip
    resolved_ip=$(dig +short "$DOMAIN" A 2>/dev/null | head -1)
    my_ip=$(curl -s --max-time 10 https://ifconfig.me 2>/dev/null || echo "unknown")

    if [[ -z "$resolved_ip" ]]; then
        log_error "Домен ${DOMAIN} не резолвиться. A-запис має вказувати на цей сервер (зовнішній IP: ${my_ip})."
        log_warn "Без DNS certbot не видасть сертифікат."
        if ! ask_yn "Пропустити HTTPS і налаштувати поки що лише HTTP-проксі?"; then
            exit 1
        fi
        setup_nginx_http_only
        return
    fi

    log_info "  DNS: ${DOMAIN} → ${resolved_ip}; цей сервер: ${my_ip}"
    if [[ "$resolved_ip" != "$my_ip" ]]; then
        log_warn "IP не збігаються — certbot може не спрацювати."
        ask_yn "Продовжити все одно?" || exit 1
    fi

    rm -f /etc/nginx/sites-enabled/default
    cat > /etc/nginx/sites-available/matrix <<NGINX_HTTP
# Тимчасовий конфіг для перевірки Let's Encrypt
server {
    listen 80;
    listen [::]:80;
    server_name ${DOMAIN};

    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }

    location / {
        return 301 https://\$host\$request_uri;
    }
}
NGINX_HTTP
    ln -sf /etc/nginx/sites-available/matrix /etc/nginx/sites-enabled/
    nginx -t && systemctl reload nginx

    local email
    email=$(ask "E-mail для Let's Encrypt (сповіщення про закінчення сертифіката)" "admin@${DOMAIN}")

    log_info "Отримую сертифікат Let's Encrypt…"
    if certbot certonly --nginx -d "$DOMAIN" --non-interactive --agree-tos -m "$email" --no-eff-email; then
        log_info "✅ Сертифікат отримано"
    else
        log_error "Certbot не зміг отримати сертифікат (DNS вказує сюди? порт 80 відкрито в Security List Oracle?)"
        setup_nginx_http_only
        return
    fi

    local federation_block=""
    if [[ "$ENABLE_FEDERATION" == "true" ]]; then
        federation_block=$(cat <<FEDBLOCK

# Федерація на 8448 (необов’язково: .well-known делегує її на 443)
server {
    listen 8448 ssl http2;
    listen [::]:8448 ssl http2;
    server_name ${DOMAIN};

    ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;

    client_max_body_size 20M;

    location / {
        proxy_pass http://127.0.0.1:${CONDUIT_PORT};
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_buffering off;
        proxy_read_timeout 300;
    }
}
FEDBLOCK
)
    fi

    log_info "Пишу повний конфіг nginx…"
    cat > /etc/nginx/sites-available/matrix <<NGINX_FULL
# nginx для Conduit Matrix Server — згенеровано setup-conduit.sh $(date -u +"%Y-%m-%d")

# HTTP → HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name ${DOMAIN};

    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }

    location / {
        return 301 https://\$host\$request_uri;
    }
}

# HTTPS: client-server API + .well-known
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name ${DOMAIN};

    ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;

    # = max_request_size у conduit.toml
    client_max_body_size 20M;

    # Matrix API чутливий до подвійних слешів
    merge_slashes off;

    location /_matrix/ {
        proxy_pass http://127.0.0.1:${CONDUIT_PORT};
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
        proxy_buffering off;
        proxy_read_timeout 300;
    }

    # Conduit сам віддає /.well-known/matrix/*
    location /.well-known/matrix/ {
        proxy_pass http://127.0.0.1:${CONDUIT_PORT};
        proxy_set_header Host \$host;
    }

    location / {
        return 200 '{"server":"conduit","status":"ok"}';
        add_header Content-Type application/json;
    }
}
${federation_block}
NGINX_FULL
    nginx -t && systemctl reload nginx
    log_info "✅ nginx налаштовано з HTTPS"
}

setup_nginx_http_only() {
    log_warn "Налаштовую HTTP-проксі без сертифіката…"
    cat > /etc/nginx/sites-available/matrix <<NGINX_PLAIN
server {
    listen 80;
    listen [::]:80;
    server_name ${DOMAIN};

    client_max_body_size 20M;
    merge_slashes off;

    location /_matrix/ {
        proxy_pass http://127.0.0.1:${CONDUIT_PORT};
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_buffering off;
        proxy_read_timeout 300;
    }

    location /.well-known/matrix/ {
        proxy_pass http://127.0.0.1:${CONDUIT_PORT};
        proxy_set_header Host \$host;
    }
}
NGINX_PLAIN
    ln -sf /etc/nginx/sites-available/matrix /etc/nginx/sites-enabled/
    nginx -t && systemctl reload nginx
    log_warn "⚠️  Працює лише HTTP. Коли DNS запрацює, отримайте сертифікат:"
    log_warn "    sudo certbot --nginx -d ${DOMAIN}"
}

# ═══════════════════════════════════════════════════════════════════════════════
# Фінальна перевірка
# ═══════════════════════════════════════════════════════════════════════════════
final_checks() {
    log_step "Фінальна перевірка"
    local ok=true

    systemctl is-active --quiet conduit && log_info "✅ conduit.service — активна" || { log_error "❌ conduit.service — не працює"; ok=false; }
    systemctl is-active --quiet nginx && log_info "✅ nginx.service — активна" || { log_error "❌ nginx.service — не працює"; ok=false; }

    if curl -sf "http://127.0.0.1:${CONDUIT_PORT}/_matrix/client/versions" > /dev/null; then
        log_info "✅ Conduit API (127.0.0.1:${CONDUIT_PORT}) — відповідає"
    else
        log_error "❌ Conduit API — не відповідає"; ok=false
    fi

    if curl -sf "https://${DOMAIN}/_matrix/client/versions" > /dev/null 2>&1; then
        log_info "✅ HTTPS API (${DOMAIN}) — відповідає"
    elif curl -sf "http://${DOMAIN}/_matrix/client/versions" > /dev/null 2>&1; then
        log_warn "⚠️  HTTP API працює, HTTPS — ні"
    else
        log_warn "⚠️  API через домен не відповідає (DNS ще не оновився або порти закриті в Security List Oracle)"
    fi

    if curl -sf "https://${DOMAIN}/.well-known/matrix/client" > /dev/null 2>&1 || curl -sf "http://127.0.0.1:${CONDUIT_PORT}/.well-known/matrix/client" > /dev/null 2>&1; then
        log_info "✅ .well-known/matrix/client — відповідає"
    else
        log_warn "⚠️  .well-known не відповідає (на першому запуску це буває)"
    fi

    systemctl is-active --quiet certbot.timer 2>/dev/null && log_info "✅ certbot.timer — автооновлення сертифіката активне"

    echo ""
    if $ok; then log_step "🎉 Встановлення завершено"; else log_step "⚠️  Завершено з попередженнями"; fi

    echo -e "${BOLD}Параметри:${NC}"
    echo "  server_name:        ${SERVER_NAME}"
    echo "  Адреса:             https://${DOMAIN}"
    echo "  Registration token: ${REG_TOKEN}"
    echo "  Конфіг:             ${CONDUIT_CONFIG}"
    echo "  Дані:               ${CONDUIT_DATA}"
    echo "  Федерація:          ${ENABLE_FEDERATION}"
    echo ""
    echo -e "${BOLD}Далі:${NC}"
    echo "  1. Відкрийте https://app.element.io → Sign In → Edit homeserver → https://${DOMAIN}"
    echo "  2. Create account: ім’я, пароль і registration token"
    echo "  3. Перший користувач = адміністратор (кімната #admins:${DOMAIN})"
    echo "  4. Міст із Telegram: sudo ./setup-bridge.sh"
    echo ""
    echo -e "${BOLD}Корисне:${NC}"
    echo "  journalctl -u conduit -f          # логи Conduit"
    echo "  systemctl restart conduit         # перезапуск"
    echo "  sudo certbot renew --dry-run      # тест оновлення сертифіката"
    echo ""

    cat > "${CONDUIT_CONFIG_DIR}/.install-params" <<PARAMS
# Згенеровано setup-conduit.sh $(date -u +"%Y-%m-%d %H:%M UTC") — читають setup-bridge.sh і collect-logs.sh
DOMAIN="${DOMAIN}"
SERVER_NAME="${SERVER_NAME}"
CONDUIT_PORT="${CONDUIT_PORT}"
CONDUIT_CONFIG="${CONDUIT_CONFIG}"
CONDUIT_DATA="${CONDUIT_DATA}"
ENABLE_FEDERATION="${ENABLE_FEDERATION}"
PARAMS
    chmod 600 "${CONDUIT_CONFIG_DIR}/.install-params"
    log_info "Параметри збережено в ${CONDUIT_CONFIG_DIR}/.install-params"
}

main() {
    echo ""
    echo -e "${CYAN}${BOLD}"
    echo "  ╔═══════════════════════════════════════════════════════╗"
    echo "  ║  Conduit Matrix Server — встановлювач                 ║"
    echo "  ║  Oracle Cloud Always Free / Ubuntu · ${CONDUIT_VERSION}         ║"
    echo "  ╚═══════════════════════════════════════════════════════╝"
    echo -e "${NC}"

    check_root
    check_ubuntu

    log_info "Встановлюю базові залежності…"
    apt-get update -qq
    apt-get install -y -qq wget curl dnsutils openssl file > /dev/null

    collect_params
    install_conduit_binary
    setup_conduit_system
    setup_nginx_ssl
    final_checks
}

main "$@"
