name: CI/CD Pipeline

on:
  push:
    branches: [ "master", "develop" ]
    paths:
      - 'src/**'
      - 'test/**'
      - 'cmake/**'
      - 'CMakeLists.txt'
      - '.github/workflows/**'
  pull_request:
    branches: [ "master", "develop" ]
    paths:
      - 'src/**'
      - 'test/**'
      - 'cmake/**'
      - 'CMakeLists.txt'
      - '.github/workflows/**'
  workflow_dispatch:
    inputs:
      build_type:
        description: 'Build Type (Debug, Release, RelWithDebInfo)'
        required: false
        default: 'RelWithDebInfo'
        type: choice
        options:
          - Debug
          - Release
          - RelWithDebInfo
      run_sanitizers:
        description: 'Run memory sanitizers'
        required: false
        default: false
        type: boolean
      upload_artifacts:
        description: 'Upload build artifacts'
        required: false
        default: false
        type: boolean

env:
  BUILD_TYPE: ${{ github.event.inputs.build_type || 'RelWithDebInfo' }}

jobs:
  build-and-test:
    name: "${{ matrix.name }}"
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - name: "Ubuntu 22.04 (GCC-12)"
            os: ubuntu-22.04
            cc: gcc-12
            cxx: g++-12
          - name: "macOS 14 (Clang)"
            os: macos-14
            cc: clang
            cxx: clang++
          - name: "Windows 2022 (MSVC)"
            os: windows-2022
            cc: cl
            cxx: cl

    steps:
    - name: Checkout
      uses: actions/checkout@v4
      with:
        submodules: recursive
      
    - name: Setup vcpkg Cache (Windows)
      if: runner.os == 'Windows'
      uses: actions/cache@v4
      with:
        path: |
          ${{ github.workspace }}/vcpkg
          !${{ github.workspace }}/vcpkg/buildtrees
          !${{ github.workspace }}/vcpkg/packages
          !${{ github.workspace }}/vcpkg/downloads
        key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json') }}
        restore-keys: |
          vcpkg-${{ runner.os }}-
    
    - name: Set up vcpkg (Windows)
      if: runner.os == 'Windows'
      uses: lukka/run-vcpkg@v11
      id: vcpkg
      continue-on-error: true
      env:
        VCPKG_DEFAULT_TRIPLET: x64-windows-static
        VCPKG_BINARY_SOURCES: 'clear;x-gha,readwrite'
      with:
        vcpkgDirectory: '${{ github.workspace }}/vcpkg'
        vcpkgGitCommitId: '01f602195983451bc83e72f4214af2cbc495aa94'  # 2024.10.21 release
        runVcpkgInstall: false  # Let CMake handle installation via manifest mode
    
    - name: Retry vcpkg setup (Windows)
      if: runner.os == 'Windows' && steps.vcpkg.outcome == 'failure'
      uses: lukka/run-vcpkg@v11
      env:
        VCPKG_DEFAULT_TRIPLET: x64-windows-static
        VCPKG_BINARY_SOURCES: 'clear;x-gha,readwrite'
      with:
        vcpkgDirectory: '${{ github.workspace }}/vcpkg'
        vcpkgGitCommitId: '01f602195983451bc83e72f4214af2cbc495aa94'
        runVcpkgInstall: false
        doNotUpdateVcpkg: true  # Use existing clone on retry
    
    - name: Diagnose vcpkg (Windows)
      if: runner.os == 'Windows' && (steps.vcpkg.outcome == 'failure' || steps.vcpkg.outcome == 'success')
      shell: pwsh
      run: |
        Write-Host "=== vcpkg Diagnostics ===" -ForegroundColor Cyan
        Write-Host "vcpkg directory: ${{ github.workspace }}/vcpkg"
        
        if (Test-Path "${{ github.workspace }}/vcpkg/vcpkg.exe") {
          Write-Host "✅ vcpkg.exe found" -ForegroundColor Green
          & "${{ github.workspace }}/vcpkg/vcpkg.exe" version
        } else {
          Write-Host "❌ vcpkg.exe not found" -ForegroundColor Red
        }
        
        Write-Host "`nEnvironment:" -ForegroundColor Cyan
        Write-Host "VCPKG_DEFAULT_TRIPLET: $env:VCPKG_DEFAULT_TRIPLET"
        Write-Host "Workspace: ${{ github.workspace }}"

    - name: Install Dependencies
      id: deps
      shell: bash
      continue-on-error: true
      run: |
        if [[ "${{ runner.os }}" == "Linux" ]]; then
          sudo apt-get update
          sudo apt-get install -y \
            build-essential ninja-build pkg-config \
            libglew-dev libxext-dev libwavpack-dev libboost-all-dev \
            libpng-dev python3-dev libpython3-dev \
            libasound2-dev libpulse-dev libaudio-dev \
            libx11-dev libxrandr-dev libxcursor-dev libxinerama-dev libxi-dev \
            libxss-dev libxxf86vm-dev libxkbcommon-dev libwayland-dev libdecor-0-dev \
            libgtk-3-dev libdbus-1-dev \
            ${{ matrix.cc }} ${{ matrix.cxx }}
          # Note: libabsl-dev removed - gRPC uses bundled Abseil via FetchContent when enabled
        elif [[ "${{ runner.os }}" == "macOS" ]]; then
          brew install ninja pkg-config
        fi
    
    - name: Retry Dependencies (if failed)
      if: steps.deps.outcome == 'failure'
      shell: bash
      run: |
        echo "::warning::First dependency install failed, retrying..."
        if [[ "${{ runner.os }}" == "Linux" ]]; then
          sudo apt-get clean
          sudo apt-get update --fix-missing
          sudo apt-get install -y \
            build-essential ninja-build pkg-config \
            libglew-dev libxext-dev libwavpack-dev libboost-all-dev \
            libpng-dev python3-dev libpython3-dev \
            libasound2-dev libpulse-dev libaudio-dev \
            libx11-dev libxrandr-dev libxcursor-dev libxinerama-dev libxi-dev \
            libxss-dev libxxf86vm-dev libxkbcommon-dev libwayland-dev libdecor-0-dev \
            libgtk-3-dev libdbus-1-dev \
            ${{ matrix.cc }} ${{ matrix.cxx }}
        elif [[ "${{ runner.os }}" == "macOS" ]]; then
          brew update
          brew install ninja pkg-config
        fi

    - name: Pre-configure Diagnostics (Windows)
      if: runner.os == 'Windows'
      shell: pwsh
      run: |
        Write-Host "=== Pre-configure Diagnostics ===" -ForegroundColor Cyan
        Write-Host "Build Type: ${{ env.BUILD_TYPE }}"
        Write-Host "Workspace: ${{ github.workspace }}"
        
        # Check Visual Studio installation
        $vsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
        if (Test-Path $vsWhere) {
          Write-Host "`nVisual Studio Installation:" -ForegroundColor Cyan
          & $vsWhere -latest -property displayName
          & $vsWhere -latest -property installationVersion
        }
        
        # Check CMake
        Write-Host "`nCMake Version:" -ForegroundColor Cyan
        cmake --version
        
        # Verify vcpkg toolchain
        $toolchain = "${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake"
        if (Test-Path $toolchain) {
          Write-Host "✅ vcpkg toolchain found at: $toolchain" -ForegroundColor Green
        } else {
          Write-Host "⚠️ vcpkg toolchain not found at: $toolchain" -ForegroundColor Yellow
        }
        
        # Show vcpkg manifest
        if (Test-Path "vcpkg.json") {
          Write-Host "`nvcpkg.json contents:" -ForegroundColor Cyan
          Get-Content "vcpkg.json" | Write-Host
        }
        
        # Show available disk space
        Write-Host "`nDisk Space:" -ForegroundColor Cyan
        Get-PSDrive C | Select-Object Used,Free | Format-Table -AutoSize
    
    - name: Configure (Windows)
      if: runner.os == 'Windows'
      id: configure_windows
      shell: pwsh
      env:
        VCPKG_DEFAULT_TRIPLET: x64-windows-static
      run: |
        Write-Host "::group::CMake Configuration (Windows)" -ForegroundColor Cyan
        
        $vcpkgToolchain = "${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake" -replace '\\', '/'
        Write-Host "Using vcpkg toolchain: $vcpkgToolchain"
        
        cmake -B build -G "Visual Studio 17 2022" -A x64 `
          -DCMAKE_BUILD_TYPE=$env:BUILD_TYPE `
          -DCMAKE_TOOLCHAIN_FILE="$vcpkgToolchain" `
          -DVCPKG_TARGET_TRIPLET=x64-windows-static `
          -DVCPKG_MANIFEST_MODE=ON `
          -DVCPKG_INSTALL_OPTIONS="--debug" `
          -DCMAKE_CXX_FLAGS="/Dgoogle_protobuf_undef_DWORD=1" `
          -DYAZE_BUILD_TESTS=ON `
          -DYAZE_ENABLE_ROM_TESTS=OFF 2>&1 | Tee-Object -FilePath cmake_config.log
        
        if ($LASTEXITCODE -ne 0) {
          Write-Host "::error::CMake configuration failed with exit code $LASTEXITCODE"
          exit $LASTEXITCODE
        }
        
        Write-Host "::endgroup::"
    
    - name: Configure (Unix)
      if: runner.os != 'Windows'
      id: configure_unix
      shell: bash
      run: |
        set -e
        echo "::group::CMake Configuration"
        cmake -B build -G Ninja \
          -DCMAKE_BUILD_TYPE=$BUILD_TYPE \
          -DCMAKE_C_COMPILER=${{ matrix.cc }} \
          -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} \
          -DYAZE_BUILD_TESTS=ON \
          -DYAZE_ENABLE_ROM_TESTS=OFF 2>&1 | tee cmake_config.log
        echo "::endgroup::"
    
    - name: Report Configure Failure
      if: failure() && (steps.configure_windows.outcome == 'failure' || steps.configure_unix.outcome == 'failure')
      shell: bash
      run: |
        echo "::error::CMake configuration failed. Check cmake_config.log for details."
        if [ -f cmake_config.log ]; then
          echo "::group::CMake Configuration Log (last 50 lines)"
          tail -50 cmake_config.log
          echo "::endgroup::"
        fi
        if [ -f build/CMakeFiles/CMakeError.log ]; then
          echo "::group::CMake Error Log"
          cat build/CMakeFiles/CMakeError.log
          echo "::endgroup::"
        fi
    
    - name: Build
      id: build
      run: cmake --build build --config ${{ env.BUILD_TYPE }} --parallel 2>&1 | tee build.log
    
    - name: Report Build Failure
      if: failure() && steps.build.outcome == 'failure'
      shell: bash
      run: |
        echo "::error::Build failed. Check build.log for details."
        if [ -f build.log ]; then
          echo "::group::Build Log (last 100 lines)"
          tail -100 build.log
          echo "::endgroup::"
          
          # Extract and highlight actual errors
          echo "::group::Build Errors"
          grep -i "error" build.log | head -20 || true
          echo "::endgroup::"
        fi
    
    - name: Windows Build Diagnostics
      if: failure() && runner.os == 'Windows' && steps.build.outcome == 'failure'
      shell: pwsh
      run: |
        Write-Host "=== Windows Build Diagnostics ===" -ForegroundColor Red
        
        # Check for vcpkg-related errors
        if (Select-String -Path "build.log" -Pattern "vcpkg" -Quiet) {
          Write-Host "`nvcpkg-related errors found:" -ForegroundColor Yellow
          Select-String -Path "build.log" -Pattern "vcpkg.*error" -CaseSensitive:$false | Select-Object -First 10
        }
        
        # Check for linker errors
        if (Select-String -Path "build.log" -Pattern "LNK[0-9]{4}" -Quiet) {
          Write-Host "`nLinker errors found:" -ForegroundColor Yellow
          Select-String -Path "build.log" -Pattern "LNK[0-9]{4}" | Select-Object -First 10
        }
        
        # Check for missing dependencies
        if (Select-String -Path "build.log" -Pattern "fatal error.*No such file" -Quiet) {
          Write-Host "`nMissing file errors found:" -ForegroundColor Yellow
          Select-String -Path "build.log" -Pattern "fatal error.*No such file" | Select-Object -First 10
        }
        
        # List vcpkg installed packages if available
        $vcpkgExe = "${{ github.workspace }}/vcpkg/vcpkg.exe"
        if (Test-Path $vcpkgExe) {
          Write-Host "`nInstalled vcpkg packages:" -ForegroundColor Cyan
          & $vcpkgExe list
        }
    
    - name: Post-Build Diagnostics (Windows)
      if: runner.os == 'Windows' && steps.build.outcome == 'success'
      shell: pwsh
      run: |
        Write-Host "=== Post-Build Diagnostics ===" -ForegroundColor Green
        
        $binPath = "build/bin/${{ env.BUILD_TYPE }}"
        if (Test-Path $binPath) {
          Write-Host "`nBuilt executables and libraries:" -ForegroundColor Cyan
          Get-ChildItem -Path $binPath -Include *.exe,*.dll -Recurse | ForEach-Object {
            $size = [math]::Round($_.Length / 1MB, 2)
            Write-Host "  $($_.Name) - ${size} MB"
          }
          
          # Check for specific yaze executable
          if (Test-Path "$binPath/yaze.exe") {
            Write-Host "`n✅ yaze.exe successfully built" -ForegroundColor Green
            $yazeSize = [math]::Round((Get-Item "$binPath/yaze.exe").Length / 1MB, 2)
            Write-Host "   Size: ${yazeSize} MB"
          } else {
            Write-Host "`n⚠️ yaze.exe not found" -ForegroundColor Yellow
          }
        } else {
          Write-Host "⚠️ Build output directory not found: $binPath" -ForegroundColor Yellow
        }
    
    - name: Upload Build Artifacts (Windows)
      if: |
        runner.os == 'Windows' && 
        steps.build.outcome == 'success' && 
        (github.event.inputs.upload_artifacts == 'true' || github.event_name == 'push')
      uses: actions/upload-artifact@v4
      with:
        name: yaze-windows-ci-${{ github.run_number }}
        path: |
          build/bin/${{ env.BUILD_TYPE }}/*.exe
          build/bin/${{ env.BUILD_TYPE }}/*.dll
        if-no-files-found: warn
        retention-days: 3

    - name: Test (Core)
      id: test_core
      working-directory: build
      run: |
        ctest --build-config ${{ env.BUILD_TYPE }} --output-on-failure -j1 \
          -R "AsarWrapperTest|SnesTileTest|CompressionTest|SnesPaletteTest|HexTest|MessageTest" \
          --output-junit core_test_results.xml 2>&1 | tee ../core_test.log
    
    - name: Test (Additional - Informational)
      id: test_additional
      working-directory: build
      continue-on-error: true
      run: |
        ctest --build-config ${{ env.BUILD_TYPE }} --output-on-failure --parallel \
          -R ".*Test" -E ".*RomTest.*|.*E2E.*|.*ZSCustomOverworld.*|.*IntegrationTest.*|CpuTest|Spc700Test|ApuTest" \
          --output-junit additional_test_results.xml 2>&1 | tee ../additional_test.log
    
    - name: Upload Test Results
      if: always()
      uses: actions/upload-artifact@v4
      with:
        name: test-results-${{ matrix.name }}
        path: |
          build/*test_results.xml
          core_test.log
          additional_test.log
        retention-days: 7
    
    - name: Upload Build Logs on Failure
      if: failure()
      uses: actions/upload-artifact@v4
      with:
        name: build-logs-${{ matrix.name }}
        path: |
          cmake_config.log
          build.log
          build/CMakeFiles/CMakeError.log
          build/CMakeFiles/CMakeOutput.log
        if-no-files-found: ignore
        retention-days: 7
    
    - name: Generate Job Summary
      if: always()
      shell: bash
      run: |
        echo "## Build Summary - ${{ matrix.name }}" >> $GITHUB_STEP_SUMMARY
        echo "" >> $GITHUB_STEP_SUMMARY
        
        # Workflow trigger info
        echo "### Workflow Information" >> $GITHUB_STEP_SUMMARY
        echo "- **Trigger**: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
        if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
          echo "- **Manual Build Type**: ${{ github.event.inputs.build_type }}" >> $GITHUB_STEP_SUMMARY
          echo "- **Upload Artifacts**: ${{ github.event.inputs.upload_artifacts }}" >> $GITHUB_STEP_SUMMARY
          echo "- **Run Sanitizers**: ${{ github.event.inputs.run_sanitizers }}" >> $GITHUB_STEP_SUMMARY
        fi
        echo "" >> $GITHUB_STEP_SUMMARY
        
        # Configuration info
        echo "### Configuration" >> $GITHUB_STEP_SUMMARY
        echo "- **Platform**: ${{ matrix.os }}" >> $GITHUB_STEP_SUMMARY
        echo "- **Compiler**: ${{ matrix.cc }}/${{ matrix.cxx }}" >> $GITHUB_STEP_SUMMARY
        echo "- **Build Type**: ${{ env.BUILD_TYPE }}" >> $GITHUB_STEP_SUMMARY
        echo "- **Build Mode**: Full (same as release)" >> $GITHUB_STEP_SUMMARY
        if [[ "${{ runner.os }}" == "Windows" ]]; then
          echo "- **vcpkg Triplet**: x64-windows-static" >> $GITHUB_STEP_SUMMARY
          if [[ "${{ steps.vcpkg.outcome }}" != "" ]]; then
            echo "- **vcpkg Setup**: ${{ steps.vcpkg.outcome }}" >> $GITHUB_STEP_SUMMARY
          fi
        fi
        echo "" >> $GITHUB_STEP_SUMMARY
        
        # Build status
        echo "### Build Status" >> $GITHUB_STEP_SUMMARY
        CONFIGURE_OUTCOME="${{ steps.configure_windows.outcome || steps.configure_unix.outcome }}"
        if [[ "$CONFIGURE_OUTCOME" == "success" ]]; then
          echo "- ✅ Configure: Success" >> $GITHUB_STEP_SUMMARY
        else
          echo "- ❌ Configure: Failed" >> $GITHUB_STEP_SUMMARY
        fi
        
        if [[ "${{ steps.build.outcome }}" == "success" ]]; then
          echo "- ✅ Build: Success" >> $GITHUB_STEP_SUMMARY
        else
          echo "- ❌ Build: Failed" >> $GITHUB_STEP_SUMMARY
        fi
        
        if [[ "${{ steps.test_core.outcome }}" == "success" ]]; then
          echo "- ✅ Core Tests: Passed" >> $GITHUB_STEP_SUMMARY
        else
          echo "- ❌ Core Tests: Failed" >> $GITHUB_STEP_SUMMARY
        fi
        
        if [[ "${{ steps.test_additional.outcome }}" == "success" ]]; then
          echo "- ✅ Additional Tests: Passed" >> $GITHUB_STEP_SUMMARY
        elif [[ "${{ steps.test_additional.outcome }}" == "failure" ]]; then
          echo "- ⚠️ Additional Tests: Failed (informational)" >> $GITHUB_STEP_SUMMARY
        else
          echo "- ⏭️ Additional Tests: Skipped" >> $GITHUB_STEP_SUMMARY
        fi
        echo "" >> $GITHUB_STEP_SUMMARY
        
        # Artifacts info
        if [[ "${{ runner.os }}" == "Windows" && "${{ steps.build.outcome }}" == "success" ]]; then
          if [[ "${{ github.event.inputs.upload_artifacts }}" == "true" || "${{ github.event_name }}" == "push" ]]; then
            echo "### Artifacts" >> $GITHUB_STEP_SUMMARY
            echo "- 📦 Windows build artifacts uploaded: yaze-windows-ci-${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY
            echo "" >> $GITHUB_STEP_SUMMARY
          fi
        fi
        
        # Test results
        if [ -f build/core_test_results.xml ]; then
          echo "### Test Results" >> $GITHUB_STEP_SUMMARY
          echo '```' >> $GITHUB_STEP_SUMMARY
          grep -E "tests=|failures=|errors=" build/core_test_results.xml | head -1 || echo "Test summary not available"
          echo '```' >> $GITHUB_STEP_SUMMARY
        fi

  code-quality:
    name: "✨ Code Quality"
    runs-on: ubuntu-22.04
    continue-on-error: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') }}
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Install Tools
      run: |
        sudo apt-get update
        sudo apt-get install -y clang-format-14 clang-tidy-14 cppcheck
    
    - name: Check Formatting
      run: find src test -name "*.cc" -o -name "*.h" | xargs clang-format-14 --dry-run --Werror
    
    - name: Run cppcheck
      run: |
        cppcheck --enable=warning,style,performance --error-exitcode=0 \
          --suppress=missingIncludeSystem --suppress=unusedFunction --inconclusive src/
    
    - name: Run clang-tidy
      run: |
        find src -name "*.cc" -not -path "*/lib/*" | head -20 | \
          xargs clang-tidy-14 --header-filter='src/.*\.(h|hpp)$'

  memory-sanitizer:
    name: "🔬 Memory Sanitizer"
    runs-on: ubuntu-22.04
    if: |
      github.event_name == 'pull_request' || 
      (github.event_name == 'workflow_dispatch' && github.event.inputs.run_sanitizers == 'true')
    
    steps:
    - uses: actions/checkout@v4
      with:
        submodules: recursive
    
    - name: Install Dependencies
      run: |
        sudo apt-get update
        sudo apt-get install -y \
          build-essential ninja-build clang-14 libc++-14-dev libc++abi-14-dev \
          libglew-dev libxext-dev libwavpack-dev libpng-dev libgtk-3-dev libdbus-1-dev
    
    - name: Configure with AddressSanitizer
      run: |
        cmake -B build -G Ninja \
          -DCMAKE_BUILD_TYPE=Debug \
          -DCMAKE_C_COMPILER=clang-14 \
          -DCMAKE_CXX_COMPILER=clang++-14 \
          -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer" \
          -DYAZE_BUILD_TESTS=ON
    
    - name: Build
      run: cmake --build build --parallel
    
    - name: Test
      working-directory: build
      env:
        ASAN_OPTIONS: detect_leaks=1:abort_on_error=1
      run: ctest --output-on-failure

  z3ed-agent-test:
    name: "🤖 z3ed Agent"
    runs-on: macos-14
    
    steps:
    - uses: actions/checkout@v4
      with:
        submodules: recursive
    
    - name: Install Dependencies
      run: brew install ollama ninja
    
    - name: Build z3ed
      run: |
        cmake -B build_test -G Ninja \
          -DCMAKE_BUILD_TYPE=Release \
          -DZ3ED_AI=ON \
          -DYAZE_BUILD_Z3ED=ON
        cmake --build build_test --target z3ed
    
    - name: Start Ollama
      run: |
        ollama serve & 
        sleep 10
        ollama pull qwen2.5-coder:7b
    
    - name: Run Test Suite
      run: |
        chmod +x scripts/agent_test_suite.sh
        ./scripts/agent_test_suite.sh ollama
