name: Release

on:
  push:
    tags:
      - 'v*.*.*'
  workflow_dispatch:
    inputs:
      version:
        description: 'Version to release (e.g., v1.0.0)'
        required: true
        type: string

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  validate-release:
    name: Validate Release
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.get-version.outputs.version }}
      tag: ${{ steps.get-version.outputs.tag }}
    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Get version from tag or input
      id: get-version
      run: |
        if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
          VERSION="${{ github.event.inputs.version }}"
          echo "version=${VERSION#v}" >> $GITHUB_OUTPUT
          echo "tag=${VERSION}" >> $GITHUB_OUTPUT
        else
          VERSION="${GITHUB_REF#refs/tags/}"
          echo "version=${VERSION#v}" >> $GITHUB_OUTPUT
          echo "tag=${VERSION}" >> $GITHUB_OUTPUT
        fi

    - name: Validate version format
      run: |
        VERSION="${{ steps.get-version.outputs.version }}"
        if ! [[ $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then
          echo "❌ Invalid version format: $VERSION"
          echo "Expected format: X.Y.Z or X.Y.Z-suffix"
          exit 1
        fi
        echo "✅ Valid version format: $VERSION"

    - name: Check if version matches Cargo.toml
      run: |
        CARGO_VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
        INPUT_VERSION="${{ steps.get-version.outputs.version }}"
        if [ "$CARGO_VERSION" != "$INPUT_VERSION" ]; then
          echo "❌ Version mismatch:"
          echo "  Cargo.toml: $CARGO_VERSION"
          echo "  Tag/Input:  $INPUT_VERSION"
          exit 1
        fi
        echo "✅ Version matches Cargo.toml: $INPUT_VERSION"

  test:
    name: Run Tests
    runs-on: ${{ matrix.os }}
    needs: validate-release
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        rust: [stable]
    steps:
    - name: Checkout code
      uses: actions/checkout@v4

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

    - name: Cache Cargo registry
      uses: actions/cache@v4
      with:
        path: |
          ~/.cargo/registry
          ~/.cargo/git
          target
        key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }}

    - name: Install system dependencies (Ubuntu)
      if: matrix.os == 'ubuntu-latest'
      run: |
        sudo apt-get update
        sudo apt-get install -y \
          build-essential \
          pkg-config \
          libssl-dev \
          libfontconfig1-dev \
          libfreetype6-dev \
          libjpeg-dev \
          libpng-dev

    - name: Install system dependencies (macOS)
      if: matrix.os == 'macos-latest'
      run: |
        brew update
        brew install pkg-config freetype jpeg libpng

    - name: Run tests (library only)
      run: cargo test --verbose --lib

  build:
    name: Build Release Artifacts
    runs-on: ${{ matrix.job.os }}
    needs: [validate-release, test]
    strategy:
      matrix:
        job:
          - { os: ubuntu-latest,  target: x86_64-unknown-linux-gnu,      use-cross: false }
          - { os: ubuntu-latest,  target: x86_64-unknown-linux-musl,     use-cross: true }
          - { os: ubuntu-latest,  target: aarch64-unknown-linux-gnu,     use-cross: true }
          - { os: ubuntu-latest,  target: aarch64-unknown-linux-musl,    use-cross: true }
          - { os: windows-latest, target: x86_64-pc-windows-msvc,        use-cross: false }
          - { os: macos-latest,   target: x86_64-apple-darwin,           use-cross: false }
          - { os: macos-latest,   target: aarch64-apple-darwin,          use-cross: false }

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

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

    - name: Install cross compilation tools
      if: matrix.job.use-cross
      run: |
        cargo install cross --git https://github.com/cross-rs/cross

    - name: Cache Cargo registry
      uses: actions/cache@v4
      with:
        path: |
          ~/.cargo/registry
          ~/.cargo/git
          target
        key: ${{ runner.os }}-${{ matrix.job.target }}-cargo-release-${{ hashFiles('**/Cargo.lock') }}

    - name: Install system dependencies (Ubuntu)
      if: matrix.job.os == 'ubuntu-latest' && !matrix.job.use-cross
      run: |
        sudo apt-get update
        sudo apt-get install -y \
          build-essential \
          pkg-config \
          libssl-dev \
          libfontconfig1-dev \
          libfreetype6-dev \
          libjpeg-dev \
          libpng-dev

    - name: Install system dependencies (macOS)
      if: matrix.job.os == 'macos-latest'
      run: |
        brew update
        brew install pkg-config freetype jpeg libpng

    - name: Build release binary
      run: |
        if [ "${{ matrix.job.use-cross }}" = "true" ]; then
          cross build --release --target ${{ matrix.job.target }}
        else
          cargo build --release --target ${{ matrix.job.target }}
        fi

    - name: Prepare release archive
      shell: bash
      run: |
        VERSION="${{ needs.validate-release.outputs.version }}"
        TARGET="${{ matrix.job.target }}"
        
        # Create staging directory
        mkdir -p staging
        
        # Copy binary
        if [[ "${{ matrix.job.os }}" == "windows-latest" ]]; then
          cp "target/${TARGET}/release/docx-mcp.exe" staging/
          BINARY="docx-mcp.exe"
        else
          cp "target/${TARGET}/release/docx-mcp" staging/
          BINARY="docx-mcp"
        fi
        
        # Copy additional files
        cp README.md staging/
        cp LICENSE staging/
        
        # Create archive name
        ARCHIVE="docx-mcp-${VERSION}-${TARGET}"
        
        # Create archive
        cd staging
        if [[ "${{ matrix.job.os }}" == "windows-latest" ]]; then
          7z a "../${ARCHIVE}.zip" *
          echo "ARCHIVE=${ARCHIVE}.zip" >> $GITHUB_ENV
        else
          tar czf "../${ARCHIVE}.tar.gz" *
          echo "ARCHIVE=${ARCHIVE}.tar.gz" >> $GITHUB_ENV
        fi
        cd ..
        
        # Generate checksums
        if [[ "${{ matrix.job.os }}" == "windows-latest" ]]; then
          certutil -hashfile "${ARCHIVE}.zip" SHA256 > "${ARCHIVE}.zip.sha256"
        else
          shasum -a 256 "${ARCHIVE}.tar.gz" > "${ARCHIVE}.tar.gz.sha256"
        fi
        
        echo "BINARY=${BINARY}" >> $GITHUB_ENV

    - name: Upload release artifacts
      uses: actions/upload-artifact@v4
      with:
        name: release-${{ matrix.job.target }}
        path: |
          ${{ env.ARCHIVE }}
          ${{ env.ARCHIVE }}.sha256

  create-release:
    name: Create GitHub Release
    runs-on: ubuntu-latest
    needs: [validate-release, build]
    permissions:
      contents: write
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Download all artifacts
      uses: actions/download-artifact@v4
      with:
        path: artifacts

    - name: Prepare release assets
      run: |
        mkdir -p release-assets
        find artifacts -type f -name "*.tar.gz" -o -name "*.zip" -o -name "*.sha256" | \
          xargs -I {} cp {} release-assets/
        ls -la release-assets/

    - name: Generate changelog
      id: changelog
      run: |
        VERSION="${{ needs.validate-release.outputs.version }}"
        TAG="${{ needs.validate-release.outputs.tag }}"
        
        # Get previous tag
        PREV_TAG=$(git tag --sort=-version:refname | grep -v "^${TAG}$" | head -1)
        
        echo "Generating changelog from ${PREV_TAG} to ${TAG}"
        
        # Generate changelog
        if [ -n "$PREV_TAG" ]; then
          CHANGELOG=$(git log --pretty=format:"- %s (%h)" --no-merges ${PREV_TAG}..HEAD)
        else
          CHANGELOG=$(git log --pretty=format:"- %s (%h)" --no-merges)
        fi
        
        # Create release notes
        cat > release-notes.md << EOF
        ## What's Changed
        
        ${CHANGELOG}
        
        ## Installation
        
        ### Pre-built Binaries
        
        Download the appropriate binary for your system:
        
        - **Linux x86_64**: \`docx-mcp-${VERSION}-x86_64-unknown-linux-gnu.tar.gz\`
        - **Linux x86_64 (musl)**: \`docx-mcp-${VERSION}-x86_64-unknown-linux-musl.tar.gz\`
        - **Linux ARM64**: \`docx-mcp-${VERSION}-aarch64-unknown-linux-gnu.tar.gz\`
        - **macOS Intel**: \`docx-mcp-${VERSION}-x86_64-apple-darwin.tar.gz\`
        - **macOS Apple Silicon**: \`docx-mcp-${VERSION}-aarch64-apple-darwin.tar.gz\`
        - **Windows x86_64**: \`docx-mcp-${VERSION}-x86_64-pc-windows-msvc.zip\`
        
        ### From Source
        
        \`\`\`bash
        cargo install --git https://github.com/hongkongkiwi/docx-mcp --tag ${TAG}
        \`\`\`
        
        ### Verification
        
        All binaries are provided with SHA256 checksums for verification:
        
        \`\`\`bash
        # Linux/macOS
        shasum -a 256 -c docx-mcp-${VERSION}-your-target.tar.gz.sha256
        
        # Windows
        certutil -hashfile docx-mcp-${VERSION}-x86_64-pc-windows-msvc.zip SHA256
        \`\`\`
        
        ## Full Changelog
        
        **Full Changelog**: https://github.com/hongkongkiwi/docx-mcp/compare/${PREV_TAG}...${TAG}
        EOF
        
        echo "CHANGELOG_FILE=release-notes.md" >> $GITHUB_OUTPUT

    - name: Create Release
      uses: softprops/action-gh-release@v2
      with:
        tag_name: ${{ needs.validate-release.outputs.tag }}
        name: Release ${{ needs.validate-release.outputs.tag }}
        body_path: ${{ steps.changelog.outputs.CHANGELOG_FILE }}
        files: release-assets/*
        draft: false
        prerelease: ${{ contains(needs.validate-release.outputs.version, '-') }}
        generate_release_notes: false
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  publish-crate:
    name: Publish to crates.io
    runs-on: ubuntu-latest
    needs: [validate-release, create-release]
    if: ${{ !contains(needs.validate-release.outputs.version, '-') }} # Only publish stable releases
    steps:
    - name: Checkout code
      uses: actions/checkout@v4

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

    - name: Install system dependencies
      run: |
        sudo apt-get update
        sudo apt-get install -y \
          build-essential \
          pkg-config \
          libssl-dev \
          libfontconfig1-dev \
          libfreetype6-dev \
          libjpeg-dev \
          libpng-dev

    - name: Cache Cargo registry
      uses: actions/cache@v4
      with:
        path: |
          ~/.cargo/registry
          ~/.cargo/git
          target
        key: ubuntu-cargo-publish-${{ hashFiles('**/Cargo.lock') }}

    - name: Verify package
      run: cargo package --dry-run

    - name: Publish to crates.io
      run: cargo publish
      env:
        CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

  docker-release:
    name: Build and Push Docker Images
    runs-on: ubuntu-latest
    needs: [validate-release, create-release]
    permissions:
      contents: read
      packages: write
    steps:
    - name: Checkout code
      uses: actions/checkout@v4

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

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

    - name: Login to Docker Hub
      if: ${{ secrets.DOCKERHUB_USERNAME && secrets.DOCKERHUB_TOKEN }}
      uses: docker/login-action@v3
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_TOKEN }}

    - name: Extract metadata
      id: meta
      uses: docker/metadata-action@v5
      with:
        images: |
          ghcr.io/${{ github.repository }}
          ${{ secrets.DOCKERHUB_USERNAME && format('{0}/docx-mcp', secrets.DOCKERHUB_USERNAME) || '' }}
        tags: |
          type=ref,event=tag
          type=semver,pattern={{version}}
          type=semver,pattern={{major}}.{{minor}}
          type=semver,pattern={{major}}
          type=raw,value=latest,enable={{is_default_branch}}

    - name: Build and push Docker image
      uses: docker/build-push-action@v5
      with:
        context: .
        file: ./Dockerfile
        platforms: linux/amd64,linux/arm64
        push: true
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}
        cache-from: type=gha
        cache-to: type=gha,mode=max

  update-docs:
    name: Update Documentation
    runs-on: ubuntu-latest
    needs: [validate-release, create-release]
    permissions:
      contents: write
      pages: write
      id-token: write
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        token: ${{ secrets.GITHUB_TOKEN }}

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

    - name: Install system dependencies
      run: |
        sudo apt-get update
        sudo apt-get install -y \
          build-essential \
          pkg-config \
          libssl-dev \
          libfontconfig1-dev \
          libfreetype6-dev \
          libjpeg-dev \
          libpng-dev

    - name: Generate documentation
      run: |
        cargo doc --all-features --no-deps
        echo '<meta http-equiv="refresh" content="0; url=docx_mcp">' > target/doc/index.html

    - name: Deploy to GitHub Pages
      uses: peaceiris/actions-gh-pages@v4
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./target/doc
        cname: docs.example.com # Replace with your custom domain if you have one

  notify-success:
    name: Notify Release Success
    runs-on: ubuntu-latest
    needs: [create-release, publish-crate, docker-release, update-docs]
    if: success()
    steps:
    - name: Success notification
      run: |
        echo "🎉 Release ${{ needs.validate-release.outputs.tag }} completed successfully!"
        echo "- ✅ GitHub release created"
        echo "- ✅ Binaries built for all platforms"
        echo "- ✅ Published to crates.io"
        echo "- ✅ Docker images pushed"
        echo "- ✅ Documentation updated"

  notify-failure:
    name: Notify Release Failure
    runs-on: ubuntu-latest
    needs: [validate-release, test, build, create-release, publish-crate, docker-release, update-docs]
    if: failure()
    steps:
    - name: Failure notification
      run: |
        echo "❌ Release ${{ needs.validate-release.outputs.tag }} failed!"
        echo "Please check the workflow logs for details."
        exit 1