name: Docs link check

on:
  push:
    branches:
    - main
  pull_request:

permissions:
  contents: read

jobs:
  lychee:
    runs-on: ubuntu-latest
    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      # Cache lychee results (e.g. to avoid hitting rate limits)
      # https://lychee.cli.rs/github_action_recipes/caching/
      - name: Restore lychee cache
        uses: actions/cache@v4
        with:
          path: .lycheecache
          key: cache-lychee-${{ github.sha }}
          restore-keys: cache-lychee-

      # https://github.com/lycheeverse/lychee/issues/1487
      - name: Install CA Certificates for lychee
        run: |
          sudo apt-get install ca-certificates

      - name: Install lychee
        run: |
          set -euo pipefail
          mkdir -p "$HOME/.local/bin"
          cd "$RUNNER_TEMP"
          # TODO: Lychee v0.19.1 doesn't support regex in --exclude-path, so use nightly
          # release until there is a released version containing regex support.
          curl -sSL -o lychee.tar.gz \
            https://github.com/lycheeverse/lychee/releases/download/nightly/lychee-x86_64-unknown-linux-gnu.tar.gz
          tar -xzf lychee.tar.gz
          BIN_PATH=$(find . -maxdepth 2 -type f -name lychee | head -n1)
          install -m 0755 "$BIN_PATH" "$HOME/.local/bin/lychee"
          echo "$HOME/.local/bin" >> "$GITHUB_PATH"
          lychee --version

      - name: Check documentation links with lychee
        env:
          # Set GITHUB_TOKEN to avoid github rate limits on URL checks
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          set -euo pipefail

          # Set offline mode for pull requests, full check for pushes to main
          if [[ "${{ github.event_name }}" == "pull_request" ]]; then
            echo "Running lychee in offline mode (internal links only) for PR check"
            OFFLINE_FLAG="--offline"
          else
            echo "Running lychee in full mode (all links) for main branch"
            OFFLINE_FLAG=""
          fi

          # Run lychee against all files in repo
          lychee \
            --cache \
            --no-progress \
            --max-retries 2 \
            --retry-wait-time 2 \
            --timeout 20 \
            --root-dir "${{ github.workspace }}" \
            --exclude-path ".*ATTRIBUTIONS.*" \
            --exclude-path "./lib/llm/tests/data/.*" \
            --accept "200..=299, 403, 429" \
            --exclude-all-private --exclude 0.0.0.0 \
            $OFFLINE_FLAG \
            .

  broken-links-check:
    name: Check for broken markdown links
    runs-on: ubuntu-latest

    steps:
      - name: Check out repository
        uses: actions/checkout@v4

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

      - name: Install Python dependencies
        run: |
          python -m pip install --upgrade pip
          # No additional dependencies needed for the basic script

      - name: Run broken links check
        id: check-links
        run: |
          echo "Running broken links check on documentation files..."

          # Run the broken links detection script and capture exit code
          set +e  # Don't exit immediately on error
          python3 .github/workflows/detect_broken_links.py \
            --verbose \
            --format json \
            --check-symlinks \
            --output broken-links-report.json \
            .
          exit_code=$?
          set -e  # Re-enable exit on error

          # Check if the script found any broken links (exit code 1 means broken links found)
          if [ $exit_code -eq 1 ]; then
            echo "::error::Broken links found in documentation files"
            echo "broken_links_found=true" >> $GITHUB_OUTPUT
          elif [ $exit_code -eq 0 ]; then
            echo "::notice::No broken links found"
            echo "broken_links_found=false" >> $GITHUB_OUTPUT
          else
            echo "::error::Script failed with exit code $exit_code"
            echo "broken_links_found=error" >> $GITHUB_OUTPUT
            exit $exit_code
          fi


      - name: Display broken links and create annotations
        if: steps.check-links.outputs.broken_links_found == 'true'
        run: |
          echo "::group::🔗 Broken Links Found"

          # Parse and display the JSON report in a readable format, plus create GitHub annotations
          python3 -c "
          import json
          import sys
          import os

          try:
              with open('broken-links-report.json', 'r') as f:
                  report = json.load(f)

              summary = report['summary']
              broken_links = report['broken_links']
              problematic_symlinks = report.get('problematic_symlinks', {})

              has_broken_links = bool(broken_links)
              has_problematic_symlinks = bool(problematic_symlinks)

              if has_broken_links:
                  print('❌ BROKEN LINKS DETECTED')
              print('=' * 50)
              print(f'📊 Summary:')
              print(f'  • Total files processed: {summary[\"total_files_processed\"]}')
              print(f'  • Files with broken links: {summary[\"files_with_broken_links\"]}')
              print(f'  • Total broken links: {summary[\"total_broken_links\"]}')
              print()

              if broken_links:
                  print('🔍 Detailed Broken Links Report:')
                  print('-' * 40)

                  for file_path, links in broken_links.items():
                      print(f'\\n📄 File: {file_path}')
                      print(f'   {len(links)} broken link(s) found')
                      print()

                      for i, link in enumerate(links, 1):
                          line = link['line']
                          link_text = link['link_text']
                          link_url = link['link_url']
                          error_reason = link.get('error_reason', 'Link target not found')
                          github_url = link.get('github_url', '')

                          # Create GitHub annotation for each broken link
                          annotation_msg = f'Broken link: [{link_text}]({link_url}) - {error_reason}'
                          if github_url:
                              annotation_msg += f' - View: {github_url}'
                          print(f'::error file={file_path},line={line}::{annotation_msg}')

                          # Display in workflow output
                          print(f'   {i}. Line {line}: [{link_text}]({link_url})')
                          print(f'      ❌ {error_reason}')
                          if github_url:
                              print(f'      🔗 View on GitHub: {github_url}')
                          else:
                              print(f'      📍 Target: {link_url}')
                          print()

              if has_problematic_symlinks:
                  if has_broken_links:
                      print('\\n' + '=' * 50)
                  print('🔗 PROBLEMATIC SYMBOLIC LINKS DETECTED')
                  print('=' * 50)
                  print(f'📊 Summary:')
                  print(f'  • Total problematic symlinks: {summary.get(\"total_problematic_symlinks\", 0)}')
                  print()

                  for category, symlinks in problematic_symlinks.items():
                      if symlinks:
                          category_icons = {
                              'broken': '💔',
                              'circular': '🔄',
                              'external': '🌐',
                              'suspicious': '⚠️'
                          }
                          icon = category_icons.get(category, '❓')
                          print(f'{icon} {category.upper()} SYMLINKS ({len(symlinks)}):')
                          print('-' * 40)

                          for i, symlink in enumerate(symlinks, 1):
                              symlink_path = symlink['symlink_path']
                              target_path = symlink['target_path']
                              issue = symlink['issue']

                              # Create GitHub annotation for each problematic symlink
                              annotation_msg = f'Problematic symlink: {issue}'
                              print(f'::error file={symlink_path}::{annotation_msg}')

                              # Display in workflow output
                              print(f'   {i}. {symlink_path}')
                              print(f'      → {target_path}')
                              print(f'      ❌ {issue}')
                              print()

              print('=' * 50)
              print('✅ Next Steps:')
              if has_broken_links:
                  print('1. Check the annotations above in the Files Changed tab (for PRs)')
                  print('2. Click the GitHub links to jump directly to each broken link')
                  print('3. Fix all broken links before merging')
              if has_problematic_symlinks:
                  step_num = 4 if has_broken_links else 1
                  print(f'{step_num}. Review and fix problematic symbolic links')
                  print(f'{step_num + 1}. Consider replacing broken symlinks with actual files or fixing targets')
                  print(f'{step_num + 2}. Evaluate if suspicious symlinks with many traversals are necessary')
              final_step = (7 if has_broken_links and has_problematic_symlinks
                           else 4 if has_broken_links
                           else 4 if has_problematic_symlinks
                           else 1)
              print(f'{final_step}. Re-run this workflow to verify fixes')

          except Exception as e:
              print(f'❌ Error reading broken links report: {e}')
              sys.exit(1)
          "

          echo "::endgroup::"

      - name: Alert for broken links
        if: steps.check-links.outputs.broken_links_found == 'true' || steps.check-links.outputs.broken_links_found == 'error'
        run: |
          if [ "${{ steps.check-links.outputs.broken_links_found }}" = "error" ]; then
            echo "::error::Workflow failed due to script error"
            echo "Please check the workflow logs for details."
            exit 1
          else
            echo "::error::❌ WORKFLOW FAILED: Broken links found in documentation files"
            echo ""
            echo "🔍 What to do next:"
            echo "1. Check the 'Display broken links and create annotations' step above for details"
            echo "2. Look for ::error annotations in the Files Changed tab (for PRs)"
            echo "3. Click the GitHub URLs in the workflow output to jump directly to each broken link"
            echo "4. Fix all broken links before merging"
            echo ""
            echo "💡 Each broken link shows:"
            echo "   - File name and line number where the broken link is located"
            echo "   - The broken link text and target URL"
            echo "   - A clickable GitHub URL to jump directly to the problem line"
            exit 1
          fi
