# Plugin Release Workflow
#
# This workflow automates the release process for OpenWebUI plugins.
#
# Triggers:
# - Push to main branch when plugins are modified (auto-release)
# - Manual trigger (workflow_dispatch) with custom release notes
# - Push of plugin version tags (<plugin>-v*)
#
# What it does:
# 1. Detects plugin version changes compared to the last release
# 2. Generates release notes with updated plugin information
# 3. Creates a GitHub Release with plugin files as downloadable assets
# 4. Enforces one plugin creation/update per release

name: Plugin Release

on:
  # Auto-trigger on push to main when plugins are modified
  push:
    branches:
      - main
    paths:
      - 'plugins/**/*.py'
      - 'plugins/**/README.md'
      - 'plugins/**/README_CN.md'
      - 'plugins/**/v*.md'
      - 'plugins/**/v*_CN.md'
      - 'docs/plugins/**/*.md'
    tags:
      - '*-v*'
      - 'v*'

  # Manual trigger with inputs
  workflow_dispatch:
    inputs:
      version:
        description: 'Release tag (e.g., markdown-normalizer-v1.2.8). Leave empty for auto-generated tag.'
        required: false
        type: string
      release_title:
        description: 'Release title (optional)'
        required: false
        type: string
      release_notes:
        description: 'Additional release notes (Markdown)'
        required: false
        type: string
      prerelease:
        description: 'Mark as pre-release'
        required: false
        type: boolean
        default: false

permissions:
  contents: write

jobs:
  check-changes:
    runs-on: ubuntu-latest
    # Skip release if commit message contains [skip release]
    if: ${{ !contains(github.event.head_commit.message, '[skip release]') }}
    env:
      LANG: en_US.UTF-8
      LC_ALL: en_US.UTF-8
    outputs:
      has_changes: ${{ steps.detect.outputs.has_changes }}
      changed_plugins: ${{ steps.detect.outputs.changed_plugins }}
      changed_plugin_title: ${{ steps.detect.outputs.changed_plugin_title }}
      changed_plugin_slug: ${{ steps.detect.outputs.changed_plugin_slug }}
      changed_plugin_version: ${{ steps.detect.outputs.changed_plugin_version }}
      changed_plugin_count: ${{ steps.detect.outputs.changed_plugin_count }}
      release_notes: ${{ steps.detect.outputs.release_notes }}
      has_doc_changes: ${{ steps.detect.outputs.has_doc_changes }}
      changed_doc_files: ${{ steps.detect.outputs.changed_doc_files }}
      previous_release_tag: ${{ steps.detect.outputs.previous_release_tag }}
      compare_ref: ${{ steps.detect.outputs.compare_ref }}
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Configure Git
        run: |
          git config --global core.quotepath false
          git config --global i18n.commitencoding utf-8
          git config --global i18n.logoutputencoding utf-8

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Detect plugin changes
        id: detect
        run: |
          # Always compare against the most recent previously released version.
          CURRENT_TAG=""
          if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
            CURRENT_TAG="${GITHUB_REF#refs/tags/}"
            echo "Current tag event detected: $CURRENT_TAG"
          fi

          PREVIOUS_RELEASE_TAG=$(git tag --sort=-creatordate | grep -Fxv "$CURRENT_TAG" | head -n1 || true)

          if [ -n "$PREVIOUS_RELEASE_TAG" ]; then
            echo "Comparing with previous release tag: $PREVIOUS_RELEASE_TAG"
            COMPARE_REF="$PREVIOUS_RELEASE_TAG"
          else
            COMPARE_REF="$(git rev-list --max-parents=0 HEAD)"
            echo "No previous release tag found, using repository root commit: $COMPARE_REF"
          fi

          echo "previous_release_tag=$PREVIOUS_RELEASE_TAG" >> "$GITHUB_OUTPUT"
          echo "compare_ref=$COMPARE_REF" >> "$GITHUB_OUTPUT"
          
          # Get current plugin versions
          python scripts/extract_plugin_versions.py --json --output current_versions.json
          
          # Get previous plugin versions by checking out old plugins
          OLD_WORKTREE=$(mktemp -d)
          if git worktree add "$OLD_WORKTREE" ${COMPARE_REF} 2>/dev/null; then
            if [ -d "$OLD_WORKTREE/plugins" ]; then
              python scripts/extract_plugin_versions.py --plugins-dir "$OLD_WORKTREE/plugins" --json --output old_versions.json
            else
              echo "[]" > old_versions.json
            fi
            git worktree remove "$OLD_WORKTREE" 2>/dev/null || true
          else
            echo "Failed to create worktree, using empty version list"
            echo "[]" > old_versions.json
          fi
          rm -rf "$OLD_WORKTREE" 2>/dev/null || true
          
          # Compare versions and generate release notes
          python scripts/extract_plugin_versions.py --compare old_versions.json --ignore-removed --output changes.md
          python scripts/extract_plugin_versions.py --compare old_versions.json --json --output changes.json
          
          echo "=== Version Changes ==="
          cat changes.md

          # Detect documentation/release-note changes that should be reflected in release notes
          git diff --name-only "$COMPARE_REF"..HEAD -- \
            'plugins/**/README.md' \
            'plugins/**/README_CN.md' \
            'plugins/**/v*.md' \
            'plugins/**/v*_CN.md' \
            'docs/plugins/**/*.md' > changed_docs.txt || true

          if [ -s changed_docs.txt ]; then
            echo "has_doc_changes=true" >> $GITHUB_OUTPUT
            echo "changed_doc_files<<EOF" >> $GITHUB_OUTPUT
            cat changed_docs.txt >> $GITHUB_OUTPUT
            echo "" >> $GITHUB_OUTPUT
            echo "EOF" >> $GITHUB_OUTPUT
          else
            echo "has_doc_changes=false" >> $GITHUB_OUTPUT
            echo "changed_doc_files=" >> $GITHUB_OUTPUT
          fi
          
          # Check if there are any changes
          if grep -q "No changes detected" changes.md; then
            # Only trigger release if there are actual version changes, not just doc changes
            echo "has_changes=false" >> $GITHUB_OUTPUT
            echo "changed_plugins=" >> $GITHUB_OUTPUT
            echo "changed_plugin_title=" >> $GITHUB_OUTPUT
            echo "changed_plugin_slug=" >> $GITHUB_OUTPUT
            echo "changed_plugin_version=" >> $GITHUB_OUTPUT
            echo "changed_plugin_count=0" >> $GITHUB_OUTPUT
          else
            echo "has_changes=true" >> $GITHUB_OUTPUT

            # Extract changed plugin metadata and enforce a single-plugin release.
            python3 <<'PY'
          import json
          import sys
          from pathlib import Path

          data = json.load(open('changes.json', 'r', encoding='utf-8'))

          def get_plugin_meta(plugin):
            manifest = plugin.get('data', {}).get('function', {}).get('meta', {}).get('manifest', {})
            title = (manifest.get('title') or plugin.get('title') or '').strip()
            version = (manifest.get('version') or plugin.get('version') or '').strip()
            file_path = (plugin.get('file_path') or '').strip()
            slug = Path(file_path).parent.name.replace('_', '-').strip() if file_path else ''
            return {
              'title': title,
              'slug': slug,
              'version': version,
              'file_path': file_path,
            }

          plugins = []
          seen_keys = set()

          for plugin in data.get('added', []):
            meta = get_plugin_meta(plugin)
            key = meta['file_path'] or meta['title']
            if key and key not in seen_keys:
              plugins.append(meta)
              seen_keys.add(key)

          for update in data.get('updated', []):
            meta = get_plugin_meta(update.get('current', {}))
            key = meta['file_path'] or meta['title']
            if key and key not in seen_keys:
              plugins.append(meta)
              seen_keys.add(key)

          Path('changed_files.txt').write_text(
            '\n'.join(meta['file_path'] for meta in plugins if meta['file_path']),
            encoding='utf-8',
          )
          Path('changed_plugin_count.txt').write_text(str(len(plugins)), encoding='utf-8')

          if len(plugins) > 1:
            print('Error: release workflow only supports one plugin creation/update per release.', file=sys.stderr)
            for meta in plugins:
              print(
                f"- {meta['title'] or 'Unknown'} v{meta['version'] or '?'} ({meta['file_path'] or 'unknown path'})",
                file=sys.stderr,
              )
            sys.exit(1)

          selected = plugins[0] if plugins else {'title': '', 'slug': '', 'version': ''}
          Path('changed_plugin_title.txt').write_text(selected['title'], encoding='utf-8')
          Path('changed_plugin_slug.txt').write_text(selected['slug'], encoding='utf-8')
          Path('changed_plugin_version.txt').write_text(selected['version'], encoding='utf-8')
          PY
            
            echo "changed_plugins<<EOF" >> $GITHUB_OUTPUT
            cat changed_files.txt >> $GITHUB_OUTPUT
            echo "" >> $GITHUB_OUTPUT
            echo "EOF" >> $GITHUB_OUTPUT

            echo "changed_plugin_title=$(cat changed_plugin_title.txt)" >> $GITHUB_OUTPUT
            echo "changed_plugin_slug=$(cat changed_plugin_slug.txt)" >> $GITHUB_OUTPUT
            echo "changed_plugin_version=$(cat changed_plugin_version.txt)" >> $GITHUB_OUTPUT
            echo "changed_plugin_count=$(cat changed_plugin_count.txt)" >> $GITHUB_OUTPUT
          fi
          
          # Store release notes
          {
            echo 'release_notes<<EOF'
            cat changes.md
            echo ""
            echo 'EOF'
          } >> $GITHUB_OUTPUT

  release:
    needs: check-changes
    if: needs.check-changes.outputs.has_changes == 'true' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/')
    runs-on: ubuntu-latest
    env:
      LANG: en_US.UTF-8
      LC_ALL: en_US.UTF-8

    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Configure Git
        run: |
          git config --global core.quotepath false
          git config --global i18n.commitencoding utf-8
          git config --global i18n.logoutputencoding utf-8

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Determine version
        id: version
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          CHANGED_PLUGIN_SLUG: ${{ needs.check-changes.outputs.changed_plugin_slug }}
          CHANGED_PLUGIN_VERSION: ${{ needs.check-changes.outputs.changed_plugin_version }}
        run: |
          if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ github.event.inputs.version }}" ]; then
            VERSION="${{ github.event.inputs.version }}"
          elif [[ "${{ github.ref }}" == refs/tags/* ]]; then
            VERSION="${GITHUB_REF#refs/tags/}"
          elif [ -n "$CHANGED_PLUGIN_SLUG" ] && [ -n "$CHANGED_PLUGIN_VERSION" ]; then
            VERSION="${CHANGED_PLUGIN_SLUG}-v${CHANGED_PLUGIN_VERSION}"
          else
            echo "Error: failed to determine plugin-scoped release tag." >&2
            exit 1
          fi
          echo "version=$VERSION" >> $GITHUB_OUTPUT
          echo "Release tag: $VERSION"

      - name: Build release metadata
        id: meta
        env:
          VERSION: ${{ steps.version.outputs.version }}
          INPUT_TITLE: ${{ github.event.inputs.release_title }}
          CHANGED_PLUGIN_TITLE: ${{ needs.check-changes.outputs.changed_plugin_title }}
          CHANGED_PLUGIN_VERSION: ${{ needs.check-changes.outputs.changed_plugin_version }}
        run: |
          if [ -n "$INPUT_TITLE" ]; then
            RELEASE_NAME="$INPUT_TITLE"
          elif [ -n "$CHANGED_PLUGIN_TITLE" ] && [ -n "$CHANGED_PLUGIN_VERSION" ]; then
            RELEASE_NAME="$CHANGED_PLUGIN_TITLE v$CHANGED_PLUGIN_VERSION"
          else
            RELEASE_NAME="$VERSION"
          fi

          echo "release_name=$RELEASE_NAME" >> "$GITHUB_OUTPUT"
          echo "Release name: $RELEASE_NAME"

      - name: Extract plugin versions
        id: plugins
        run: |
          python scripts/extract_plugin_versions.py --json --output plugin_versions.json

      - name: Collect plugin files for release
        id: collect_files
        run: |
          mkdir -p release_plugins
          
          CHANGED_PLUGINS="${{ needs.check-changes.outputs.changed_plugins }}"
          
          if [ -n "$CHANGED_PLUGINS" ]; then
            echo "Collecting changed plugin files..."
            echo "$CHANGED_PLUGINS" | while read -r file; do
              if [ -n "$file" ] && [ -f "$file" ]; then
                dir=$(dirname "$file")
                mkdir -p "release_plugins/$dir"
                cp "$file" "release_plugins/$file"
                echo "Added: $file"
              fi
            done
          else
            echo "No changed plugins detected. Skipping file collection."
          fi
          
          # Create a zip file with error handling
          # cd release_plugins
          # Zip step removed as per user request
          
          echo "=== Collected Files ==="
          find release_plugins -name "*.py" -type f | head -20

      - name: Update plugin icon URLs
        run: |
          echo "Updating icon_url in plugins to use absolute GitHub URLs..."
          # Base URL for raw content using the release tag
          REPO_URL="https://raw.githubusercontent.com/${{ github.repository }}/${{ steps.version.outputs.version }}"
          
          find release_plugins -name "*.py" | while read -r file; do
            # $file is like release_plugins/plugins/actions/infographic/infographic.py
            # Remove release_plugins/ prefix to get the path in the repo
            src_file="${file#release_plugins/}"
            src_dir=$(dirname "$src_file")
            base_name=$(basename "$src_file" .py)
            
            # Check if a corresponding png exists in the source repository
            png_file="${src_dir}/${base_name}.png"
            
            if [ -f "$png_file" ]; then
              echo "Found icon for $src_file: $png_file"
              TARGET_ICON_URL="${REPO_URL}/${png_file}"
              
              # Use python for safe replacement
              python3 -c "
          import sys
          import re
          
          file_path = '$file'
          icon_url = '$TARGET_ICON_URL'
          
          try:
              with open(file_path, 'r', encoding='utf-8') as f:
                  content = f.read()
              
              # Replace icon_url: ... with new url
              # Matches 'icon_url: ...' and replaces it
              new_content = re.sub(r'^icon_url:.*$', f'icon_url: {icon_url}', content, flags=re.MULTILINE)
              
              with open(file_path, 'w', encoding='utf-8') as f:
                  f.write(new_content)
              print(f'Successfully updated icon_url in {file_path}')
          except Exception as e:
              print(f'Error updating {file_path}: {e}', file=sys.stderr)
              sys.exit(1)
          "
            fi
          done

      - name: Debug Filenames
        run: |
          python3 -c "import sys; print(f'Filesystem encoding: {sys.getfilesystemencoding()}')"
          ls -R release_plugins

      - name: Upload Debug Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: debug-plugins
          path: release_plugins/

      - name: Get commit messages
        id: commits
        if: github.event_name == 'push'
        env:
          PREVIOUS_RELEASE_TAG: ${{ needs.check-changes.outputs.previous_release_tag }}
          COMPARE_REF: ${{ needs.check-changes.outputs.compare_ref }}
        run: |
          if [ -n "$PREVIOUS_RELEASE_TAG" ]; then
            COMMITS=$(git log ${PREVIOUS_RELEASE_TAG}..HEAD --pretty=format:"- **%s**%n%b" --no-merges -- plugins/ | sed '/^$/d' | head -40)
          elif [ -n "$COMPARE_REF" ]; then
            COMMITS=$(git log ${COMPARE_REF}..HEAD --pretty=format:"- **%s**%n%b" --no-merges -- plugins/ | sed '/^$/d' | head -40)
          else
            COMMITS=$(git log --pretty=format:"- **%s**%n%b" --no-merges -10 -- plugins/ | sed '/^$/d')
          fi
          
          {
            echo 'commits<<EOF'
            echo "$COMMITS"
            echo ""
            echo 'EOF'
          } >> "$GITHUB_OUTPUT"

      - name: Generate release notes
        id: notes
        env:
          VERSION: ${{ steps.version.outputs.version }}
          TITLE: ${{ github.event.inputs.release_title }}
          NOTES: ${{ github.event.inputs.release_notes }}
          CHANGED_PLUGIN_TITLE: ${{ needs.check-changes.outputs.changed_plugin_title }}
          CHANGED_PLUGIN_VERSION: ${{ needs.check-changes.outputs.changed_plugin_version }}
          DETECTED_CHANGES: ${{ needs.check-changes.outputs.release_notes }}
          DOC_FILES: ${{ needs.check-changes.outputs.changed_doc_files }}
        run: |
          > release_notes.md

          # 1. Primary content from v*.md files (highest priority)
          if [ -n "$DOC_FILES" ]; then
            RELEASE_NOTE_FILES=$(echo "$DOC_FILES" | grep -E '^plugins/.*/v[^/]*\.md$' | grep -v '_CN\.md$' || true)
            if [ -n "$RELEASE_NOTE_FILES" ]; then
              while IFS= read -r file; do
                [ -z "$file" ] && continue
                if [ -f "$file" ]; then
                  # Extract content, removing any H1 title from the file to avoid duplication
                  python3 -c "import pathlib, re; file_path = pathlib.Path(r'''$file'''); text = file_path.read_text(encoding='utf-8'); text = re.sub(r'^#\s+.+?(?:\r?\n)+', '', text, count=1, flags=re.MULTILINE); print(text.lstrip().rstrip())" >> release_notes.md
                  echo "" >> release_notes.md
                fi
              done <<< "$RELEASE_NOTE_FILES"
            fi
          fi

          # 2. Automated plugin version change summary
          if [ -n "$DETECTED_CHANGES" ] && ! echo "$DETECTED_CHANGES" | grep -q "No changes detected"; then
            echo "## Version Changes" >> release_notes.md
            echo "" >> release_notes.md
            echo "$DETECTED_CHANGES" >> release_notes.md
            echo "" >> release_notes.md
          fi
          
          # 3. Manual additional notes from workflow dispatch
          if [ -n "$NOTES" ]; then
            echo "## Additional Notes" >> release_notes.md
            echo "" >> release_notes.md
            echo "$NOTES" >> release_notes.md
            echo "" >> release_notes.md
          fi
          
          cat >> release_notes.md << 'EOF'
          
          ---
          
          📚 [Documentation Portal](https://fu-jie.github.io/openwebui-extensions/)
          🐛 [Report Issues](https://github.com/Fu-Jie/openwebui-extensions/issues)
          EOF

          echo "=== Final Release Notes ==="
          cat release_notes.md

          echo "=== Release Notes ==="
          cat release_notes.md

      - name: Create Git Tag
        if: ${{ !startsWith(github.ref, 'refs/tags/') }}
        run: |
          VERSION="${{ steps.version.outputs.version }}"
          
          if [ -z "$VERSION" ]; then
            echo "Error: Version is empty!"
            exit 1
          fi
          
          if ! git rev-parse "$VERSION" >/dev/null 2>&1; then
            echo "Creating tag $VERSION"
            git tag "$VERSION"
            git push origin "$VERSION"
          else
            echo "Tag $VERSION already exists"
          fi
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ steps.version.outputs.version }}
          target_commitish: ${{ github.sha }}
          name: ${{ steps.meta.outputs.release_name }}
          body_path: release_notes.md
          prerelease: ${{ github.event.inputs.prerelease || false }}
          make_latest: true
          files: |
            plugin_versions.json
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Upload Release Assets
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          # Check if there are any .py files to upload
          if [ -d release_plugins ] && [ -n "$(find release_plugins -type f -name '*.py' 2>/dev/null)" ]; then
            echo "Uploading plugin files..."
            find release_plugins -type f -name "*.py" -print0 | xargs -0 gh release upload ${{ steps.version.outputs.version }} --clobber
          else
            echo "No plugin files to upload. Skipping asset upload."
          fi

      - name: Summary
        run: |
          echo "## 🚀 Release Created Successfully!" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "**Version:** ${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "### Updated Plugins" >> $GITHUB_STEP_SUMMARY
          echo "${{ needs.check-changes.outputs.release_notes }}" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY

