pipeline {
    agent {
        docker {
            image 'mcr.microsoft.com/dotnet/sdk:9.0'
            args  '-u root:root -e HOME=/tmp -e DOTNET_CLI_HOME=/tmp'
        }
    }

    environment {
        DOTNET_VERSION              = '9.0'
        DOTNET_NOLOGO               = 'true'
        DOTNET_CLI_TELEMETRY_OPTOUT = '1'
        CONFIGURATION               = 'Release'
        PLUGIN_NAME                 = 'Jellyfin.Plugin.Smack'
        PLUGIN_VERSION              = '1.0.0'
    }

    options {
        timestamps()
        buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '10'))
        timeout(time: 20, unit: 'MINUTES')
    }

    triggers {
        pollSCM('H/5 * * * *')
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
                sh "git config --global --add safe.directory '${env.WORKSPACE}'"
            }
        }

        stage('Restore') {
            steps {
                sh 'dotnet restore ${PLUGIN_NAME}.sln --configfile nuget.config'
            }
        }

        stage('Build') {
            steps {
                sh '''
                    BUILD_VERSION="${PLUGIN_VERSION}.${BUILD_NUMBER}"
                    dotnet build ${PLUGIN_NAME}.sln \
                        --configuration ${CONFIGURATION} \
                        --no-restore \
                        -p:Version=${BUILD_VERSION} \
                        -p:AssemblyVersion=${BUILD_VERSION} \
                        -p:FileVersion=${BUILD_VERSION}
                '''
            }
        }

        stage('Package') {
            steps {
                sh '''
                    command -v zip >/dev/null || (apt-get update -qq && apt-get install -y -qq zip)
                    BUILD_VERSION="${PLUGIN_VERSION}.${BUILD_NUMBER}"
                    OUT=artifacts
                    BIN=${PLUGIN_NAME}/bin/${CONFIGURATION}/net9.0
                    STAGE=${OUT}/stage
                    rm -rf ${OUT} && mkdir -p ${STAGE}

                    cp ${BIN}/*.dll ${STAGE}/
                    [ -d ${BIN}/runtimes ] && cp -r ${BIN}/runtimes ${STAGE}/
                    cp build.yaml ${STAGE}/
                    sed -i -E "s/^version:.*/version: \\"${BUILD_VERSION}\\"/" ${STAGE}/build.yaml

                    # Windows natives (e.g. e_sqlite3.dll) ship as .dll and would trip
                    # Jellyfin's assembly scanner on non-Windows hosts (BadImageFormat
                    # → plugin disabled). Rename them; NativeLibraryLoader.cs loads
                    # the renamed files by path at runtime on Windows.
                    if [ -d ${STAGE}/runtimes ]; then
                        find ${STAGE}/runtimes -path '*/win-*/native/*.dll' \
                            -exec sh -c 'mv "$1" "$1.native"' _ {} \\;
                    fi

                    ZIP="${PLUGIN_NAME}_${BUILD_VERSION}.zip"
                    (cd ${STAGE} && zip -r9 "../${ZIP}" .)
                    (cd ${OUT} && md5sum "${ZIP}" > "${ZIP}.md5")
                '''
            }
        }

        stage('Archive') {
            steps {
                archiveArtifacts artifacts: 'artifacts/*.zip, artifacts/*.md5', fingerprint: true
            }
        }

        stage('Release') {
            steps {
                withCredentials([usernamePassword(credentialsId: 'gitea_usr_pwd', usernameVariable: 'GU', passwordVariable: 'GP')]) {
                    sh '''
                        command -v curl >/dev/null && command -v jq >/dev/null || \
                            (apt-get update -qq && apt-get install -y -qq curl jq)

                        TAG="v${PLUGIN_VERSION}.${BUILD_NUMBER}"
                        SHA=$(git rev-parse HEAD)
                        API="http://chips:3000/api/v1/repos/Esmond/jellyfin-plugin-smack"

                        BODY=$(jq -n \
                            --arg tag "$TAG" \
                            --arg sha "$SHA" \
                            --arg build "$BUILD_NUMBER" \
                            '{tag_name:$tag, name:$tag, target_commitish:$sha, body:("Build " + $build + " (" + $sha[0:7] + ")")}')

                        REL_ID=$(curl -sSf -u "${GU}:${GP}" \
                            -H 'Content-Type: application/json' \
                            -d "$BODY" \
                            "${API}/releases" | jq -r '.id')

                        for asset in artifacts/*.zip artifacts/*.zip.md5; do
                            [ -f "${asset}" ] || continue
                            curl -sSf -u "${GU}:${GP}" \
                                -F "attachment=@${asset}" \
                                "${API}/releases/${REL_ID}/assets?name=$(basename ${asset})"
                        done

                        echo "Published release ${TAG} (id=${REL_ID})"
                    '''
                }
            }
        }

        stage('Publish Manifest') {
            steps {
                withCredentials([usernamePassword(credentialsId: 'gitea_usr_pwd', usernameVariable: 'GU', passwordVariable: 'GP')]) {
                    sh '''
                        API="http://chips:3000/api/v1/repos/Esmond/jellyfin-plugin-smack"
                        BASE="http://chips:3000/Esmond/jellyfin-plugin-smack"
                        PUBLIC_BASE="https://git.missen.ca/Esmond/jellyfin-plugin-smack"
                        MANIFEST_TAG="manifest"
                        GUID="11c1ffe5-e4de-4cbb-a30c-c2870eb81603"
                        TARGET_ABI="10.11.0.0"

                        RELEASES=$(curl -sSf -u "${GU}:${GP}" "${API}/releases?limit=50" | \
                            jq '[.[] | select(.tag_name != "'"${MANIFEST_TAG}"'")]')

                        VERSIONS='[]'
                        for row in $(echo "$RELEASES" | jq -r '.[] | @base64'); do
                            REL=$(echo "$row" | base64 -d)
                            TAG=$(echo "$REL" | jq -r '.tag_name')
                            CREATED=$(echo "$REL" | jq -r '.created_at')
                            CREATED_UTC=$(date -u -d "${CREATED}" +"%Y-%m-%dT%H:%M:%SZ")
                            BODY=$(echo "$REL" | jq -r '.body // ""')
                            VER=${TAG#v}
                            ZIP_NAME="${PLUGIN_NAME}_${VER}.zip"
                            MD5=$(curl -sSf -u "${GU}:${GP}" "${BASE}/releases/download/${TAG}/${ZIP_NAME}.md5" 2>/dev/null | awk '{print $1}')
                            [ -z "$MD5" ] && continue
                            ENTRY=$(jq -n \
                                --arg v "$VER" \
                                --arg abi "$TARGET_ABI" \
                                --arg url "${PUBLIC_BASE}/releases/download/${TAG}/${ZIP_NAME}" \
                                --arg md5 "$MD5" \
                                --arg ts "$CREATED_UTC" \
                                --arg cl "$BODY" \
                                '{version:$v, changelog:$cl, targetAbi:$abi, sourceUrl:$url, checksum:$md5, timestamp:$ts}')
                            VERSIONS=$(echo "$VERSIONS" | jq ". + [${ENTRY}]")
                        done

                        VERSIONS=$(echo "$VERSIONS" | jq 'sort_by(.timestamp) | reverse')

                        jq -n \
                            --arg guid "$GUID" \
                            --argjson versions "$VERSIONS" \
                            '[{
                                guid:$guid,
                                name:"Smack",
                                description:"A Jellyfin plugin for synchronizing local tracks with your Jellyfin server library.",
                                overview:"Sync local tracks with Jellyfin",
                                owner:"Esmond",
                                category:"General",
                                versions:$versions
                            }]' > manifest.json

                        CODE=$(curl -sS -u "${GU}:${GP}" -o rel.json -w '%{http_code}' "${API}/releases/tags/${MANIFEST_TAG}")
                        if [ "$CODE" != "200" ]; then
                            curl -sSf -u "${GU}:${GP}" \
                                -H 'Content-Type: application/json' \
                                -d "$(jq -n --arg t "$MANIFEST_TAG" '{tag_name:$t, name:"Plugin manifest", body:"Auto-generated Jellyfin plugin manifest"}')" \
                                -o rel.json "${API}/releases"
                        fi
                        REL_ID=$(jq -r '.id' rel.json)

                        # Clean up any stale per-RID manifests from the earlier split approach.
                        for stale in $(jq -r '.assets[]? | select(.name | startswith("manifest-")) | .id' rel.json); do
                            curl -sSf -u "${GU}:${GP}" -X DELETE "${API}/releases/${REL_ID}/assets/${stale}"
                        done

                        OLD=$(jq -r '.assets[]? | select(.name=="manifest.json") | .id' rel.json)
                        if [ -n "$OLD" ]; then
                            curl -sSf -u "${GU}:${GP}" -X DELETE "${API}/releases/${REL_ID}/assets/${OLD}"
                        fi

                        curl -sSf -u "${GU}:${GP}" \
                            -F "attachment=@manifest.json" \
                            "${API}/releases/${REL_ID}/assets?name=manifest.json"

                        echo "Manifest URL: ${BASE}/releases/download/${MANIFEST_TAG}/manifest.json"
                    '''
                }
            }
        }
    }

    post {
        failure { echo "Build ${env.BUILD_NUMBER} failed on ${env.BRANCH_NAME ?: 'master'}" }
        cleanup { cleanWs() }
    }
}
