name: Release

on:
  push:
    tags:
      - 'v*'

env:
  CARGO_TERM_COLOR: always
  CARGO_INCREMENTAL: 0

jobs:
  create-release:
    name: Create GitHub Release
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.extract_version.outputs.version }}
      upload_url: ${{ steps.create_release.outputs.upload_url }}
    steps:
      - name: Extract version from tag
        id: extract_version
        run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

      - name: Create Release
        id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ steps.extract_version.outputs.version }}
          draft: false
          prerelease: false

  build-release:
    name: Build ${{ matrix.target }}
    needs: create-release
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            use_cross: false
          - os: ubuntu-latest
            target: aarch64-unknown-linux-gnu
            use_cross: true
          - os: macos-latest
            target: x86_64-apple-darwin
            use_cross: false
          - os: macos-latest
            target: aarch64-apple-darwin
            use_cross: false
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            use_cross: false

    steps:
      - uses: actions/checkout@v4

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

      - name: Install cross
        if: matrix.use_cross
        run: cargo install cross --git https://github.com/cross-rs/cross

      - name: Cache cargo
        uses: Swatinem/rust-cache@v2
        with:
          key: ${{ matrix.target }}

      - name: Build release
        shell: bash
        run: |
          if [ "${{ matrix.use_cross }}" = "true" ]; then
            cross build --release --target ${{ matrix.target }} --locked
          else
            cargo build --release --target ${{ matrix.target }} --locked
          fi

      - name: Create distribution directory
        shell: bash
        run: |
          VERSION="${{ needs.create-release.outputs.version }}"
          TARGET="${{ matrix.target }}"
          DIST_DIR="typedialog-${VERSION}-${TARGET}"

          mkdir -p "$DIST_DIR"

          # Copy binary
          if [ "${{ matrix.os }}" = "windows-latest" ]; then
            cp target/${TARGET}/release/typedialog.exe "$DIST_DIR/"
            cp target/${TARGET}/release/typedialog-tui.exe "$DIST_DIR/"
            cp target/${TARGET}/release/typedialog-web.exe "$DIST_DIR/"
            cp target/${TARGET}/release/typedialog-ag.exe "$DIST_DIR/"
            cp target/${TARGET}/release/typedialog-ag-server.exe "$DIST_DIR/"
          else
            cp target/${TARGET}/release/typedialog "$DIST_DIR/"
            cp target/${TARGET}/release/typedialog-tui "$DIST_DIR/"
            cp target/${TARGET}/release/typedialog-web "$DIST_DIR/"
            cp target/${TARGET}/release/typedialog-ag "$DIST_DIR/"
            cp target/${TARGET}/release/typedialog-ag-server "$DIST_DIR/"
          fi

          # Copy README and LICENSE
          cp README.md "$DIST_DIR/" 2>/dev/null || true
          cp LICENSE "$DIST_DIR/" 2>/dev/null || true

          echo "DIST_DIR=$DIST_DIR" >> $GITHUB_ENV

      - name: Create tarball
        shell: bash
        run: |
          if [ "${{ matrix.os }}" = "windows-latest" ]; then
            7z a -tzip "${DIST_DIR}.zip" "${DIST_DIR}"
            echo "ASSET=${DIST_DIR}.zip" >> $GITHUB_ENV
          else
            tar czf "${DIST_DIR}.tar.gz" "${DIST_DIR}"
            echo "ASSET=${DIST_DIR}.tar.gz" >> $GITHUB_ENV
          fi

      - name: Generate SHA256 checksum
        shell: bash
        run: |
          if [ "${{ matrix.os }}" = "windows-latest" ]; then
            certutil -hashfile "${ASSET}" SHA256 > "${ASSET}.sha256"
          else
            shasum -a 256 "${ASSET}" > "${ASSET}.sha256"
          fi

      - name: Upload release asset
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ needs.create-release.outputs.upload_url }}
          asset_path: ${{ env.ASSET }}
          asset_name: ${{ env.ASSET }}
          asset_content_type: application/gzip

      - name: Upload checksum
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ needs.create-release.outputs.upload_url }}
          asset_path: ${{ env.ASSET }}.sha256
          asset_name: ${{ env.ASSET }}.sha256
          asset_content_type: text/plain

  generate-sbom:
    name: Generate SBOM
    needs: create-release
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Install cargo-sbom
        run: cargo install cargo-sbom --locked

      - name: Generate SPDX SBOM
        run: cargo sbom --output-format spdx_json_2_3 > sbom-spdx.json

      - name: Generate CycloneDX SBOM
        run: cargo sbom --output-format cyclone_dx_json_1_4 > sbom-cyclonedx.json

      - name: Upload SPDX SBOM
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ needs.create-release.outputs.upload_url }}
          asset_path: sbom-spdx.json
          asset_name: sbom-spdx.json
          asset_content_type: application/json

      - name: Upload CycloneDX SBOM
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ needs.create-release.outputs.upload_url }}
          asset_path: sbom-cyclonedx.json
          asset_name: sbom-cyclonedx.json
          asset_content_type: application/json

  publish-crates:
    name: Publish to crates.io
    needs: [create-release, build-release, generate-sbom]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Verify version matches tag
        run: |
          TAG_VERSION="${{ needs.create-release.outputs.version }}"
          CARGO_VERSION=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "typedialog-core") | .version')

          if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then
            echo "Tag version ($TAG_VERSION) does not match Cargo.toml version ($CARGO_VERSION)"
            exit 1
          fi

      - name: Publish typedialog-core
        run: cargo publish -p typedialog-core --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

      - name: Wait for crates.io
        run: sleep 30

      - name: Publish typedialog-ag-core
        run: cargo publish -p typedialog-ag-core --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

      - name: Wait for crates.io
        run: sleep 30

      - name: Publish typedialog-ai
        run: cargo publish -p typedialog-ai --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

      - name: Wait for crates.io
        run: sleep 30

      - name: Publish typedialog-prov-gen
        run: cargo publish -p typedialog-prov-gen --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

      - name: Wait for crates.io
        run: sleep 30

      - name: Publish typedialog
        run: cargo publish -p typedialog --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

      - name: Wait for crates.io
        run: sleep 30

      - name: Publish typedialog-tui
        run: cargo publish -p typedialog-tui --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

      - name: Wait for crates.io
        run: sleep 30

      - name: Publish typedialog-web
        run: cargo publish -p typedialog-web --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

      - name: Wait for crates.io
        run: sleep 30

      - name: Publish typedialog-ag
        run: cargo publish -p typedialog-ag --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

      - name: Wait for crates.io
        run: sleep 30

      - name: Publish typedialog-ag-server
        run: cargo publish -p typedialog-ag-server --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
