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/**'

env:
  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
  BUILD_TYPE: RelWithDebInfo
  VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"

jobs:
  build-and-test:
    strategy:
      fail-fast: false
      matrix:
        include:
          - name: "Ubuntu 22.04 (GCC-12)"
            os: ubuntu-22.04
            cc: gcc-12
            cxx: g++-12
            vcpkg_triplet: x64-linux
            
          - name: "Ubuntu 22.04 (Clang)"
            os: ubuntu-22.04
            cc: clang-15
            cxx: clang++-15
            vcpkg_triplet: x64-linux
            
          - name: "macOS 13 (Clang)"
            os: macos-13
            cc: clang
            cxx: clang++
            vcpkg_triplet: x64-osx
            
          - name: "macOS 14 (Clang)"
            os: macos-14
            cc: clang
            cxx: clang++
            vcpkg_triplet: arm64-osx
            
          - name: "Windows 2022 (MSVC x64)"
            os: windows-2022
            cc: cl
            cxx: cl
            vcpkg_triplet: x64-windows
            cmake_generator: "Visual Studio 17 2022"
            cmake_generator_platform: x64
            
          - name: "Windows 2022 (MSVC x86)"
            os: windows-2022
            cc: cl
            cxx: cl
            vcpkg_triplet: x86-windows
            cmake_generator: "Visual Studio 17 2022"
            cmake_generator_platform: Win32

    name: ${{ matrix.name }}
    runs-on: ${{ matrix.os }}

    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        submodules: recursive
        fetch-depth: 0

    - name: Export GitHub Actions cache environment variables
      uses: actions/github-script@v7
      with:
        script: |
          core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
          core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');

    - name: Set up vcpkg cache
      if: runner.os == 'Windows'
      uses: actions/cache@v4
      with:
        path: |
          ${{ github.workspace }}/vcpkg
          ${{ github.workspace }}/vcpkg_installed
        key: vcpkg-${{ matrix.vcpkg_triplet }}-${{ hashFiles('vcpkg.json') }}
        restore-keys: |
          vcpkg-${{ matrix.vcpkg_triplet }}-

    # Linux-specific setup
    - name: Install Linux dependencies
      if: runner.os == 'Linux'
      run: |
        sudo apt-get update
        sudo apt-get install -y \
          build-essential \
          ninja-build \
          pkg-config \
          libglew-dev \
          libxext-dev \
          libwavpack-dev \
          libabsl-dev \
          libboost-all-dev \
          libboost-python-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 \
          gcc-12 \
          g++-12 \
          clang-15

    - name: Set up Linux compilers
      if: runner.os == 'Linux'
      run: |
        sudo update-alternatives --install /usr/bin/cc cc /usr/bin/${{ matrix.cc }} 100
        sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/${{ matrix.cxx }} 100

    # macOS-specific setup
    - name: Install macOS dependencies
      if: runner.os == 'macOS'
      run: |
        # Install Homebrew dependencies if needed
        # brew install pkg-config libpng boost abseil

    # Windows-specific setup (skip vcpkg for CI builds)
    - name: Set up vcpkg (non-CI builds only)
      if: runner.os == 'Windows' && github.event_name != 'push' && github.event_name != 'pull_request'
      uses: lukka/run-vcpkg@v11
      with:
        vcpkgGitCommitId: 'c8696863d371ab7f46e213d8f5ca923c4aef2a00'
        runVcpkgInstall: true
        vcpkgJsonGlob: '**/vcpkg.json'
        vcpkgDirectory: '${{ github.workspace }}/vcpkg'
    - name: Configure CMake (Linux/macOS)
      if: runner.os != 'Windows'
      run: |
        cmake -B ${{ github.workspace }}/build \
          -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \
          -DCMAKE_C_COMPILER=${{ matrix.cc }} \
          -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} \
          -DCMAKE_POLICY_VERSION_MINIMUM=3.16 \
          -DYAZE_MINIMAL_BUILD=ON \
          -DYAZE_ENABLE_ROM_TESTS=OFF \
          -DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF \
          -DYAZE_ENABLE_UI_TESTS=OFF \
          -Wno-dev \
          -GNinja

    - name: Configure CMake (Windows)
      if: runner.os == 'Windows'
      shell: cmd
      run: |
        cmake -B ${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_POLICY_VERSION_MINIMUM=3.16 -DYAZE_MINIMAL_BUILD=ON -DYAZE_ENABLE_ROM_TESTS=OFF -DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF -DYAZE_ENABLE_UI_TESTS=OFF -Wno-dev -G "${{ matrix.cmake_generator }}" -A ${{ matrix.cmake_generator_platform }}

    # Build
    - name: Build
      run: cmake --build ${{ github.workspace }}/build --config ${{ env.BUILD_TYPE }} --parallel

    # Test (stable core functionality only for CI)
    - name: Run Core Tests
      working-directory: ${{ github.workspace }}/build
      run: ctest --build-config ${{ env.BUILD_TYPE }} --output-on-failure -j1 -R "AsarWrapperTest|SnesTileTest|CompressionTest|SnesPaletteTest|HexTest"
      
    # Run experimental tests separately (allowed to fail for information only)
    - name: Run Additional Tests (Informational)
      working-directory: ${{ github.workspace }}/build
      continue-on-error: true
      run: ctest --build-config ${{ env.BUILD_TYPE }} --output-on-failure --parallel -E "AsarWrapperTest|SnesTileTest|CompressionTest|SnesPaletteTest|HexTest|CpuTest|Spc700Test|ApuTest|MessageTest|.*IntegrationTest"

    # Package (only on successful builds)
    - name: Package artifacts
      if: success()
      run: |
        cmake --build ${{ github.workspace }}/build --config ${{ env.BUILD_TYPE }} --target package

    # Upload artifacts
    - name: Upload build artifacts
      if: success()
      uses: actions/upload-artifact@v4
      with:
        name: yaze-${{ matrix.name }}-${{ github.sha }}
        path: |
          ${{ github.workspace }}/build/bin/
          ${{ github.workspace }}/build/lib/
        retention-days: 7

    # Upload packages for release candidates
    - name: Upload package artifacts
      if: success() && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/'))
      uses: actions/upload-artifact@v4
      with:
        name: yaze-package-${{ matrix.name }}-${{ github.sha }}
        path: |
          ${{ github.workspace }}/build/*.tar.gz
          ${{ github.workspace }}/build/*.zip
          ${{ github.workspace }}/build/*.dmg
          ${{ github.workspace }}/build/*.msi
        retention-days: 30

  code-quality:
    name: Code Quality Checks
    runs-on: ubuntu-22.04
    # Relaxed requirements for releases and master branch:
    # - Formatting errors become warnings
    # - Fewer cppcheck categories enabled  
    # - Reduced clang-tidy file count
    # - Job failure won't block releases
    continue-on-error: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') }}
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        submodules: recursive

    - name: Install dependencies
      run: |
        sudo apt-get update
        sudo apt-get install -y \
          clang-format-14 \
          clang-tidy-14 \
          cppcheck

    - name: Check code formatting
      run: |
        # Relaxed formatting check for releases and master branch
        if [[ "${{ github.ref }}" == "refs/heads/master" ]] || [[ "${{ github.ref }}" == refs/tags/* ]] || [[ "${{ github.event_name }}" == "pull_request" && "${{ github.base_ref }}" == "master" ]]; then
          echo "🔄 Running relaxed formatting check for release/master branch..."
          find src test -name "*.cc" -o -name "*.h" | \
          xargs clang-format-14 --dry-run --Werror || {
            echo "⚠️  Code formatting issues found, but allowing for release builds"
            echo "📝 Consider running 'make format' to fix formatting before next release"
            exit 0
          }
        else
          echo "🔍 Running strict formatting check for development..."
          find src test -name "*.cc" -o -name "*.h" | \
          xargs clang-format-14 --dry-run --Werror
        fi

    - name: Run cppcheck
      run: |
        if [[ "${{ github.ref }}" == "refs/heads/master" ]] || [[ "${{ github.ref }}" == refs/tags/* ]]; then
          echo "🔄 Running relaxed cppcheck for release/master branch..."
          cppcheck --enable=warning \
            --error-exitcode=0 \
            --suppress=missingIncludeSystem \
            --suppress=unusedFunction \
            --suppress=unmatchedSuppression \
            --suppress=variableScope \
            --suppress=cstyleCast \
            --suppress=unreadVariable \
            --suppress=unusedStructMember \
            --suppress=constParameter \
            --suppress=constVariable \
            --suppress=useStlAlgorithm \
            --suppress=noExplicitConstructor \
            --suppress=passedByValue \
            --suppress=functionStatic \
            src/ || echo "Cppcheck completed (non-blocking for releases)"
        else
          echo "🔍 Running standard cppcheck for development..."
          cppcheck --enable=warning,style,performance \
            --error-exitcode=0 \
            --suppress=missingIncludeSystem \
            --suppress=unusedFunction \
            --suppress=unmatchedSuppression \
            --suppress=variableScope \
            --suppress=cstyleCast \
            --suppress=unreadVariable \
            --suppress=unusedStructMember \
            --suppress=constParameter \
            --suppress=constVariable \
            --suppress=useStlAlgorithm \
            --inconclusive \
            src/ || echo "Cppcheck completed with warnings (non-blocking)"
        fi
          
    - name: Run clang-tidy (lenient)
      run: |
        if [[ "${{ github.ref }}" == "refs/heads/master" ]] || [[ "${{ github.ref }}" == refs/tags/* ]]; then
          echo "🔄 Running minimal clang-tidy for release/master branch..."
          # Only check a small subset of critical files for releases
          find src -name "*.cc" -not -path "*/lib/*" -not -path "*/gui/*" | head -10 | \
          xargs clang-tidy-14 --config-file=.clang-tidy \
          --header-filter='src/.*\.(h|hpp)$' || echo "Clang-tidy completed (non-blocking for releases)"
        else
          echo "🔍 Running standard clang-tidy for development..."
          # Run clang-tidy on a subset of files to avoid overwhelming output
          find src -name "*.cc" -not -path "*/lib/*" | head -20 | \
          xargs clang-tidy-14 --config-file=.clang-tidy \
          --header-filter='src/.*\.(h|hpp)$' || echo "Clang-tidy completed with warnings (non-blocking)"
        fi

  memory-sanitizer:
    name: Memory Sanitizer (Linux)
    runs-on: ubuntu-22.04
    if: github.event_name == 'pull_request'
    
    steps:
    - name: Checkout code
      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 ${{ github.workspace }}/build \
          -DCMAKE_BUILD_TYPE=Debug \
          -DCMAKE_C_COMPILER=clang-14 \
          -DCMAKE_CXX_COMPILER=clang++-14 \
          -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer" \
          -DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer" \
          -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" \
          -DYAZE_MINIMAL_BUILD=ON \
          -DYAZE_ENABLE_ROM_TESTS=OFF \
          -DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF \
          -GNinja

    - name: Build
      run: cmake --build ${{ github.workspace }}/build --parallel

    - name: Test with AddressSanitizer
      working-directory: ${{ github.workspace }}/build
      env:
        ASAN_OPTIONS: detect_leaks=1:abort_on_error=1
      run: ctest --output-on-failure

  coverage:
    name: Code Coverage
    runs-on: ubuntu-22.04
    if: github.event_name == 'pull_request'
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        submodules: recursive

    - name: Install dependencies
      run: |
        sudo apt-get update
        sudo apt-get install -y \
          build-essential \
          ninja-build \
          gcov \
          lcov \
          libglew-dev \
          libxext-dev \
          libwavpack-dev \
          libpng-dev \
          libgtk-3-dev \
          libdbus-1-dev

    - name: Configure with coverage
      run: |
        cmake -B ${{ github.workspace }}/build \
          -DCMAKE_BUILD_TYPE=Debug \
          -DCMAKE_CXX_FLAGS="--coverage" \
          -DCMAKE_C_FLAGS="--coverage" \
          -DCMAKE_EXE_LINKER_FLAGS="--coverage" \
          -DYAZE_MINIMAL_BUILD=ON \
          -DYAZE_ENABLE_ROM_TESTS=OFF \
          -DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF \
          -GNinja

    - name: Build
      run: cmake --build ${{ github.workspace }}/build --parallel

    - name: Test
      working-directory: ${{ github.workspace }}/build
      run: ctest --output-on-failure

    - name: Generate coverage report
      run: |
        lcov --capture --directory ${{ github.workspace }}/build --output-file coverage.info
        lcov --remove coverage.info '/usr/*' --output-file coverage.info
        lcov --remove coverage.info '**/test/**' --output-file coverage.info
        lcov --remove coverage.info '**/lib/**' --output-file coverage.info

    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v3
      with:
        file: ./coverage.info
        flags: unittests
        name: codecov-umbrella
        fail_ci_if_error: false
