name: Installation Tests

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

on:
  pull_request:
    branches: [master, main]
  push:
    branches: [master, main]
  workflow_dispatch:

permissions:
  contents: read

jobs:
  # Test installation on Ubuntu 24.04 (full end-to-end)
  ubuntu-install:
    name: Ubuntu 24.04 Installation Test
    runs-on: ubuntu-24.04
    timeout-minutes: 30
    strategy:
      matrix:
        install_method: [binary, source]

    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Set up Go (for source builds)
        if: matrix.install_method == 'source'
        uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
        with:
          go-version: '1.25'
          cache: true

      - name: Install Incus (Ubuntu)
        run: |
          # Add Zabbly repo for latest Incus
          sudo mkdir -p /etc/apt/keyrings/
          sudo curl -fsSL https://pkgs.zabbly.com/key.asc -o /etc/apt/keyrings/zabbly.asc
          sudo sh -c 'cat <<SOURCES > /etc/apt/sources.list.d/zabbly-incus-stable.sources
          Enabled: yes
          Types: deb
          URIs: https://pkgs.zabbly.com/incus/stable
          Suites: $(. /etc/os-release && echo ${VERSION_CODENAME})
          Components: main
          Architectures: $(dpkg --print-architecture)
          Signed-By: /etc/apt/keyrings/zabbly.asc
          SOURCES'

          sudo apt-get update
          sudo apt-get install -y incus

      - name: Initialize Incus
        run: |
          # Wait for Incus service to be ready
          sudo systemctl start incus.socket || true
          sleep 5

          # Configure subuid/subgid for UID mapping
          echo "root:1001:1" | sudo tee -a /etc/subuid
          echo "root:1001:1" | sudo tee -a /etc/subgid

          # Restart Incus to pick up changes
          sudo systemctl restart incus || true
          sleep 5

          # Initialize Incus with btrfs storage
          cat <<EOF | sudo incus admin init --preseed
          config:
            images.compression_algorithm: none
          networks:
          - config:
              ipv4.address: auto
              ipv6.address: auto
            name: incusbr0
            type: bridge
          storage_pools:
          - config:
              size: 15GiB
            name: default
            driver: btrfs
          profiles:
          - config: {}
            devices:
              eth0:
                name: eth0
                network: incusbr0
                type: nic
              root:
                path: /
                pool: default
                type: disk
            name: default
          EOF

          # Allow access without re-login
          sudo chmod 666 /var/lib/incus/unix.socket
          sudo usermod -aG incus-admin $USER

          # Enable NAT
          incus network set incusbr0 ipv4.nat=true
          incus network set incusbr0 ipv6.nat=true

          # Verify Incus is working
          incus version

      - name: Configure networking for Incus
        run: |
          echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward > /dev/null
          echo 1 | sudo tee /proc/sys/net/ipv6/conf/all/forwarding > /dev/null

          DEFAULT_IFACE=$(ip route | grep default | awk '{print $5}' | head -1)

          if sudo iptables -nL DOCKER-USER 2>/dev/null; then
            sudo iptables -I DOCKER-USER -i incusbr0 -j ACCEPT
            sudo iptables -I DOCKER-USER -o incusbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
          else
            sudo iptables -P FORWARD ACCEPT
            sudo iptables -I FORWARD 1 -i incusbr0 -j ACCEPT
            sudo iptables -I FORWARD 1 -o incusbr0 -j ACCEPT
          fi

          BRIDGE_SUBNET=$(ip -4 addr show incusbr0 | grep -oP 'inet \K[\d.]+/\d+' || echo "")
          if [ -n "$BRIDGE_SUBNET" ]; then
            sudo iptables -t nat -I POSTROUTING 1 -s $BRIDGE_SUBNET -o $DEFAULT_IFACE -j MASQUERADE
          fi

      - name: Install COI (binary method)
        if: matrix.install_method == 'binary'
        run: |
          # Build binary first (simulating a release)
          go build -o /tmp/coi-linux-amd64 ./cmd/coi

          # Make it available as a "release" for testing
          sudo mkdir -p /usr/local/bin
          sudo cp /tmp/coi-linux-amd64 /usr/local/bin/coi
          sudo chmod +x /usr/local/bin/coi

          # Verify installation
          coi version

      - name: Install COI (source method)
        if: matrix.install_method == 'source'
        run: |
          # This simulates what install.sh does when building from source
          make build
          sudo make install

          # Verify installation
          coi version

      - name: Test coi build (THIS CATCHES ISSUE #50)
        run: |
          # Run from repository root (where scripts exist)
          coi build

          # Verify image was created
          coi image list

      - name: Test coi commands
        run: |
          coi --help
          coi version
          coi image list
          coi list

      - name: Test exotic terminal types (issue #53)
        run: |
          # Test with various exotic terminal types
          for TERM_VALUE in "xterm-ghostty" "wezterm" "alacritty" "kitty"; do
            # Set TERM and try to run a simple command in a container
            if ! TERM="$TERM_VALUE" timeout 30 coi shell -e "TERM=$TERM_VALUE" <<< 'exit' 2>&1 | tee /tmp/term-test.log; then
              if grep -q "missing or unsuitable terminal" /tmp/term-test.log; then
                echo "FAILED: Got 'missing or unsuitable terminal' error with TERM=$TERM_VALUE"
                exit 1
              fi
            fi
          done

      - name: Test Quick Start scenario (USER'S ACTUAL WORKFLOW)
        if: matrix.install_method == 'binary'
        run: |
          # Clean up the existing image to force a real build
          incus image delete coi || true

          # Test if scripts are embedded/fetched (issue #50)
          cd /tmp
          if coi build 2>&1 | tee /tmp/build-output.log | grep -q "build script not found"; then
            echo "KNOWN ISSUE #50: coi build requires repository files"
            echo "Users should run 'coi build' from the cloned repository"
          fi

      - name: Cleanup
        if: always()
        run: |
          coi kill --all --force || true
          coi clean --force || true

  # End-to-end installation test on Arch Linux
  # NOTE: This test shows the full installation flow for users to reference,
  #       but runs in a container so Incus cannot fully initialize.
  #       On a real Arch system, all steps would work.
  arch-install:
    name: Arch Linux Installation Flow
    runs-on: ubuntu-24.04
    timeout-minutes: 15
    container:
      image: archlinux:latest@sha256:f5add4183c5f05abf3b65489447aee6d5714db735cd639d4000505a59838e1c1
      options: --privileged

    steps:
      - name: Install system packages
        run: |
          pacman -Syu --noconfirm
          pacman -S --noconfirm base-devel git go wget curl

      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Configure git for container
        run: |
          # Fix git ownership issues in container (GitHub Actions specific)
          git config --global --add safe.directory '*'

      - name: Install Incus
        continue-on-error: true
        run: |
          # Attempt to install (will have package conflicts in container)
          pacman -S --noconfirm incus || echo "Container limitation: package conflicts expected"
          incus version 2>/dev/null || echo "Incus not available (expected in CI container)"

      - name: Build and install COI
        run: |
          make build
          make install
          coi version

      - name: Verify installation
        run: |
          coi --help
          coi version

  # End-to-end installation test on macOS with Colima
  # NOTE: Using macos-15-intel (Intel) because ARM runners (macos-14/15) don't support
  # nested virtualization required for Colima/Lima VMs. macos-13 was deprecated in 2025.
  # macos-15-intel will be available until August 2027.
  macos-install:
    name: macOS Installation Test (Colima)
    runs-on: macos-15-intel
    timeout-minutes: 45
    strategy:
      matrix:
        install_method: [binary, source]

    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Set up Go
        uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
        with:
          go-version: '1.25'
          cache: true

      - name: Install Colima and Incus
        run: |
          brew install colima incus

      - name: Start Colima with Incus runtime
        run: |
          colima start --runtime incus --cpu 2 --memory 4
          sleep 20
          incus version

      - name: Initialize Incus
        run: |
          # Initialize Incus with minimal config using command-line flags
          # The Homebrew version doesn't support --preseed flag

          # Create storage pool if it doesn't exist
          incus storage create default dir 2>/dev/null || echo "Storage pool already exists"

          # Create network if it doesn't exist
          incus network create incusbr0 ipv4.address=auto ipv6.address=none 2>/dev/null || echo "Network already exists"

          # Enable NAT
          incus network set incusbr0 ipv4.nat=true 2>/dev/null || true

          # Update default profile with network and storage (skip if already exists)
          incus profile device add default eth0 nic network=incusbr0 2>/dev/null || echo "eth0 device already exists"
          incus profile device add default root disk path=/ pool=default 2>/dev/null || echo "root device already exists"

          # Verify Incus is working
          incus list
          incus network list
          incus storage list

      - name: Install COI (binary method)
        if: matrix.install_method == 'binary'
        run: |
          go build -o /tmp/coi-darwin-amd64 ./cmd/coi
          sudo cp /tmp/coi-darwin-amd64 /usr/local/bin/coi
          sudo chmod +x /usr/local/bin/coi
          coi version

      - name: Install COI (source method)
        if: matrix.install_method == 'source'
        run: |
          # This simulates what install.sh does when building from source
          make build
          sudo make install

          # Verify installation
          coi version

      - name: Test coi build
        run: |
          # Run from repository root (where scripts exist)
          #
          # RETRY LOGIC WITH TIMEOUT: Colima/Lima VM can have two types of failures:
          # 1. Network failures: "connection timed out" when downloading packages
          # 2. Build hangs: containerd/docker setup sometimes hangs in Colima VM
          #
          # We use timeout to catch hangs (15 min max per attempt) and retry on network failures.
          max_attempts=3
          attempt=1
          build_timeout=900  # 15 minutes per attempt

          while [ $attempt -le $max_attempts ]; do
            echo "Attempt $attempt of $max_attempts (timeout: ${build_timeout}s)..."

            # Run with timeout to prevent hangs
            if timeout $build_timeout coi build 2>&1 | tee /tmp/build-output.log; then
              echo "Build succeeded on attempt $attempt"
              coi image list
              exit 0
            fi

            exit_code=$?

            # Check if it was a timeout (exit code 124)
            if [ $exit_code -eq 124 ]; then
              echo "Build timed out after ${build_timeout}s on attempt $attempt"
              if [ $attempt -lt $max_attempts ]; then
                echo "Retrying in 10 seconds..."
                sleep 10
              fi
            # Check if failure was due to network issues
            elif grep -qi "failed to fetch\|could not connect\|connection timed out\|unable to connect" /tmp/build-output.log; then
              echo "Network failure detected on attempt $attempt"
              if [ $attempt -lt $max_attempts ]; then
                echo "Retrying in 10 seconds..."
                sleep 10
              fi
            else
              echo "Non-network/non-timeout failure detected, not retrying"
              cat /tmp/build-output.log
              exit 1
            fi

            attempt=$((attempt + 1))
          done

          echo "Build failed after $max_attempts attempts"
          exit 1

      - name: Test coi commands
        run: |
          coi --help
          coi version
          coi image list
          coi list

      - name: Test Colima/Lima auto-detection
        run: |
          # Verify UID shifting is auto-disabled for virtiofs compatibility
          echo "Colima/Lima environment detected (UID shifting auto-disabled)"

      - name: Cleanup
        if: always()
        run: |
          coi kill --all --force || true
          coi clean --force || true
          colima stop || true

  install-test-success:
    name: Installation Tests Success
    runs-on: ubuntu-24.04
    timeout-minutes: 5
    if: always()
    needs:
      - ubuntu-install
      - arch-install
      - macos-install
    steps:
      - name: Check all jobs passed
        if: |
          contains(needs.*.result, 'failure') ||
          contains(needs.*.result, 'cancelled')
        run: exit 1
