package App::GHGen::Generator;

use v5.36;

use strict;
use warnings;

use App::GHGen::PerlCustomizer qw(detect_perl_requirements generate_custom_perl_workflow);

use Exporter 'import';
our @EXPORT_OK = qw(
	generate_workflow
	list_workflow_types
);

our $VERSION = '0.08';

=head1 NAME

App::GHGen::Generator - Generate GitHub Actions workflows

=head1 SYNOPSIS

    use App::GHGen::Generator qw(generate_workflow);
    use Path::Tiny;

    my $yaml = generate_workflow('perl');
    path('.github/workflows/ci.yml')->spew_utf8($yaml);

=head1 FUNCTIONS

=head2 generate_workflow($type)

Generate a complete GitHub Actions workflow YAML string for the given project type.

=head3 Purpose

Dispatch to the appropriate language-specific workflow generator and return
the resulting YAML string ready to be written to a C<.github/workflows/> file.

=head3 Arguments

=over 4

=item C<$type> (Str, required)

A project-type identifier.  Supported values: C<perl>, C<node>, C<python>,
C<rust>, C<go>, C<ruby>, C<java>, C<cpp>, C<php>, C<docker>, C<static>.

=back

=head3 Returns

A multi-line YAML string beginning with C<---> when C<$type> is recognised,
or C<undef> when the type is not supported.

=head3 Side Effects

The C<perl> type reads the current directory to detect project requirements
(via L<App::GHGen::PerlCustomizer>).

=head3 Usage Example

    use App::GHGen::Generator qw(generate_workflow);
    my $yaml = generate_workflow('node') // die "unsupported type";
    path('.github/workflows/ci.yml')->spew_utf8($yaml);

=head3 API SPECIFICATION

=head4 Input

    { type => { type => 'scalar', required => 1 } }

=head4 Output

    known type   → { type => 'scalar' }   # YAML string
    unknown type → undef

=head3 FORMAL SPECIFICATION

    SupportedTypes ≔ { perl, node, python, rust, go, ruby, java, cpp, php, docker, static }

    generate_workflow : ℤ* → ℤ* ∪ { ⊥ }

    t ∈ SupportedTypes → generators[t]()    (non-empty YAML string)
    t ∉ SupportedTypes → ⊥                  (undef)

=cut

sub generate_workflow($type) {
    my %generators = (
        perl   => \&_generate_perl_workflow,
        node   => \&_generate_node_workflow,
        python => \&_generate_python_workflow,
        rust   => \&_generate_rust_workflow,
        go     => \&_generate_go_workflow,
        ruby   => \&_generate_ruby_workflow,
        java   => \&_generate_java_workflow,
        cpp    => \&_generate_cpp_workflow,
        php    => \&_generate_php_workflow,
        docker => \&_generate_docker_workflow,
        static => \&_generate_static_workflow,
    );

	return undef unless exists $generators{$type};
	return $generators{$type}->();
}

=head2 list_workflow_types()

Return a flat hash of all supported workflow types and their descriptions.

=head3 Purpose

Enumerate every type that C<generate_workflow> can handle, with a one-line
human-readable description for each.

=head3 Arguments

None.

=head3 Returns

A flat hash (not a reference) mapping type strings to description strings.
Contains exactly eleven entries: C<perl>, C<node>, C<python>, C<rust>,
C<go>, C<ruby>, C<java>, C<cpp>, C<php>, C<docker>, C<static>.

=head3 Side Effects

None.  Pure function.

=head3 Usage Example

    my %types = list_workflow_types();
    say "$_: $types{$_}" for sort keys %types;

=head3 API SPECIFICATION

=head4 Input

    # No parameters.

=head4 Output

    { type => 'hashref', keys => { '*' => { type => 'scalar' } } }

=head3 FORMAL SPECIFICATION

    list_workflow_types : → ℤ* → ℤ*

    result ≔ { t ↦ desc(t) ∣ t ∈ SupportedTypes }
    |result| = 11

=cut

sub list_workflow_types() {
    return (
        node   => 'Node.js/npm projects with testing and linting',
        python => 'Python projects with pytest and coverage',
        rust   => 'Rust projects with cargo, clippy, and formatting',
        go     => 'Go projects with testing and race detection',
        ruby   => 'Ruby projects with bundler and rake',
        perl   => 'Perl projects with cpanm, prove, and coverage',
        java   => 'Java projects with Maven or Gradle',
        cpp    => 'C++ projects with CMake',
        php    => 'PHP projects with Composer and PHPUnit',
        docker => 'Docker image build and push workflow',
        static => 'Static site deployment to GitHub Pages',
    );
}

# Private workflow generators

sub _generate_perl_workflow() {
	# Try to detect requirements from project
	my $reqs = detect_perl_requirements();

	# Use detected min version or default to 5.36
	my $min_version = $reqs->{min_version} // '5.36';

	# Generate custom workflow with detected settings
	return generate_custom_perl_workflow({
		min_perl_version => $min_version,
		max_perl_version => '5.40',
		os => ['macos-latest', 'ubuntu-latest', 'windows-latest'],
		enable_critic => 1,
		enable_coverage => 1,
	});
}

sub _generate_node_workflow() {
	return <<'YAML';
---
name: Node.js CI

'on':
  push:
    branches:
      - main
      - develop
  pull_request:
    branches:
      - main
      - develop

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

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version:
          - 18.x
          - 20.x
          - 22.x
    steps:
      - name: Checkout code
        uses: actions/checkout@76

      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}

      - name: Cache dependencies
        uses: actions/cache@v6
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install dependencies
        run: npm ci

      - name: Run linter
        run: npm run lint --if-present

      - name: Run tests
        run: npm test

      - name: Build project
        run: npm run build --if-present
YAML
}

sub _generate_python_workflow() {
    return <<'YAML';
---
name: Python CI

'on':
  push:
    branches:
      - main
      - develop
  pull_request:
    branches:
      - main
      - develop

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

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version:
          - '3.9'
          - '3.10'
          - '3.11'
          - '3.12'
    steps:
      - name: Checkout code
        uses: actions/checkout@v7

      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}

      - name: Cache pip packages
        uses: actions/cache@v6
        with:
          path: ~/.cache/pip
          key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
          restore-keys: |
            ${{ runner.os }}-pip-

      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install pytest pytest-cov flake8

      - name: Lint with flake8
        run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics

      - name: Run tests with coverage
        run: pytest --cov=. --cov-report=xml
YAML
}

sub _generate_rust_workflow() {
    return <<'YAML';
---
name: Rust CI

'on':
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

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

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v7

      - name: Setup Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Cache cargo
        uses: actions/cache@v6
        with:
          path: |
            ~/.cargo/bin/
            ~/.cargo/registry/index/
            ~/.cargo/registry/cache/
            ~/.cargo/git/db/
            target/
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}

      - name: Check formatting
        run: cargo fmt -- --check

      - name: Run clippy
        run: cargo clippy -- -D warnings

      - name: Run tests
        run: cargo test --verbose

      - name: Build release
        run: cargo build --release --verbose
YAML
}

sub _generate_go_workflow() {
    return <<'YAML';
---
name: Go CI

'on':
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

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

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v7

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.22'

      - name: Cache Go modules
        uses: actions/cache@v6
        with:
          path: ~/go/pkg/mod
          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-

      - name: Download dependencies
        run: go mod download

      - name: Run go vet
        run: go vet ./...

      - name: Run tests
        run: go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...

      - name: Build
        run: go build -v ./...
YAML
}

sub _generate_ruby_workflow() {
    return <<'YAML';
---
name: Ruby CI

'on':
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

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

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        ruby-version:
          - '3.1'
          - '3.2'
          - '3.3'
    steps:
      - name: Checkout code
        uses: actions/checkout@v7

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: ${{ matrix.ruby-version }}
          bundler-cache: true

      - name: Run tests
        run: bundle exec rake test
YAML
}

sub _generate_docker_workflow() {
    return <<'YAML';
---
name: Docker Build

'on':
  push:
    branches:
      - main
    tags:
      - v*
  pull_request:
    branches:
      - main

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

permissions:
  contents: read
  packages: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v7

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
        if: github.event_name != 'pull_request'

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: your-username/your-image

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
YAML
}

sub _generate_static_workflow() {
    return <<'YAML';
---
name: Deploy Static Site

'on':
  push:
    branches:
      - main

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: pages
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v7

      - name: Setup Pages
        uses: actions/configure-pages@v4

      - name: Build
        run: echo "Add your build command here"

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: ./public

  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4
YAML
}

sub _generate_java_workflow() {
    return <<'YAML';
---
name: Java CI

'on':
  push:
    branches:
      - main
      - develop
  pull_request:
    branches:
      - main
      - develop

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

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        java-version:
          - '11'
          - '17'
          - '21'
    steps:
      - name: Checkout code
        uses: actions/checkout@v7

      - name: Set up JDK ${{ matrix.java-version }}
        uses: actions/setup-java@v4
        with:
          java-version: ${{ matrix.java-version }}
          distribution: 'temurin'

      - name: Cache Maven packages
        uses: actions/cache@v6
        with:
          path: ~/.m2/repository
          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
          restore-keys: |
            ${{ runner.os }}-maven-

      - name: Build with Maven
        run: mvn -B package --file pom.xml

      - name: Run tests
        run: mvn -B test

      - name: Generate test report
        if: always()
        run: mvn surefire-report:report
YAML
}

sub _generate_cpp_workflow() {
    return <<'YAML';
---
name: C++ CI

'on':
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

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

permissions:
  contents: read

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os:
          - ubuntu-latest
          - macos-latest
          - windows-latest
        build-type:
          - Debug
          - Release
    steps:
      - name: Checkout code
        uses: actions/checkout@v7

      - name: Install CMake
        uses: lukka/get-cmake@latest

      - name: Cache build artifacts
        uses: actions/cache@v6
        with:
          path: |
            build
            ~/.cache/ccache
          key: ${{ runner.os }}-${{ matrix.build-type }}-${{ hashFiles('**/CMakeLists.txt') }}

      - name: Configure CMake
        run: cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build-type }}

      - name: Build
        run: cmake --build build --config ${{ matrix.build-type }}

      - name: Run tests
        working-directory: build
        run: ctest -C ${{ matrix.build-type }} --output-on-failure
YAML
}

sub _generate_php_workflow() {
    return <<'YAML';
---
name: PHP CI

'on':
  push:
    branches:
      - main
      - develop
  pull_request:
    branches:
      - main
      - develop

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

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        php-version:
          - '8.1'
          - '8.2'
          - '8.3'
    steps:
      - name: Checkout code
        uses: actions/checkout@v7

      - name: Setup PHP ${{ matrix.php-version }}
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php-version }}
          extensions: mbstring, xml, ctype, json
          coverage: xdebug

      - name: Validate composer.json
        run: composer validate --strict

      - name: Cache Composer packages
        uses: actions/cache@v6
        with:
          path: vendor
          key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
          restore-keys: |
            ${{ runner.os }}-php-

      - name: Install dependencies
        run: composer install --prefer-dist --no-progress

      - name: Run PHPUnit tests
        run: vendor/bin/phpunit --coverage-text

      - name: Run PHP CodeSniffer
        run: vendor/bin/phpcs --standard=PSR12 src tests
        continue-on-error: true
YAML
}

=head1 AUTHOR

Nigel Horne E<lt>njh@nigelhorne.comE<gt>

L<https://github.com/nigelhorne>

=head1 LICENSE

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut

1;
