#!/usr/bin/env bash
#
# Downloads latest Flart Studio plugin ZIPs from their Akeeba Release System (ARS)
# update server, injects composer.json, and publishes to GitHub repos for Satis.
#
# Requires: curl, xmllint (libxml2), git, gh (GitHub CLI), unzip
#
# Usage:
#   FLART_DLID=<your-download-id> ./bin/update-flart-plugins.sh
#   # or with secrets.env in project root:
#   ./bin/update-flart-plugins.sh
#
# Options:
#   --slug <slug>      Download only the specified plugin (e.g., --slug fs-grid)
#   --output <dir>     Output directory for ZIPs (default: ./dist/flart)
#   --dry-run          Show versions without downloading or publishing
#   --download-only    Download ZIPs but don't publish to GitHub repos
#   --help             Show this help message

set -euo pipefail

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

# ── Configuration ────────────────────────────────────────────────────────────

ARS_BASE="https://flart.studio/component/ars"
ARS_STREAM_URL="${ARS_BASE}/update?task=stream&format=xml&Itemid=107"
OUTPUT_DIR="${PROJECT_DIR}/dist/flart"
GITHUB_ORG="greenleafadvancement"
WORK_DIR=""

# Plugin slug → ARS WordPress stream ID
declare -A STREAM_IDS=(
    [fs-grid]=12
    [fs-table]=4
    [fs-teaser]=6
    [fs-changelog]=30
    [fs-circleprogress]=26
    [fs-counter]=28
    [fs-row]=32
    [fs-section-shape-divider]=40
    [fs-modal]=48
)

# Plugin slug → composer description (matches existing repos)
declare -A DESCRIPTIONS=(
    [fs-grid]="Grid Pro Element for YOOtheme Pro"
    [fs-table]="Table Pro Element for YOOtheme Pro"
    [fs-teaser]="Teaser Card element for YOOtheme Pro"
    [fs-changelog]="Changelog Element for YOOtheme Pro"
    [fs-circleprogress]="Circle Progress element for YOOtheme Pro"
    [fs-counter]="Counter Element for YOOtheme Pro"
    [fs-row]="Row Grid Layouts for YOOtheme Pro"
    [fs-section-shape-divider]="Shape Dividers Pro plugin for YOOtheme Pro"
    [fs-modal]="Modal window element for YOOtheme Pro"
)

# ── Helpers ──────────────────────────────────────────────────────────────────

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

log()   { echo -e "${CYAN}→${NC} $*"; }
ok()    { echo -e "${GREEN}✓${NC} $*"; }
warn()  { echo -e "${YELLOW}!${NC} $*"; }
err()   { echo -e "${RED}✗${NC} $*" >&2; }

usage() {
    sed -n '2,/^$/s/^# \?//p' "$0"
    exit 0
}

cleanup() {
    if [[ -n "$WORK_DIR" && -d "$WORK_DIR" ]]; then
        rm -rf "$WORK_DIR"
    fi
}
trap cleanup EXIT

# Decode XML/HTML entities (handles double-encoded &amp;amp; from ARS)
decode_entities() {
    echo "$1" | sed 's/&amp;amp;/\&/g; s/&amp;/\&/g; s/&lt;/</g; s/&gt;/>/g'
}

# ── Load DLID ────────────────────────────────────────────────────────────────

load_dlid() {
    if [[ -n "${FLART_DLID:-}" ]]; then
        return
    fi

    local secrets_file="${PROJECT_DIR}/secrets.env"
    if [[ -f "$secrets_file" ]]; then
        local val
        val=$(grep -E '^FLART_DLID=' "$secrets_file" 2>/dev/null | head -1 | cut -d= -f2- | tr -d '"' | tr -d "'")
        if [[ -n "$val" ]]; then
            FLART_DLID="$val"
            return
        fi
    fi

    err "FLART_DLID not set. Provide it via environment variable or in secrets.env"
    exit 1
}

# ── Fetch and parse update XML ───────────────────────────────────────────────

fetch_stream() {
    local stream_id="$1"
    curl -sf "${ARS_STREAM_URL}&id=${stream_id}" 2>/dev/null
}

parse_latest_version() {
    local xml="$1" slug="$2"
    echo "$xml" | xmllint --xpath "string(//update[element='${slug}'][last()]/version)" - 2>/dev/null
}

parse_download_url() {
    local xml="$1" slug="$2"
    local raw_url
    raw_url=$(echo "$xml" | xmllint --xpath "string(//update[element='${slug}'][last()]/downloads/downloadurl)" - 2>/dev/null)
    decode_entities "$raw_url"
}

# ── Get current version from GitHub repo ─────────────────────────────────────

get_repo_version() {
    local slug="$1"
    gh api "repos/${GITHUB_ORG}/${slug}/tags" --jq '.[0].name' 2>/dev/null || echo ""
}

# ── Generate composer.json ───────────────────────────────────────────────────

generate_composer_json() {
    local slug="$1"
    local description="${DESCRIPTIONS[$slug]:-"Flart Studio element for YOOtheme Pro"}"

    cat <<COMPOSER_EOF
{
  "name": "${GITHUB_ORG}/${slug}",
  "description": "${description}",
  "type": "wordpress-plugin",
  "config": {
    "allow-plugins": {
      "wikimedia/composer-merge-plugin": true
    }
  }
}
COMPOSER_EOF
}

# ── Process a single plugin ──────────────────────────────────────────────────

process_plugin() {
    local slug="$1"
    local stream_id="${STREAM_IDS[$slug]}"

    log "Checking ${slug} (stream ${stream_id})..."

    # Fetch update XML
    local xml
    xml=$(fetch_stream "$stream_id")
    if [[ -z "$xml" ]]; then
        err "Failed to fetch update XML for ${slug}"
        return 1
    fi

    # Parse version and download URL
    local version
    version=$(parse_latest_version "$xml" "$slug")
    if [[ -z "$version" ]]; then
        err "Could not parse version for ${slug}"
        return 1
    fi

    local download_url
    download_url=$(parse_download_url "$xml" "$slug")
    if [[ -z "$download_url" ]]; then
        err "Could not parse download URL for ${slug}"
        return 1
    fi

    # Check if this version already exists in the GitHub repo
    local repo_version
    repo_version=$(get_repo_version "$slug")
    if [[ "$repo_version" == "$version" ]]; then
        ok "${slug} v${version} already up to date in repo"
        return 0
    fi

    if [[ -n "$repo_version" ]]; then
        log "${slug}: ${repo_version} → ${version}"
    else
        log "${slug}: (no tags) → ${version}"
    fi

    # Dry run stops here
    if [[ "$DRY_RUN" == "true" ]]; then
        echo "  Would download: ${download_url}"
        echo "  Would publish:  ${GITHUB_ORG}/${slug} @ ${version}"
        return 0
    fi

    # Append DLID to download URL
    if [[ "$download_url" == *"?"* ]]; then
        download_url="${download_url}&dlid=${FLART_DLID}"
    else
        download_url="${download_url}?dlid=${FLART_DLID}"
    fi

    # Download ZIP
    local outfile="${OUTPUT_DIR}/${slug}-${version}.zip"

    if [[ ! -f "$outfile" ]]; then
        log "Downloading ${slug} v${version}..."
        local http_code
        http_code=$(curl -L -w "%{http_code}" -o "$outfile" -s "$download_url")

        if [[ "$http_code" -ne 200 ]]; then
            err "Download failed for ${slug} (HTTP ${http_code})"
            rm -f "$outfile"
            return 1
        fi

        if ! unzip -tq "$outfile" &>/dev/null; then
            err "Downloaded file for ${slug} is not a valid ZIP"
            rm -f "$outfile"
            return 1
        fi
    fi

    ok "${slug} v${version} downloaded"

    # Stop here if download-only mode
    if [[ "$DOWNLOAD_ONLY" == "true" ]]; then
        return 0
    fi

    # Publish to GitHub repo
    publish_to_repo "$slug" "$version" "$outfile"
}

# ── Publish plugin to its GitHub repo ────────────────────────────────────────

publish_to_repo() {
    local slug="$1"
    local version="$2"
    local zipfile="$3"

    local repo_url="git@github.com:${GITHUB_ORG}/${slug}.git"
    local clone_dir="${WORK_DIR}/${slug}"

    log "Publishing ${slug} v${version} to ${GITHUB_ORG}/${slug}..."

    # Clone the repo
    if ! git clone --depth 1 "$repo_url" "$clone_dir" 2>/dev/null; then
        err "Failed to clone ${repo_url}"
        return 1
    fi

    # Detect default branch
    local default_branch
    default_branch=$(git -C "$clone_dir" rev-parse --abbrev-ref HEAD)

    # Remove all existing files except .git and .github
    find "$clone_dir" -maxdepth 1 \
        -not -name '.git' \
        -not -name '.github' \
        -not -path "$clone_dir" \
        -exec rm -rf {} + 2>/dev/null || true

    # Extract ZIP contents (ZIP has files under slug/ directory)
    local extract_dir="${WORK_DIR}/extract-${slug}"
    unzip -qo "$zipfile" -d "$extract_dir"
    local extract_src="${extract_dir}/${slug}"

    if [[ ! -d "$extract_src" ]]; then
        err "Expected directory ${slug}/ in ZIP, not found"
        ls "$extract_dir/"
        return 1
    fi

    # Copy extracted files to repo
    cp -a "$extract_src"/. "$clone_dir"/

    # Inject composer.json
    generate_composer_json "$slug" > "$clone_dir/composer.json"

    # Stage all changes
    git -C "$clone_dir" add -A

    # Commit if there are changes
    if ! git -C "$clone_dir" diff --cached --quiet; then
        git -C "$clone_dir" commit -m "Update ${slug} to v${version} from Flart Studio"
    else
        warn "${slug} v${version}: no file changes, tagging only"
    fi

    # Create tag
    git -C "$clone_dir" tag -f "$version" -m "v${version}"

    # Push commit and tag
    if ! git -C "$clone_dir" push origin "$default_branch" 2>&1; then
        err "Failed to push to ${repo_url}"
        return 1
    fi

    if ! git -C "$clone_dir" push origin "$version" --force 2>&1; then
        err "Failed to push tag ${version} to ${repo_url}"
        return 1
    fi

    ok "${slug} v${version} published to ${GITHUB_ORG}/${slug}"
}

# ── Main ─────────────────────────────────────────────────────────────────────

main() {
    local filter_slug=""
    DRY_RUN="false"
    DOWNLOAD_ONLY="false"

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --slug)          filter_slug="$2"; shift 2 ;;
            --output)        OUTPUT_DIR="$2"; shift 2 ;;
            --dry-run)       DRY_RUN="true"; shift ;;
            --download-only) DOWNLOAD_ONLY="true"; shift ;;
            --help|-h)       usage ;;
            *)               err "Unknown option: $1"; usage ;;
        esac
    done

    load_dlid

    mkdir -p "$OUTPUT_DIR"
    WORK_DIR=$(mktemp -d)

    local success=0 fail=0

    if [[ -n "$filter_slug" ]]; then
        if [[ -z "${STREAM_IDS[$filter_slug]+x}" ]]; then
            err "Unknown plugin slug: ${filter_slug}"
            err "Available: ${!STREAM_IDS[*]}"
            exit 1
        fi
        if process_plugin "$filter_slug"; then success=$((success + 1)); else fail=$((fail + 1)); fi
    else
        for slug in $(echo "${!STREAM_IDS[@]}" | tr ' ' '\n' | sort); do
            if process_plugin "$slug"; then success=$((success + 1)); else fail=$((fail + 1)); fi
        done
    fi

    echo ""
    log "Done: ${success} succeeded, ${fail} failed"

    if [[ "$DRY_RUN" != "true" && "$DOWNLOAD_ONLY" != "true" && $success -gt 0 ]]; then
        echo ""
        log "Satis will auto-rebuild via the daily schedule or you can trigger manually:"
        echo "  gh workflow run rebuild.yml -R ${GITHUB_ORG}/satis"
    fi

    [[ $fail -eq 0 ]] || exit 1
}

main "$@"
