name: Build Releases

on:
  push:
    branches:
      - "**"

env:
  APP_NAME: gitree

jobs:
  build-windows:
    name: Build Windows x64
    runs-on: ubuntu-latest

    steps:
      - name: Check out repository
        uses: actions/checkout@v4
        with:
          submodules: recursive
          fetch-depth: 0

      - name: Sync submodules
        run: |
          git submodule sync --recursive
          git submodule update --init --force --recursive
          git submodule status --recursive

      - name: Generate build names
        env:
          GITEA_SHA: ${{ gitea.sha }}
        run: |
          short_sha="$(printf '%s' "$GITEA_SHA" | cut -c1-7)"

          echo "BUILD_HASH=${short_sha}" >> "$GITHUB_ENV"
          echo "WINDOWS_EXE=${APP_NAME}-windows-x64-${short_sha}.exe" >> "$GITHUB_ENV"
          echo "WINDOWS_ARTIFACT=${APP_NAME}-windows-x64-${short_sha}" >> "$GITHUB_ENV"

      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y mingw-w64 cmake ninja-build curl jq

      - name: Create MinGW toolchain
        run: |
          cat > mingw-toolchain.cmake <<'EOF'
          set(CMAKE_SYSTEM_NAME Windows)
          set(CMAKE_SYSTEM_PROCESSOR x86_64)

          set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
          set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
          set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)

          set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)

          set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
          set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
          set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
          EOF

      - name: Configure Windows release build
        run: |
          cmake -S . -B build-win -G Ninja \
            -DCMAKE_TOOLCHAIN_FILE=mingw-toolchain.cmake \
            -DCMAKE_BUILD_TYPE=Release \
            -DBUILD_SHARED_LIBS=OFF \
            -DCMAKE_EXE_LINKER_FLAGS="-static -static-libgcc -static-libstdc++"

      - name: Build Windows executable
        run: cmake --build build-win --parallel

      - name: Locate Windows EXE
        run: |
          exe_path=""

          if [ -f "build-win/bin/${APP_NAME}.exe" ]; then
            exe_path="build-win/bin/${APP_NAME}.exe"
          elif [ -f "build-win/bin/Gitree.exe" ]; then
            exe_path="build-win/bin/Gitree.exe"
          elif [ -f "build-win/bin/gitree.exe" ]; then
            exe_path="build-win/bin/gitree.exe"
          else
            exe_path="$(find build-win -type f -iname '*.exe' | head -n 1)"
          fi

          if [ -z "$exe_path" ]; then
            echo "Could not find built Windows EXE."
            exit 1
          fi

          echo "WINDOWS_EXE_PATH=${exe_path}" >> "$GITHUB_ENV"
          echo "Found Windows EXE: ${exe_path}"

      - name: Verify static runtime linkage
        run: |
          dependencies="$(x86_64-w64-mingw32-objdump -p "$WINDOWS_EXE_PATH" | sed -n 's/.*DLL Name: //p')"

          printf '%s\n' "$dependencies"

          if printf '%s\n' "$dependencies" | grep -Eqi 'lib(gcc|stdc\+\+|winpthread).*\.dll'; then
            echo "The executable still depends on a MinGW runtime DLL."
            exit 1
          fi

      - name: Prepare Windows artifact
        run: |
          mkdir -p dist
          cp "$WINDOWS_EXE_PATH" "dist/${WINDOWS_EXE}"

      - name: Upload Windows build
        uses: actions/upload-artifact@v3
        with:
          name: ${{ env.WINDOWS_ARTIFACT }}
          path: dist/${{ env.WINDOWS_EXE }}
          if-no-files-found: error

  build-linux:
    name: Build Linux x64 DEB
    runs-on: ubuntu-latest

    steps:
      - name: Check out repository
        uses: actions/checkout@v4
        with:
          submodules: recursive
          fetch-depth: 0

      - name: Sync submodules
        run: |
          git submodule sync --recursive
          git submodule update --init --force --recursive
          git submodule status --recursive

      - name: Generate build names
        env:
          GITEA_SHA: ${{ gitea.sha }}
        run: |
          short_sha="$(printf '%s' "$GITEA_SHA" | cut -c1-7)"

          echo "BUILD_HASH=${short_sha}" >> "$GITHUB_ENV"
          echo "LINUX_DEB=${APP_NAME}-deb-x64-${short_sha}.deb" >> "$GITHUB_ENV"
          echo "LINUX_ARTIFACT=${APP_NAME}-deb-x64-${short_sha}" >> "$GITHUB_ENV"

      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y \
            cmake ninja-build curl jq dpkg-dev \
            libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev \
            libgl1-mesa-dev

      - name: Configure Linux release build
        run: |
          cmake -S . -B build-linux -G Ninja \
            -DCMAKE_BUILD_TYPE=Release \
            -DBUILD_SHARED_LIBS=OFF

      - name: Build Linux executable
        run: cmake --build build-linux --parallel

      - name: Locate Linux executable
        run: |
          exe_path=""

          if [ -f "build-linux/bin/${APP_NAME}" ]; then
            exe_path="build-linux/bin/${APP_NAME}"
          elif [ -f "build-linux/bin/Gitree" ]; then
            exe_path="build-linux/bin/Gitree"
          elif [ -f "build-linux/bin/gitree" ]; then
            exe_path="build-linux/bin/gitree"
          else
            exe_path="$(find build-linux -type f -executable -name "${APP_NAME}" | head -n 1)"
          fi

          if [ -z "$exe_path" ]; then
            echo "Could not find built Linux executable."
            exit 1
          fi

          echo "LINUX_EXE_PATH=${exe_path}" >> "$GITHUB_ENV"
          echo "Found Linux executable: ${exe_path}"

      - name: Package Linux DEB
        run: |
          mkdir -p "pkg/DEBIAN"
          mkdir -p "pkg/usr/bin"

          cp "$LINUX_EXE_PATH" "pkg/usr/bin/${APP_NAME}"
          chmod 755 "pkg/usr/bin/${APP_NAME}"

          version="0.0.0+${BUILD_HASH}"

          cat > "pkg/DEBIAN/control" <<EOF
          Package: ${APP_NAME}
          Version: ${version}
          Section: utils
          Priority: optional
          Architecture: amd64
          Maintainer: GigabiteStudios <ceo@gigabitestudios.xyz>
          Description: ${APP_NAME}
           ${APP_NAME} Linux x64 build.
          EOF

          mkdir -p dist
          dpkg-deb --build pkg "dist/${LINUX_DEB}"

      - name: Upload Linux DEB build
        uses: actions/upload-artifact@v3
        with:
          name: ${{ env.LINUX_ARTIFACT }}
          path: dist/${{ env.LINUX_DEB }}
          if-no-files-found: error

  release:
    name: Create Release
    runs-on: ubuntu-latest
    needs:
      - build-windows
      - build-linux
    if: ${{ always() && gitea.ref_name == 'prod' }}

    env:
      GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
      GITEA_SERVER_URL: ${{ gitea.server_url }}
      GITEA_REPOSITORY: ${{ gitea.repository }}
      GITEA_SHA: ${{ gitea.sha }}
      WINDOWS_RESULT: ${{ needs.build-windows.result }}
      LINUX_RESULT: ${{ needs.build-linux.result }}
      APP_NAME: gitree

    steps:
      - name: Check build results
        run: |
          echo "Windows result: ${WINDOWS_RESULT}"
          echo "Linux result:   ${LINUX_RESULT}"

          if [ "$WINDOWS_RESULT" != "success" ] && [ "$LINUX_RESULT" != "success" ]; then
            echo "Both Windows and Linux builds failed. Refusing to create release."
            exit 1
          fi

      - name: Check out repository
        uses: actions/checkout@v4
        with:
          submodules: recursive
          fetch-depth: 0

      - name: Sync submodules
        run: |
          git submodule sync --recursive
          git submodule update --init --force --recursive
          git submodule status --recursive

      - name: Generate release names
        run: |
          short_sha="$(printf '%s' "$GITEA_SHA" | cut -c1-7)"

          echo "BUILD_HASH=${short_sha}" >> "$GITHUB_ENV"
          echo "RELEASE_TAG=release-${short_sha}" >> "$GITHUB_ENV"
          echo "RELEASE_NAME=Release ${short_sha}" >> "$GITHUB_ENV"

          echo "WINDOWS_EXE=${APP_NAME}-windows-x64-${short_sha}.exe" >> "$GITHUB_ENV"
          echo "WINDOWS_ARTIFACT=${APP_NAME}-windows-x64-${short_sha}" >> "$GITHUB_ENV"

          echo "LINUX_DEB=${APP_NAME}-deb-x64-${short_sha}.deb" >> "$GITHUB_ENV"
          echo "LINUX_ARTIFACT=${APP_NAME}-deb-x64-${short_sha}" >> "$GITHUB_ENV"

      - name: Install release dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y curl jq

      - name: Download Windows artifact
        if: ${{ needs.build-windows.result == 'success' }}
        uses: actions/download-artifact@v3
        with:
          name: ${{ env.WINDOWS_ARTIFACT }}
          path: release-assets

      - name: Download Linux artifact
        if: ${{ needs.build-linux.result == 'success' }}
        uses: actions/download-artifact@v3
        with:
          name: ${{ env.LINUX_ARTIFACT }}
          path: release-assets

      - name: Create prod release
        run: |
          if [ -z "$GITEA_TOKEN" ]; then
            echo "The repository secret GITEA_TOKEN is required to publish prod releases."
            exit 1
          fi

          git fetch --tags --force

          api="${GITEA_SERVER_URL%/}/api/v1/repos/${GITEA_REPOSITORY}"
          repo_url="${GITEA_SERVER_URL%/}/${GITEA_REPOSITORY}"

          previous_tag="$(
            curl --fail-with-body --silent --show-error \
              -H "Authorization: token ${GITEA_TOKEN}" \
              "${api}/releases?limit=50" |
            jq -r '[.[] | select(.tag_name | startswith("release-"))][0].tag_name // empty'
          )"

          if [ -n "$previous_tag" ]; then
            range="${previous_tag}..${GITEA_SHA}"
          else
            range="${GITEA_SHA}"
          fi

          {
            if [ -n "$previous_tag" ]; then
              echo "## Changes since ${previous_tag}"
            else
              echo "## Changes"
            fi

            echo

            commit_count="$(git rev-list --count "$range")"

            if [ "$commit_count" -eq 0 ]; then
              echo "- No commits found since the previous release."
            else
              git log "$range" \
                --reverse \
                --pretty=format:'%H%x1f%s' |
              while IFS="$(printf '\037')" read -r hash subject; do
                short="$(printf '%s' "$hash" | cut -c1-7)"
                echo "- ([${short}](${repo_url}/commit/${hash})) ${subject}"
              done
            fi

            echo
            echo "## Builds"
            echo

            if [ "$WINDOWS_RESULT" = "success" ]; then
              echo "- ${WINDOWS_EXE}"
            else
              echo "- Windows x64 failed"
            fi

            if [ "$LINUX_RESULT" = "success" ]; then
              echo "- ${LINUX_DEB}"
            else
              echo "- Linux DEB x64 failed"
            fi

            echo
            echo "## Authors"
            echo
            echo "Sorted by total lines added or removed."
            echo

            git log "$range" --numstat --format='author:%an <%ae>' |
            awk '
              /^author:/ {
                author = substr($0, 8)
                next
              }

              NF >= 3 {
                added = ($1 == "-" ? 0 : $1)
                removed = ($2 == "-" ? 0 : $2)

                adds[author] += added
                dels[author] += removed
                churn[author] += added + removed
              }

              END {
                for (a in churn) {
                  printf "%d\t%d\t%d\t%s\n", churn[a], adds[a], dels[a], a
                }
              }
            ' |
            sort -nr |
            while IFS="$(printf '\t')" read -r total added removed author; do
              echo "- ${author} — ${total} lines changed (+${added} / -${removed})"
            done
          } > release-notes.md

          payload="$(jq -n \
            --arg tag "$RELEASE_TAG" \
            --arg sha "$GITEA_SHA" \
            --arg name "$RELEASE_NAME" \
            --rawfile body release-notes.md \
            '{
              tag_name: $tag,
              target_commitish: $sha,
              name: $name,
              body: $body,
              draft: false,
              prerelease: false
            }')"

          release="$(curl --fail-with-body --silent --show-error \
            -X POST \
            -H "Authorization: token ${GITEA_TOKEN}" \
            -H "Content-Type: application/json" \
            --data "$payload" \
            "${api}/releases")"

          release_id="$(printf '%s' "$release" | jq -er '.id')"

          for asset in release-assets/*; do
            if [ ! -f "$asset" ]; then
              continue
            fi

            asset_name="$(basename "$asset")"

            echo "Uploading ${asset_name}"

            curl --fail-with-body --silent --show-error \
              -X POST \
              -H "Authorization: token ${GITEA_TOKEN}" \
              -F "attachment=@${asset}" \
              "${api}/releases/${release_id}/assets?name=${asset_name}"
          done
