name: Release

on:
  workflow_dispatch:
    inputs:
      tag:
        description: "Tag to release (e.g., v1.0.1)"
        required: true
        type: string
      notify_issues:
        description: "Comment on open related/fixes issues asking reporters to retest"
        required: false
        type: boolean
        default: false

permissions:
  contents: write
  actions: write
  issues: write

concurrency:
  group: release-${{ inputs.tag }}
  cancel-in-progress: true

jobs:
  build-core:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        include:
          - { goos: linux, arch: amd64 }
          - { goos: linux, arch: arm64 }
          - { goos: freebsd, arch: amd64 }
          - { goos: freebsd, arch: arm64 }

    defaults:
      run:
        working-directory: core

    env:
      TAG: ${{ inputs.tag }}
      # linux assets keep their historical arch-only names
      ASSET: ${{ matrix.goos == 'linux' && matrix.arch || format('{0}-{1}', matrix.goos, matrix.arch) }}

    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ inputs.tag }}
          fetch-depth: 0
          submodules: recursive

      - name: Set up Go
        uses: actions/setup-go@v6
        with:
          go-version-file: ./core/go.mod

      - name: Format check
        if: matrix.goos == 'linux'
        run: |
          if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
            echo "The following files are not formatted:"
            gofmt -s -l .
            exit 1
          fi

      - name: Run tests
        if: matrix.goos == 'linux'
        run: go test -v ./...

      - name: Build dankinstall (${{ env.ASSET }})
        if: matrix.goos == 'linux'
        env:
          GOOS: ${{ matrix.goos }}
          CGO_ENABLED: 0
          GOARCH: ${{ matrix.arch }}
        run: |
          set -eux
          cd cmd/dankinstall
          go build -trimpath -ldflags "-s -w -X main.Version=${TAG}" \
            -o ../../dankinstall-${{ env.ASSET }}
          cd ../..
          gzip -9 -k dankinstall-${{ env.ASSET }}
          sha256sum dankinstall-${{ env.ASSET }}.gz > dankinstall-${{ env.ASSET }}.gz.sha256

      - name: Sync embedded shell
        run: make sync-shell

      - name: Build dms (${{ env.ASSET }})
        env:
          GOOS: ${{ matrix.goos }}
          CGO_ENABLED: 0
          GOARCH: ${{ matrix.arch }}
        run: |
          set -eux
          cd cmd/dms
          go build -trimpath -tags withshell -ldflags "-s -w -X main.Version=${TAG}" \
            -o ../../dms-${{ env.ASSET }}
          cd ../..
          gzip -9 -k dms-${{ env.ASSET }}
          sha256sum dms-${{ env.ASSET }}.gz > dms-${{ env.ASSET }}.gz.sha256

      - name: Generate shell completions
        if: matrix.goos == 'linux' && matrix.arch == 'amd64'
        run: |
          set -eux
          chmod +x dms-amd64
          ./dms-amd64 completion bash > completion.bash
          ./dms-amd64 completion fish > completion.fish
          ./dms-amd64 completion zsh > completion.zsh

      - name: Build dms-distropkg (${{ env.ASSET }})
        env:
          GOOS: ${{ matrix.goos }}
          CGO_ENABLED: 0
          GOARCH: ${{ matrix.arch }}
        run: |
          set -eux
          cd cmd/dms
          go build -trimpath -tags 'distro_binary withshell' -ldflags "-s -w -X main.Version=${TAG}" \
            -o ../../dms-distropkg-${{ env.ASSET }}
          cd ../..
          gzip -9 -k dms-distropkg-${{ env.ASSET }}
          sha256sum dms-distropkg-${{ env.ASSET }}.gz > dms-distropkg-${{ env.ASSET }}.gz.sha256

      - name: Upload artifacts (${{ env.ASSET }})
        if: matrix.goos == 'linux' && matrix.arch == 'arm64'
        uses: actions/upload-artifact@v5
        with:
          name: core-assets-${{ env.ASSET }}
          path: |
            core/dankinstall-${{ env.ASSET }}.gz
            core/dankinstall-${{ env.ASSET }}.gz.sha256
            core/dms-${{ env.ASSET }}.gz
            core/dms-${{ env.ASSET }}.gz.sha256
            core/dms-distropkg-${{ env.ASSET }}.gz
            core/dms-distropkg-${{ env.ASSET }}.gz.sha256
          if-no-files-found: error

      - name: Upload artifacts (${{ env.ASSET }}, no installer)
        if: matrix.goos != 'linux'
        uses: actions/upload-artifact@v5
        with:
          name: core-assets-${{ env.ASSET }}
          path: |
            core/dms-${{ env.ASSET }}.gz
            core/dms-${{ env.ASSET }}.gz.sha256
            core/dms-distropkg-${{ env.ASSET }}.gz
            core/dms-distropkg-${{ env.ASSET }}.gz.sha256
          if-no-files-found: error

      - name: Upload artifacts with completions
        if: matrix.goos == 'linux' && matrix.arch == 'amd64'
        uses: actions/upload-artifact@v5
        with:
          name: core-assets-${{ env.ASSET }}
          path: |
            core/dankinstall-${{ env.ASSET }}.gz
            core/dankinstall-${{ env.ASSET }}.gz.sha256
            core/dms-${{ env.ASSET }}.gz
            core/dms-${{ env.ASSET }}.gz.sha256
            core/dms-distropkg-${{ env.ASSET }}.gz
            core/dms-distropkg-${{ env.ASSET }}.gz.sha256
            core/completion.bash
            core/completion.fish
            core/completion.zsh
          if-no-files-found: error

  # update-versions:
  #   runs-on: ubuntu-latest
  #   needs: build-core
  #   steps:
  #     - name: Create GitHub App token
  #       id: app_token
  #       uses: actions/create-github-app-token@v1
  #       with:
  #         app-id: ${{ secrets.APP_ID }}
  #         private-key: ${{ secrets.APP_PRIVATE_KEY }}

  #     - name: Checkout
  #       uses: actions/checkout@v6
  #       with:
  #         token: ${{ steps.app_token.outputs.token }}
  #         fetch-depth: 0

  #     - name: Update VERSION
  #       env:
  #         GH_TOKEN: ${{ steps.app_token.outputs.token }}
  #       run: |
  #         set -euo pipefail
  #         git config user.name "dms-ci[bot]"
  #         git config user.email "dms-ci[bot]@users.noreply.github.com"

  #         version="${GITHUB_REF#refs/tags/}"
  #         echo "Updating to version: $version"
  #         echo "${version}" > quickshell/VERSION
  #         git add quickshell/VERSION

  #         if ! git diff --cached --quiet; then
  #           git commit -m "chore: bump version to $version"
  #           git pull --rebase origin master
  #           git push https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git HEAD:master
  #         fi

  #         git tag -f "${version}"
  #         git push -f https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git "${version}"

  release:
    runs-on: ubuntu-24.04
    needs: [build-core] #, update-versions]
    env:
      TAG: ${{ inputs.tag }}
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ inputs.tag }}
          fetch-depth: 0
          submodules: recursive

      - name: Fetch updated tag after version bump
        run: |
          git fetch origin --force tag ${TAG}
          git checkout ${TAG}
          git submodule update --init --recursive

      - name: Set up Go
        uses: actions/setup-go@v6
        with:
          go-version-file: ./core/go.mod

      - name: Download core artifacts
        uses: actions/download-artifact@v5
        with:
          pattern: core-assets-*
          merge-multiple: true
          path: ./_core_assets

      - name: Generate Changelog
        id: changelog
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          set -e
          PREVIOUS_TAG=$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || echo "")
          CHANGELOG=""
          if [ -n "$PREVIOUS_TAG" ]; then
            # PR-based notes with author credits; falls back to raw git log below
            CHANGELOG=$(python3 scripts/release-notes.py "${PREVIOUS_TAG}..${TAG}" --format github --bare 2>/dev/null || true)
          fi
          if [ -z "$CHANGELOG" ]; then
            echo "release-notes.py unavailable or empty, using git log fallback"
            if [ -z "$PREVIOUS_TAG" ]; then
              CHANGELOG=$(git log --oneline --pretty=format:"%an|%s (%h)" | grep -v "^github-actions\[bot\]|" | sed 's/^[^|]*|/- /' | head -50)
            else
              CHANGELOG=$(git log --oneline --pretty=format:"%an|%s (%h)" "${PREVIOUS_TAG}..${TAG}" | grep -v "^github-actions\[bot\]|" | sed 's/^[^|]*|/- /')
            fi
          fi

          cat > RELEASE_BODY.md << 'EOF'
          ## Installation

          ```bash
          curl -fsSL https://install.danklinux.com | sh
          ```

          ## Assets

          ### Complete Packages
          - **`dms-full-amd64.tar.gz`** - Complete package for x86_64 systems (CLI binaries + QML source + shell completions + installation guide)
          - **`dms-full-arm64.tar.gz`** - Complete package for ARM64 systems (CLI binaries + QML source + shell completions + installation guide)
          - **`dms-full-freebsd-amd64.tar.gz`** - Complete package for FreeBSD x86_64 systems
          - **`dms-full-freebsd-arm64.tar.gz`** - Complete package for FreeBSD ARM64 systems

          ### Individual Components
          - **`dms-cli-amd64.gz`** - DMS CLI binary for x86_64 systems
          - **`dms-cli-arm64.gz`** - DMS CLI binary for ARM64 systems
          - **`dms-cli-freebsd-amd64.gz`** - DMS CLI binary for FreeBSD x86_64 systems
          - **`dms-cli-freebsd-arm64.gz`** - DMS CLI binary for FreeBSD ARM64 systems
          - **`dms-distropkg-amd64.gz`** - DMS CLI binary built with distro_package tag for AMD64 systems
          - **`dms-distropkg-arm64.gz`** - DMS CLI binary built with distro_package tag for ARM64 systems
          - **`dms-distropkg-freebsd-amd64.gz`** - DMS CLI binary built with distro_package tag for FreeBSD x86_64 systems
          - **`dms-distropkg-freebsd-arm64.gz`** - DMS CLI binary built with distro_package tag for FreeBSD ARM64 systems
          - **`dankinstall-amd64.gz`** - Installer binary for x86_64 systems
          - **`dankinstall-arm64.gz`** - Installer binary for ARM64 systems
          - **`dms-cli-<version>.tar.gz`** - Go source code with vendored modules (for distro packaging)
          - **`dms-qml.tar.gz`** - QML source code only
          - **`dms-source.tar.gz`** - Full repository source with bundled DankCommon (for distro packaging)

          ### Checksums
          - **`*.sha256`** - SHA256 checksums for verifying download integrity

          **Installation:** Extract the `dms-full-*.tar.gz` package for your architecture and follow the `INSTALL.md` instructions inside.

          ---

          EOF

          cat >> RELEASE_BODY.md << EOF
          ## What's Changed

          $CHANGELOG

          **Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREVIOUS_TAG}...${TAG}
          EOF

          echo "changelog<<EOF" >> $GITHUB_OUTPUT
          cat RELEASE_BODY.md >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

      - name: Prepare release assets
        run: |
          set -euxo pipefail

          mkdir -p _release_assets

          # Copy core binaries and rename dms-*.gz to dms-cli-*.gz
          for file in _core_assets/dms-*.gz*; do
            if [ -f "$file" ]; then
              basename=$(basename "$file")
              if [[ "$basename" == dms-distropkg-* ]]; then
                cp "$file" "_release_assets/$basename"
              else
                newname=$(echo "$basename" | sed 's/^dms-/dms-cli-/')
                cp "$file" "_release_assets/$newname"
              fi
            fi
          done

          # Copy dankinstall binaries
          cp _core_assets/dankinstall-*.gz* _release_assets/

          # Copy completions
          cp _core_assets/completion.* _release_assets/ 2>/dev/null || true

          # Replace the DankCommon symlink with real submodule content for packaging
          rm quickshell/DankCommon
          cp -r dank-qml-common/DankCommon quickshell/DankCommon

          # Create full source tarball (GitHub tag archives never contain submodule content)
          VERSION_NUM=${TAG#v}
          tar --exclude='.git' \
              --exclude='.github' \
              --exclude='dank-qml-common' \
              --exclude='_release_assets' \
              --exclude='_core_assets' \
              --exclude='RELEASE_BODY.md' \
              --transform "s,^\.,DankMaterialShell-${VERSION_NUM},S" \
              -czf _release_assets/dms-source.tar.gz .
          (cd _release_assets && sha256sum dms-source.tar.gz > dms-source.tar.gz.sha256)

          # Create QML source package (exclude build artifacts and git files)
          # Copy root LICENSE and CONTRIBUTING.md to quickshell/ for packaging
          cp LICENSE CONTRIBUTING.md quickshell/

          # Copy root assets directory to quickshell for systemd service and desktop file
          cp -r assets quickshell/

          # Tar the CONTENTS of quickshell/, not the directory itself
          (cd quickshell && tar --exclude='.git' \
              --exclude='.github' \
              --exclude='*.tar.gz' \
              -czf ../_release_assets/dms-qml.tar.gz .)

          # Generate checksum for QML package
          (cd _release_assets && sha256sum dms-qml.tar.gz > dms-qml.tar.gz.sha256)

          # Create full packages for each os/architecture
          for arch in amd64 arm64 freebsd-amd64 freebsd-arm64; do
            mkdir -p _temp_full/dms
            mkdir -p _temp_full/bin
            mkdir -p _temp_full/completions

            # Extract QML source
            tar -xzf _release_assets/dms-qml.tar.gz -C _temp_full/dms

            # Add CLI binaries
            if [ -f "_core_assets/dms-${arch}.gz" ]; then
              gunzip -c "_core_assets/dms-${arch}.gz" > _temp_full/bin/dms
              chmod +x _temp_full/bin/dms
            fi

            if [ -f "_core_assets/dms-distropkg-${arch}.gz" ]; then
              gunzip -c "_core_assets/dms-distropkg-${arch}.gz" > _temp_full/bin/dms-distropkg
              chmod +x _temp_full/bin/dms-distropkg
            fi

            # Add shell completions
            for completion in _core_assets/completion.*; do
              if [ -f "$completion" ]; then
                cp "$completion" _temp_full/completions/
              fi
            done

            # Copy docs directory
            if [ -d "docs" ]; then
              cp -r docs _temp_full/
            fi

            # Create installation guide
            cat > _temp_full/INSTALL.md << 'EOFINSTALL'
          # DankMaterialShell Installation

          ## Requirements

          - Wayland compositor (niri or Hyprland recommended)
          - Quickshell framework
          - Qt6

          ## Installation Steps

          The Quickshell UI is embedded in the `dms` binary. The bundled `dms/`
          tree is optional — pass it with `-c` or `DMS_SHELL_DIR` to run a
          modified copy instead of the embedded UI.

          1. **Install the DMS CLI binaries:**
             ```bash
             sudo install -m 755 bin/dms /usr/local/bin/dms
             ```

          2. **Install shell completions (optional):**
             ```bash
             # Bash
             sudo install -m 644 completions/completion.bash /usr/share/bash-completion/completions/dms

             # Fish
             sudo install -m 644 completions/completion.fish /usr/share/fish/vendor_completions.d/dms.fish

             # Zsh
             sudo install -m 644 completions/completion.zsh /usr/share/zsh/site-functions/_dms
             ```

          3. **Start the shell:**
             ```bash
             dms run
             ```

          ## Configuration

          - Settings are stored in `~/.config/DankMaterialShell/settings.json`
          - Plugins go in `~/.config/DankMaterialShell/plugins/`
          - See the documentation in the `dms/` directory for more details

          ## Troubleshooting

          - Run with verbose output: `DMS_LOG_LEVEL=debug dms run`
          - Ensure all dependencies are installed
          EOFINSTALL

            # Create the full package
            (cd _temp_full && tar -czf "../_release_assets/dms-full-${arch}.tar.gz" .)

            # Generate checksum
            (cd _release_assets && sha256sum "dms-full-${arch}.tar.gz" > "dms-full-${arch}.tar.gz.sha256")

            # Cleanup
            rm -rf _temp_full
          done

      - name: Generate vendored source tarball
        run: |
          set -euxo pipefail
          VERSION_NUM=${TAG#v}
          cd core
          go mod vendor
          cd ..
          tar czf "_release_assets/dms-cli-${VERSION_NUM}.tar.gz" \
            --transform "s,^core/,dms-cli-${VERSION_NUM}/," \
            --exclude='core/.git' \
            core/
          (cd _release_assets && sha256sum "dms-cli-${VERSION_NUM}.tar.gz" > "dms-cli-${VERSION_NUM}.tar.gz.sha256")

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ env.TAG }}
          name: Release ${{ env.TAG }}
          body: ${{ steps.changelog.outputs.changelog }}
          files: _release_assets/**
          draft: false
          prerelease: ${{ contains(env.TAG, '-') }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      # Ask reporters on still-open "related/fixes #N" issues to retest.
      # Opt-in via notify_issues (Point release defaults it on). Skipped for
      # prereleases. Preview: scripts/notify-issues.sh <prev> <tag> --dry-run
      - name: Notify open issues to retest
        if: ${{ inputs.notify_issues && !contains(env.TAG, '-') }}
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          set -euo pipefail
          PREVIOUS_TAG=$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || true)
          if [ -z "$PREVIOUS_TAG" ]; then
            echo "no previous tag; skipping issue notify"
            exit 0
          fi
          bash scripts/notify-issues.sh "$PREVIOUS_TAG" "$TAG"
