name: Publish Workflow

on:
  push:
    paths-ignore:
      - '**.md'
      - 'docs/**'
      - '.gitignore'
      - 'LICENSE'
  pull_request:
    paths-ignore:
      - '**.md'
      - 'docs/**'
      - '.gitignore'
      - 'LICENSE'
  workflow_dispatch:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

permissions:
  contents: write
  pull-requests: read
  security-events: write
  checks: write
  actions: read

env:
  DOTNET_VERSION: '10.x'
  DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
  DOTNET_CLI_TELEMETRY_OPTOUT: true

jobs:
  # ==============================================================================
  # 1. Build & Test (Generates Coverage)
  # ==============================================================================
  Build_Test_Coverage:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup .NET SDK
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: ${{ env.DOTNET_VERSION }}

      - name: Cache NuGet packages
        uses: actions/cache@v4
        with:
          path: ~/.nuget/packages
          key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
          restore-keys: ${{ runner.os }}-nuget-

      - name: Restore dependencies
        run: dotnet restore

      - name: Build
        run: dotnet build --no-restore --configuration Debug

      - name: Run Tests
        run: |
          dotnet test --no-build --configuration Debug \
            --collect:"XPlat Code Coverage" \
            -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover

      - name: Collect Coverage Files
        run: |
          mkdir -p coverage
          count=1
          while read -r file; do
            dest="coverage/coverage_${count}.opencover.xml"
            cp "$file" "$dest"
            # Sanitize paths for Qodana (convert absolute to relative)
            # This strips the current working directory from the paths in the XML
            sed -i "s|$(pwd)/||g" "$dest"
            count=$((count+1))
          done < <(find . -name "coverage.opencover.xml" -type f)

      - name: Upload Coverage Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: coverage-reports
          path: coverage/

  # ==============================================================================
  # 2. SonarCloud Analysis
  # ==============================================================================
  Sonar_Analysis:
    needs: Build_Test_Coverage
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup .NET SDK
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: ${{ env.DOTNET_VERSION }}

      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: 17
          distribution: 'zulu'

      - name: Cache NuGet packages
        uses: actions/cache@v4
        with:
          path: ~/.nuget/packages
          key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
          restore-keys: ${{ runner.os }}-nuget-

      - name: Download Coverage Reports
        uses: actions/download-artifact@v4
        with:
          name: coverage-reports
          path: coverage

      - name: Install SonarScanner
        run: |
          dotnet tool install --global dotnet-sonarscanner --version 9.0.0
          echo "$HOME/.dotnet/tools" >> $GITHUB_PATH

      - name: Run SonarCloud Analysis
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        shell: bash
        run: |
          set -e # Fail immediately if any command exits with a non-zero status
          
          if [ -z "$SONAR_TOKEN" ]; then
            echo "WARNING: SONAR_TOKEN is not set. Skipping analysis."
            exit 0
          fi
          echo "SONAR_TOKEN is set (Length: ${#SONAR_TOKEN})"
          
          dotnet restore
          
          # Added verbose logging to debug 9.0.0 auth issues
          # Reverted to sonar.token since the token has been updated
          # Removed quotes from /k and /o to ensure they are parsed correctly
          dotnet sonarscanner begin /k:mihakralj_QuanTAlib /o:mihakralj-quantalib \
            /d:sonar.token="$SONAR_TOKEN" \
            /d:sonar.host.url="https://sonarcloud.io" \
            /d:sonar.cs.opencover.reportsPaths="coverage/*.xml" \
            /d:sonar.coverage.exclusions="**Tests.cs,**/*.md,**/*.html,**/*.css,**/docs/**/*,**/archive/**/*,**/notebooks/**/*,**/obj/**/*,**/bin/**/*" \
            /d:sonar.exclusions="**/TestResults/**/*,**/bin/**/*,**/obj/**/*,**/*.html,**/coverage/**/*,**/CoverageReport/**/*,**/*.md,**/*.css,**/docs/**/*,**/archive/**/*,**/notebooks/**/*" \
            /d:sonar.test.exclusions="**Tests.cs,**/obj/**/*,**/bin/**/*" \
            /d:sonar.scanner.scanAll="false"

          dotnet build --no-restore --configuration Debug
          
          dotnet sonarscanner end /d:sonar.token="$SONAR_TOKEN"

  # ==============================================================================
  # 3. Qodana Scan
  # ==============================================================================
  Qodana_Scan:
    needs: Build_Test_Coverage
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      
      - name: Download Coverage Reports
        uses: actions/download-artifact@v4
        with:
          name: coverage-reports
          path: .qodana/code-coverage

      - name: Qodana Scan
        uses: JetBrains/qodana-action@v2024.3.4
        env:
          QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
        with:
          args: --coverage-dir,.qodana/code-coverage

  # ==============================================================================
  # 5. Codacy Upload
  # ==============================================================================
  Codacy_Upload:
    needs: Build_Test_Coverage
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      
      - name: Download Coverage Reports
        uses: actions/download-artifact@v4
        with:
          name: coverage-reports
          path: coverage

      - name: Upload to Codacy
        env:
          CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
        run: |
          if [ -z "$CODACY_PROJECT_TOKEN" ]; then
            echo "CODACY_PROJECT_TOKEN is not set. Skipping upload."
            exit 0
          fi

          # Download the reporter script
          curl -Ls https://coverage.codacy.com/get.sh -o codacy.sh
          chmod +x codacy.sh
          
          # Upload each file
          for file in coverage/*.xml; do
            echo "Uploading $file..."
            ./codacy.sh report -r "$file" --partial
          done
          
          # Finalize
          ./codacy.sh final
        continue-on-error: true

  # ==============================================================================
  # 6. Snyk Scan
  # ==============================================================================
  Snyk_Scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      
      - name: Setup .NET SDK
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: ${{ env.DOTNET_VERSION }}

      - name: Install Snyk CLI
        uses: snyk/actions/setup@806182742461562b67788a64410098c9d9b96adb # master

      - name: Restore dependencies
        run: dotnet restore

      - name: Run Snyk on Solution
        continue-on-error: true
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        run: snyk test --all-projects

  # ==============================================================================
  # 7. CodeQL Analysis
  # ==============================================================================
  CodeQL_Analysis:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
      actions: read
      contents: read
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup .NET SDK
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: ${{ env.DOTNET_VERSION }}

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v4
        with:
          languages: 'csharp'
          queries: security-and-quality
          config-file: ./.github/codeql/codeql-config.yml

      - name: Build
        run: dotnet build --configuration Debug

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v4
        with:
          output: results
          upload: true

  # ==============================================================================
  # 8. Publish Package
  # ==============================================================================
  Publish_Package:
    needs: [Build_Test_Coverage, Sonar_Analysis, Qodana_Scan, Codacy_Upload, Snyk_Scan, CodeQL_Analysis]
    if: |
      success() &&
      (
        (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || endsWith(github.ref, '-dev'))) ||
        github.event_name == 'workflow_dispatch'
      )
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup .NET SDK
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: ${{ env.DOTNET_VERSION }}

      - name: Install GitVersion
        uses: gittools/actions/gitversion/setup@v4.2.0
        with:
          versionSpec: '6.x'
          includePrerelease: true

      - name: Determine Version
        id: gitversion
        uses: gittools/actions/gitversion/execute@v4.2.0
        with:
          updateAssemblyInfo: false

      - name: Build & Pack
        run: |
          dotnet restore
          dotnet build ./lib/quantalib.csproj --configuration Release --nologo
          dotnet build ./quantower/Momentum.csproj --configuration Release --nologo
          dotnet build ./quantower/Trends.csproj --configuration Release --nologo
          dotnet build ./quantower/Volume.csproj --configuration Release --nologo

      - name: Create Development Release
        if: github.ref == 'refs/heads/dev' || endsWith(github.ref, '-dev')
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release delete development --yes || true
          gh release create development \
            --title "Development Build" \
            --notes "Latest development build from commit ${{ github.sha }}" \
            --prerelease \
            --target ${{ github.sha }} \
            lib/bin/Release/net10.0/QuanTAlib.dll \
            quantower/bin/Release/Momentum.dll \
            quantower/bin/Release/Trends.dll \
            quantower/bin/Release/Volume.dll

      - name: Push to MyGet
        if: github.ref == 'refs/heads/dev' || endsWith(github.ref, '-dev')
        continue-on-error: true
        run: |
          dotnet nuget push 'lib/bin/Release/QuanTAlib.*.nupkg' \
            --source https://www.myget.org/F/quantalib/api/v3/index.json \
            --force-english-output \
            --api-key ${{ secrets.MYGET_DEPLOY_KEY_QUANTALIB }}

      - name: Create GitHub Release
        if: github.ref == 'refs/heads/main'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release create v${{ steps.gitversion.outputs.MajorMinorPatch }} \
            --title "Release from commit ${{ steps.gitversion.outputs.MajorMinorPatch }}" \
            --notes "Release notes for this version." \
            quantower/bin/Release/Momentum.dll \
            quantower/bin/Release/Trends.dll \
            quantower/bin/Release/Volume.dll

      - name: Push to NuGet
        if: ${{ github.ref == 'refs/heads/main' }}
        run: |
          dotnet nuget push 'lib/bin/Release/QuanTAlib.*.nupkg' \
            --source https://api.nuget.org/v3/index.json \
            --skip-duplicate \
            --api-key ${{ secrets.NUGET_DEPLOY_KEY_QUANTLIB }}
