name: Release

on:
  push:
    tags:
      - "v*"

permissions:
  contents: write
  packages: write

env:
  CARGO_TERM_COLOR: always

jobs:
  create-release:
    name: Create Release
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Get version from tag
        id: get_version
        run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

      - name: Extract changelog
        id: changelog
        run: |
          # Extract the version section from CHANGELOG.md
          VERSION="${GITHUB_REF#refs/tags/v}"
          sed -n "/## \[$VERSION\]/,/## \[/p" CHANGELOG.md | sed '1d;$d' > release_notes.md
          cat release_notes.md

      - name: Create Release
        uses: softprops/action-gh-release@v2
        with:
          body_path: release_notes.md
          draft: false
          prerelease: false
          generate_release_notes: false

  build-release:
    name: Build Release Binary
    needs: create-release
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            artifact_name: mkdlint
            lsp_artifact_name: mkdlint-lsp
            asset_name: mkdlint-linux-x86_64
          - os: ubuntu-latest
            target: aarch64-unknown-linux-gnu
            artifact_name: mkdlint
            lsp_artifact_name: mkdlint-lsp
            asset_name: mkdlint-linux-aarch64
          - os: macos-latest
            target: x86_64-apple-darwin
            artifact_name: mkdlint
            lsp_artifact_name: mkdlint-lsp
            asset_name: mkdlint-macos-x86_64
          - os: macos-latest
            target: aarch64-apple-darwin
            artifact_name: mkdlint
            lsp_artifact_name: mkdlint-lsp
            asset_name: mkdlint-macos-aarch64
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            artifact_name: mkdlint.exe
            lsp_artifact_name: mkdlint-lsp.exe
            asset_name: mkdlint-windows-x86_64.exe

    steps:
      - uses: actions/checkout@v6

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - name: Install cross-compilation tools (Linux ARM64)
        if: matrix.target == 'aarch64-unknown-linux-gnu'
        run: |
          sudo apt-get update
          sudo apt-get install -y gcc-aarch64-linux-gnu

      - name: Configure ARM64 linker
        if: matrix.target == 'aarch64-unknown-linux-gnu'
        run: |
          mkdir -p .cargo
          echo '[target.aarch64-unknown-linux-gnu]' >> .cargo/config.toml
          echo 'linker = "aarch64-linux-gnu-gcc"' >> .cargo/config.toml

      - name: Build release binaries (CLI + LSP)
        run: cargo build --release --target ${{ matrix.target }} --features lsp

      - name: Strip binaries (Linux x86_64)
        if: matrix.target == 'x86_64-unknown-linux-gnu'
        run: |
          strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
          strip target/${{ matrix.target }}/release/${{ matrix.lsp_artifact_name }}

      - name: Strip binaries (Linux ARM64)
        if: matrix.target == 'aarch64-unknown-linux-gnu'
        run: |
          aarch64-linux-gnu-strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
          aarch64-linux-gnu-strip target/${{ matrix.target }}/release/${{ matrix.lsp_artifact_name }}

      - name: Strip binaries (macOS)
        if: startsWith(matrix.os, 'macos')
        run: |
          strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
          strip target/${{ matrix.target }}/release/${{ matrix.lsp_artifact_name }}

      - name: Create archive
        shell: bash
        run: |
          cd target/${{ matrix.target }}/release
          if [ "${{ matrix.os }}" = "windows-latest" ]; then
            7z a ../../../${{ matrix.asset_name }}.zip ${{ matrix.artifact_name }} ${{ matrix.lsp_artifact_name }}
          else
            tar czf ../../../${{ matrix.asset_name }}.tar.gz ${{ matrix.artifact_name }} ${{ matrix.lsp_artifact_name }}
          fi

      - name: Generate SHA256 checksums
        shell: bash
        run: |
          if [ "${{ matrix.os }}" = "windows-latest" ]; then
            ARCHIVE="${{ matrix.asset_name }}.zip"
          else
            ARCHIVE="${{ matrix.asset_name }}.tar.gz"
          fi

          if command -v sha256sum >/dev/null 2>&1; then
            sha256sum "$ARCHIVE" > "${ARCHIVE}.sha256"
          elif command -v shasum >/dev/null 2>&1; then
            shasum -a 256 "$ARCHIVE" > "${ARCHIVE}.sha256"
          else
            echo "::error::No SHA256 utility found"
            exit 1
          fi

          echo "Generated checksum:"
          cat "${ARCHIVE}.sha256"

      - name: Upload Release Asset
        uses: softprops/action-gh-release@v2
        with:
          files: |
            ./${{ matrix.asset_name }}${{ matrix.os == 'windows-latest' && '.zip' || '.tar.gz' }}
            ./${{ matrix.asset_name }}${{ matrix.os == 'windows-latest' && '.zip' || '.tar.gz' }}.sha256

      - name: Upload LSP binary artifact
        uses: actions/upload-artifact@v4
        with:
          name: lsp-${{ matrix.target }}
          path: target/${{ matrix.target }}/release/${{ matrix.lsp_artifact_name }}

  build-vscode-extension:
    name: Build VS Code Extension (${{ matrix.vscode_target }})
    needs: build-release
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - vscode_target: linux-x64
            rust_target: x86_64-unknown-linux-gnu
            lsp_binary: mkdlint-lsp
          - vscode_target: linux-arm64
            rust_target: aarch64-unknown-linux-gnu
            lsp_binary: mkdlint-lsp
          - vscode_target: darwin-x64
            rust_target: x86_64-apple-darwin
            lsp_binary: mkdlint-lsp
          - vscode_target: darwin-arm64
            rust_target: aarch64-apple-darwin
            lsp_binary: mkdlint-lsp
          - vscode_target: win32-x64
            rust_target: x86_64-pc-windows-msvc
            lsp_binary: mkdlint-lsp.exe
    steps:
      - uses: actions/checkout@v6

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Install dependencies
        working-directory: editors/vscode
        run: npm ci

      - name: Compile TypeScript
        working-directory: editors/vscode
        run: npm run compile

      - name: Download LSP binary
        uses: actions/download-artifact@v4
        with:
          name: lsp-${{ matrix.rust_target }}
          path: editors/vscode/server/

      - name: Make binary executable
        if: matrix.vscode_target != 'win32-x64'
        run: chmod +x editors/vscode/server/${{ matrix.lsp_binary }}

      - name: Package VSIX
        working-directory: editors/vscode
        run: npx vsce package --target ${{ matrix.vscode_target }}

      - name: Upload VSIX to release
        uses: softprops/action-gh-release@v2
        with:
          files: editors/vscode/*.vsix

      - name: Upload VSIX artifact
        uses: actions/upload-artifact@v4
        with:
          name: vsix-${{ matrix.vscode_target }}
          path: editors/vscode/*.vsix

  build-vscode-extension-universal:
    name: Build VS Code Extension (universal)
    needs: create-release
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Install dependencies
        working-directory: editors/vscode
        run: npm ci

      - name: Compile TypeScript
        working-directory: editors/vscode
        run: npm run compile

      - name: Package universal VSIX
        working-directory: editors/vscode
        run: npx vsce package

      - name: Upload VSIX to release
        uses: softprops/action-gh-release@v2
        with:
          files: editors/vscode/*.vsix

      - name: Upload VSIX artifact
        uses: actions/upload-artifact@v4
        with:
          name: vsix-universal
          path: editors/vscode/*.vsix

  publish-vscode-extension:
    name: Publish to VS Code Marketplace
    needs: [build-vscode-extension, build-vscode-extension-universal]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Install dependencies
        working-directory: editors/vscode
        run: npm ci

      - name: Download all VSIX artifacts
        uses: actions/download-artifact@v4
        with:
          pattern: vsix-*
          path: editors/vscode/vsix/
          merge-multiple: true

      - name: Publish to Marketplace
        working-directory: editors/vscode
        run: npx vsce publish --packagePath vsix/*.vsix
        env:
          VSCE_PAT: ${{ secrets.VSCE_PAT }}

  publish-crate:
    name: Publish to crates.io
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: Verify version matches tag
        run: |
          CARGO_VERSION=$(grep '^version =' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
          TAG_VERSION=${GITHUB_REF#refs/tags/v}
          if [ "$CARGO_VERSION" != "$TAG_VERSION" ]; then
            echo "Version mismatch: Cargo.toml has $CARGO_VERSION but tag is $TAG_VERSION"
            exit 1
          fi
          echo "Version check passed: $CARGO_VERSION"
      - name: Run tests
        run: cargo test --verbose

      - name: Run clippy
        run: cargo clippy --all-targets --all-features -- -D warnings

      - name: Check documentation
        run: cargo doc --no-deps

      - name: Publish to crates.io
        run: cargo publish --token ${{ secrets.CRATES_IO }}

  publish-docker:
    name: Publish Docker Image
    needs: create-release
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract version from tag
        id: version
        run: echo "version=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"

      - name: Build and push Docker image
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          platforms: linux/amd64,linux/arm64
          tags: |
            ghcr.io/192d-wing/mkdlint:latest
            ghcr.io/192d-wing/mkdlint:${{ steps.version.outputs.version }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
