#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
# =============================================================================
# Recette - Test Generator Module
# Generates unit, functional, and Behat tests from detected errors
# Implements the Golden Rule: A fixed bug should NEVER reappear
# =============================================================================

# Template directory
RECETTE_TEMPLATES_DIR="${RECETTE_TEMPLATES_DIR:-$(dirname "${BASH_SOURCE[0]}")/../templates}"

# Generated tests output
RECETTE_GENERATED_TESTS_DIR="${RECETTE_BASE:-.recette}/regression/tests"

# Error classification for test type selection
declare -A ERROR_TO_TEST_TYPE=(
    ["logic"]="unit"
    ["validation"]="unit"
    ["calculation"]="unit"
    ["api"]="functional"
    ["service"]="functional"
    ["database"]="functional"
    ["user_flow"]="behat"
    ["ui"]="behat"
    ["interaction"]="behat"
    ["navigation"]="behat"
    ["visual"]="behat"
    ["security"]="functional"
)

# =============================================================================
# Test Generation Main Entry
# =============================================================================

# Generate tests from an error
generate_tests_from_error() {
    local error_id="$1"
    local error_type="$2"
    local error_description="$3"
    local test_context="${4:-}"

    mkdir -p "$RECETTE_GENERATED_TESTS_DIR"

    # Determine test types to generate
    local primary_type="${ERROR_TO_TEST_TYPE[$error_type]:-unit}"

    local generated_tests=()

    # Always generate unit test for logic/validation errors
    if [[ "$primary_type" == "unit" ]] || [[ "$error_type" == "logic" ]] || [[ "$error_type" == "validation" ]]; then
        local unit_test
        unit_test=$(generate_unit_test "$error_id" "$error_description" "$test_context")
        generated_tests+=("unit:$unit_test")
    fi

    # Generate functional test for API/service errors
    if [[ "$primary_type" == "functional" ]] || [[ "$error_type" == "api" ]] || [[ "$error_type" == "service" ]]; then
        local functional_test
        functional_test=$(generate_functional_test "$error_id" "$error_description" "$test_context")
        generated_tests+=("functional:$functional_test")
    fi

    # Generate Behat for user flow errors
    if [[ "$primary_type" == "behat" ]] || [[ "$error_type" == "user_flow" ]] || [[ "$error_type" == "ui" ]]; then
        local behat_test
        behat_test=$(generate_behat_feature "$error_id" "$error_description" "$test_context")
        generated_tests+=("behat:$behat_test")
    fi

    # Output generated test paths
    printf '%s\n' "${generated_tests[@]}"
}

# =============================================================================
# Unit Test Generation
# =============================================================================

generate_unit_test() {
    local error_id="$1"
    local description="$2"
    local context="$3"

    local test_name
    test_name=$(sanitize_test_name "$error_id")

    local output_file="$RECETTE_GENERATED_TESTS_DIR/Unit/${test_name}Test.php"
    mkdir -p "$(dirname "$output_file")"

    # Parse context for class/method info
    local class_name="UnknownClass"
    local method_name="unknownMethod"

    if [[ -n "$context" ]]; then
        class_name=$(echo "$context" | grep -oP 'class[=:]\K[A-Za-z0-9_]+' | head -1)
        method_name=$(echo "$context" | grep -oP 'method[=:]\K[A-Za-z0-9_]+' | head -1)
        [[ -z "$class_name" ]] && class_name="UnknownClass"
        [[ -z "$method_name" ]] && method_name="unknownMethod"
    fi

    cat > "$output_file" << EOF
<?php

declare(strict_types=1);

namespace Tests\\Unit\\Regression;

use PHPUnit\\Framework\\TestCase;
use PHPUnit\\Framework\\Attributes\\Group;

/**
 * Regression test for error: $error_id
 *
 * Description: $description
 *
 * Golden Rule: This test ensures the bug never reappears.
 *
 * @generated by Recette
 * @see .recette/regression/registry.yaml
 */
#[Group('regression')]
class ${test_name}Test extends TestCase
{
    /**
     * Test that the error condition from $error_id does not occur.
     *
     * @regression $error_id
     */
    public function test_${method_name}_does_not_reproduce_${error_id}(): void
    {
        // Arrange
        // TODO: Set up the conditions that caused the error
        \$subject = \$this->createSubject();

        // Act
        // TODO: Perform the action that triggered the error
        \$result = \$subject->${method_name}();

        // Assert
        // TODO: Verify the error condition does not occur
        \$this->assertNotNull(\$result, 'Result should not be null (regression: $error_id)');

        // Additional assertions based on the error type
        // $description
    }

    private function createSubject(): mixed
    {
        // TODO: Create the subject under test
        // Based on context: $class_name
        return null;
    }
}
EOF

    echo "$output_file"
}

# =============================================================================
# Functional Test Generation
# =============================================================================

generate_functional_test() {
    local error_id="$1"
    local description="$2"
    local context="$3"

    local test_name
    test_name=$(sanitize_test_name "$error_id")

    local output_file="$RECETTE_GENERATED_TESTS_DIR/Functional/${test_name}Test.php"
    mkdir -p "$(dirname "$output_file")"

    # Parse context for endpoint info
    local endpoint="/api/unknown"
    local method="GET"

    if [[ -n "$context" ]]; then
        endpoint=$(echo "$context" | grep -oP 'endpoint[=:]\K[^ ]+' | head -1)
        method=$(echo "$context" | grep -oP 'method[=:]\K[A-Z]+' | head -1)
        [[ -z "$endpoint" ]] && endpoint="/api/unknown"
        [[ -z "$method" ]] && method="GET"
    fi

    cat > "$output_file" << EOF
<?php

declare(strict_types=1);

namespace Tests\\Functional\\Regression;

use Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase;
use Symfony\\Component\\HttpFoundation\\Response;

/**
 * Regression test for error: $error_id
 *
 * Description: $description
 *
 * Golden Rule: This test ensures the bug never reappears.
 *
 * @generated by Recette
 * @see .recette/regression/registry.yaml
 */
class ${test_name}Test extends WebTestCase
{
    /**
     * Test that the API error from $error_id does not occur.
     *
     * @group regression
     * @regression $error_id
     */
    public function test_endpoint_does_not_reproduce_${error_id}(): void
    {
        \$client = static::createClient();

        // Arrange
        // TODO: Set up authentication and request data
        \$requestData = [];

        // Act
        \$client->request(
            '$method',
            '$endpoint',
            [],
            [],
            ['CONTENT_TYPE' => 'application/json'],
            json_encode(\$requestData)
        );

        \$response = \$client->getResponse();

        // Assert - Verify the error does not occur
        \$this->assertNotEquals(
            Response::HTTP_INTERNAL_SERVER_ERROR,
            \$response->getStatusCode(),
            'Should not return 500 error (regression: $error_id)'
        );

        // Additional assertions based on the error
        // $description
        \$content = json_decode(\$response->getContent(), true);
        \$this->assertArrayNotHasKey('error', \$content, 'Response should not contain error key');
    }
}
EOF

    echo "$output_file"
}

# =============================================================================
# Behat Feature Generation
# =============================================================================

generate_behat_feature() {
    local error_id="$1"
    local description="$2"
    local context="$3"

    local feature_name
    feature_name=$(echo "$error_id" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/_/g')

    local output_file="$RECETTE_GENERATED_TESTS_DIR/Behat/regression_${feature_name}.feature"
    mkdir -p "$(dirname "$output_file")"

    # Parse context for page/action info
    local page_url="/unknown"
    local element_selector="#unknown"

    if [[ -n "$context" ]]; then
        page_url=$(echo "$context" | grep -oP 'url[=:]\K[^ ]+' | head -1)
        element_selector=$(echo "$context" | grep -oP 'selector[=:]\K[^ ]+' | head -1)
        [[ -z "$page_url" ]] && page_url="/unknown"
        [[ -z "$element_selector" ]] && element_selector="#unknown"
    fi

    cat > "$output_file" << EOF
@regression @$error_id
Feature: Regression test for $error_id
  As a user
  I want to ensure the bug $error_id does not reappear
  So that the application remains stable

  Background:
    # Golden Rule: A fixed bug should NEVER reappear
    # Description: $description
    # Generated by Recette

  @regression
  Scenario: Verify $error_id does not reproduce
    Given I am on "$page_url"
    # TODO: Add preconditions that set up the error scenario

    When I perform the action that caused the error
    # TODO: Add the specific steps that triggered the bug

    Then I should not see an error message
    And the page should function correctly
    # TODO: Add specific assertions for this error

  @regression @negative
  Scenario: Edge case for $error_id
    Given I am on "$page_url"
    # TODO: Add edge case conditions

    When I trigger the edge case
    # TODO: Add edge case actions

    Then the application should handle it gracefully
    And I should not see "$description"
EOF

    echo "$output_file"
}

# =============================================================================
# Helper Functions
# =============================================================================

sanitize_test_name() {
    local input="$1"

    # Convert to PascalCase and remove invalid characters
    echo "$input" | sed 's/[-_]/ /g' | awk '{for(i=1;i<=NF;i++)$i=toupper(substr($i,1,1))tolower(substr($i,2))}1' | tr -d ' '
}

# Get recommended test types for an error
get_recommended_test_types() {
    local error_type="$1"

    local types=()

    case "$error_type" in
        logic|validation|calculation)
            types+=("unit")
            ;;
        api|service|database)
            types+=("functional")
            types+=("unit")
            ;;
        user_flow|ui|interaction|navigation)
            types+=("behat")
            types+=("functional")
            ;;
        security)
            types+=("functional")
            types+=("unit")
            ;;
        visual)
            types+=("behat")
            ;;
        *)
            types+=("unit")
            types+=("behat")
            ;;
    esac

    printf '%s\n' "${types[@]}"
}

# =============================================================================
# Test Registration in Regression Suite
# =============================================================================

register_regression_test() {
    local error_id="$1"
    local test_path="$2"
    local test_type="$3"

    local registry_file="${RECETTE_BASE:-.recette}/regression/registry.yaml"

    mkdir -p "$(dirname "$registry_file")"

    # Create registry if it doesn't exist
    if [[ ! -f "$registry_file" ]]; then
        cat > "$registry_file" << 'EOF'
# Recette Regression Test Registry
# Golden Rule: A fixed bug should NEVER reappear
# All tests in this registry are run on every build

version: "1.0"
entries: []
EOF
    fi

    # Add entry to registry
    if command -v yq &>/dev/null; then
        local entry="
  - id: \"REG-$(date +%s)-$RANDOM\"
    error_id: \"$error_id\"
    test_type: \"$test_type\"
    test_path: \"$test_path\"
    registered_at: \"$(date -Iseconds)\"
    status: \"active\"
"
        yq e -i ".entries += [$entry]" "$registry_file" 2>/dev/null || true
    fi
}

# =============================================================================
# Test Content Templates
# =============================================================================

# Get unit test template content
get_unit_test_template() {
    local template_file="$RECETTE_TEMPLATES_DIR/unit-test.php.template"

    if [[ -f "$template_file" ]]; then
        cat "$template_file"
    else
        cat << 'EOF'
<?php

declare(strict_types=1);

namespace Tests\Unit\Regression;

use PHPUnit\Framework\TestCase;

/**
 * {{DESCRIPTION}}
 *
 * @group regression
 * @regression {{ERROR_ID}}
 */
class {{CLASS_NAME}}Test extends TestCase
{
    public function test_{{METHOD_NAME}}_does_not_reproduce_{{ERROR_ID}}(): void
    {
        // Arrange
        {{ARRANGE}}

        // Act
        {{ACT}}

        // Assert
        {{ASSERT}}
    }
}
EOF
    fi
}

# Get functional test template content
get_functional_test_template() {
    local template_file="$RECETTE_TEMPLATES_DIR/functional-test.php.template"

    if [[ -f "$template_file" ]]; then
        cat "$template_file"
    else
        cat << 'EOF'
<?php

declare(strict_types=1);

namespace Tests\Functional\Regression;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

/**
 * {{DESCRIPTION}}
 *
 * @group regression
 * @regression {{ERROR_ID}}
 */
class {{CLASS_NAME}}Test extends WebTestCase
{
    public function test_endpoint_does_not_reproduce_{{ERROR_ID}}(): void
    {
        $client = static::createClient();

        // {{ENDPOINT}} {{METHOD}}
        $client->request('{{METHOD}}', '{{ENDPOINT}}');

        $this->assertResponseIsSuccessful();
    }
}
EOF
    fi
}

# Get Behat feature template content
get_behat_feature_template() {
    local template_file="$RECETTE_TEMPLATES_DIR/feature.feature.template"

    if [[ -f "$template_file" ]]; then
        cat "$template_file"
    else
        cat << 'EOF'
@regression @{{ERROR_ID}}
Feature: Regression test for {{ERROR_ID}}
  As a user
  I want to ensure the bug {{ERROR_ID}} does not reappear
  So that the application remains stable

  # {{DESCRIPTION}}

  Scenario: Verify {{ERROR_ID}} does not reproduce
    Given I am on "{{URL}}"
    When I {{ACTION}}
    Then I should not see an error message
EOF
    fi
}

# =============================================================================
# Batch Test Generation
# =============================================================================

# Generate tests for all errors in a session
generate_tests_for_session() {
    local session_id="$1"

    local session_dir="${RECETTE_BASE:-.recette}/sessions/$session_id"
    local state_file="$session_dir/state.yaml"

    if [[ ! -f "$state_file" ]]; then
        echo "Session not found: $session_id"
        return 1
    fi

    if ! command -v yq &>/dev/null; then
        echo "yq required for test generation"
        return 1
    fi

    # Get all errors from session
    local error_count
    error_count=$(yq e '.errors_detected | length' "$state_file")

    local generated=0

    for ((i=0; i<error_count; i++)); do
        local error
        error=$(yq e ".errors_detected[$i]" "$state_file" -o json)

        local error_id error_type description
        error_id=$(echo "$error" | jq -r '.id')
        error_type=$(echo "$error" | jq -r '.type')
        description=$(echo "$error" | jq -r '.description')

        # Generate tests
        local tests
        tests=$(generate_tests_from_error "$error_id" "$error_type" "$description")

        # Update session state with generated test paths
        while IFS= read -r test_line; do
            local type="${test_line%%:*}"
            local path="${test_line#*:}"

            case "$type" in
                unit)
                    yq e -i "(.errors_detected[$i]).generated_tests.unit = \"$path\"" "$state_file"
                    ;;
                functional)
                    yq e -i "(.errors_detected[$i]).generated_tests.functional = \"$path\"" "$state_file"
                    ;;
                behat)
                    yq e -i "(.errors_detected[$i]).generated_tests.behat = \"$path\"" "$state_file"
                    ;;
            esac

            # Register in regression suite
            register_regression_test "$error_id" "$path" "$type"

            generated=$((generated + 1))
        done <<< "$tests"
    done

    echo "Generated $generated tests for $error_count errors"
}

# =============================================================================
# Export
# =============================================================================

export RECETTE_TEMPLATES_DIR
export RECETTE_GENERATED_TESTS_DIR
