name: CI

on:
  push:
    branches: [master]
    tags: ["v*"]
  pull_request:
    branches: [master]
  workflow_dispatch:

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

env:
  # Pin the ADK version to match the committed manifest.json.
  # This ensures CI is hermetic — `just scan` in CI installs the same ADK
  # that generated the committed manifest.  Bump this when updating the manifest.
  ADK_VERSION: "1.25.0"

jobs:
  # ---------------------------------------------------------------------------
  # LINT — pre-commit on hand-written files only (generated files excluded)
  # ---------------------------------------------------------------------------
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: astral-sh/setup-uv@v5
        with:
          enable-cache: true
          version: "0.8.17"

      - uses: taiki-e/install-action@just

      - name: Install dependencies
        run: uv sync --all-extras

      - name: Run Pre-commit Hooks (hand-written files only)
        run: uv run pre-commit run --all-files --show-diff-on-failure

  # ---------------------------------------------------------------------------
  # CHECK-GEN — verify generated files match generator output (idempotency)
  # Uses the COMMITTED manifest.json + seed.toml as inputs — does NOT re-scan ADK.
  # Catches: hand-edited generated files, stale codegen, generator drift.
  # To check for upstream ADK changes, run `just scan` locally and diff.
  # ---------------------------------------------------------------------------
  check-gen:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: astral-sh/setup-uv@v5
        with:
          enable-cache: true
          version: "0.8.17"

      - uses: taiki-e/install-action@just

      - name: Install dependencies
        run: uv sync --all-extras

      - name: Regenerate from committed manifest+seed and verify no diff
        run: |
          just generate
          if ! git diff --quiet; then
            echo "::error::Generated files don't match generator output. Run 'just generate' and commit."
            git diff --stat
            git diff
            exit 1
          fi

  # ---------------------------------------------------------------------------
  # TYPECHECK — validates generated stubs and hand-written code
  # ---------------------------------------------------------------------------
  typecheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: astral-sh/setup-uv@v5
        with:
          enable-cache: true
          version: "0.8.17"

      - uses: taiki-e/install-action@just

      - name: Install dependencies
        run: uv sync --all-extras

      - name: Generate Code and Stubs
        run: just scan seed generate

      - name: Run pyright Typecheck (stubs)
        run: just typecheck

      - name: Run pyright Typecheck (hand-written code)
        run: just typecheck-core

  # ---------------------------------------------------------------------------
  # TEST — runs on every push and PR, across Python versions
  # ---------------------------------------------------------------------------
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.11", "3.12", "3.13"]
    steps:
      - uses: actions/checkout@v4

      - uses: astral-sh/setup-uv@v5
        with:
          enable-cache: true
          version: "0.8.17"
          python-version: ${{ matrix.python-version }}

      - uses: taiki-e/install-action@just

      - name: Install dependencies
        run: uv sync --all-extras

      - name: Generate Code
        run: just scan seed generate

      - name: Run Tests with coverage
        run: uv run pytest tests/ -v --tb=short --cov=src/adk_fluent --cov-report=xml --cov-report=term-missing

      - name: Run Cookbook Tests
        run: uv run pytest examples/cookbook/ -q --tb=short

      - name: Upload coverage to Codecov
        if: matrix.python-version == '3.12'
        uses: codecov/codecov-action@v4
        with:
          file: coverage.xml
          fail_ci_if_error: false
        env:
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

  # ---------------------------------------------------------------------------
  # BUILD — produces the wheel and sdist, then smoke-tests the wheel
  # ---------------------------------------------------------------------------
  build:
    needs: [lint, check-gen, typecheck, test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: astral-sh/setup-uv@v5
        with:
          enable-cache: true
          version: "0.8.17"

      - uses: taiki-e/install-action@just

      - name: Install dependencies
        run: uv sync --all-extras

      - name: Generate Code
        run: just scan seed generate

      - name: Build package
        run: uv build

      - name: Smoke-test wheel install
        run: |
          uv venv /tmp/smoke-venv
          VIRTUAL_ENV=/tmp/smoke-venv uv pip install dist/*.whl
          /tmp/smoke-venv/bin/python -c "
          from adk_fluent import Agent, Pipeline, FanOut, Loop
          from adk_fluent._version import __version__
          a = Agent('smoke-test').instruct('hello')
          print(f'Smoke test passed: adk-fluent v{__version__}')
          "

      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/

  # ---------------------------------------------------------------------------
  # VERSION CHECK — on tagged releases, assert tag matches _version.py
  # ---------------------------------------------------------------------------
  version-check:
    if: startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Verify tag matches _version.py
        run: |
          TAG="${GITHUB_REF#refs/tags/v}"
          VERSION=$(python -c "exec(open('src/adk_fluent/_version.py').read()); print(__version__)")
          if [ "$TAG" != "$VERSION" ]; then
            echo "::error::Tag v$TAG does not match _version.py ($VERSION)"
            exit 1
          fi
          echo "Version check passed: v$TAG == $VERSION"

  # ---------------------------------------------------------------------------
  # PUBLISH — PyPI via Trusted Publishing
  # ---------------------------------------------------------------------------
  publish-pypi:
    needs: [build, version-check]
    if: startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-latest
    environment:
      name: pypi
      url: https://pypi.org/p/adk-fluent
    permissions:
      id-token: write
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist/

      - uses: pypa/gh-action-pypi-publish@release/v1
        with:
          skip-existing: true

  publish-testpypi:
    needs: build
    if: github.event_name == 'workflow_dispatch'
    runs-on: ubuntu-latest
    environment:
      name: testpypi
      url: https://test.pypi.org/p/adk-fluent
    permissions:
      id-token: write
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist/

      - uses: pypa/gh-action-pypi-publish@release/v1
        with:
          repository-url: https://test.pypi.org/legacy/
          skip-existing: true
