{%- import "cloudevents.jinja.include" as cloudEvents -%}
{%- import "util.jinja.include" as util -%}
{%- set data_module_name = data_project_name | strip_invalid_identifier_characters %}
// This code was generated by the xRegistry tool.
// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.

import { ServiceBusClient, ServiceBusReceiver } from '@azure/service-bus';
import { GenericContainer, StartedTestContainer, Network, StartedNetwork, Wait } from 'testcontainers';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
{%- set messagegroups = root.messagegroups %}
{%- set uses_cloudevents_message = cloudEvents.usesCloudEvents(root) %}

import { {% for messagegroupid, messagegroup in messagegroups.items() -%}{%- set class_name = (messagegroupid | pascal | strip_namespace) + "Producer" %}{{ class_name }}{% if not loop.last %}, {% endif %}{% endfor %} } from '../src';
{%- if uses_cloudevents_message %}
import { CloudEvent } from 'cloudevents';
{%- endif %}

// Import data types
import * as {{ data_module_name }} from '../../{{ data_project_name }}/dist/index.js';

const QUEUE_NAME = 'myqueue';

// Increased timeout for container startup and tests
jest.setTimeout(360000); // 6 minutes

// Service Bus emulator tests use log-based wait strategy to detect when the emulator
// outputs "Emulator Service is Successfully Up!" instead of relying on port binding.
// This should work reliably in CI environments. Set SKIP_SERVICEBUS_TESTS=true to skip if needed.
const SKIP_SERVICEBUS_TESTS = process.env.SKIP_SERVICEBUS_TESTS === 'true';
const describeTest = SKIP_SERVICEBUS_TESTS ? describe.skip : describe;

describeTest('{{ project_name | strip_dots | pascal }} Service Bus Producer Tests', () => {
    let sqlEdgeContainer: StartedTestContainer;
    let serviceBusEmulatorContainer: StartedTestContainer;
    let network: StartedNetwork;
    let serviceBusConnectionString: string;
    let configFilePath: string;
    
    beforeAll(async () => {
        // Create network for containers to communicate
        network = await new Network().start();
        
        // Create Service Bus emulator configuration
        const emulatorConfig = {
            UserConfig: {
                Namespaces: [
                    {
                        Name: 'sbemulatorns',
                        Queues: [
                            {
                                Name: QUEUE_NAME,
                                Properties: {
                                    DeadLetteringOnMessageExpiration: false,
                                    DefaultMessageTimeToLive: 'PT1H',
                                    LockDuration: 'PT1M',
                                    MaxDeliveryCount: 10,
                                    RequiresDuplicateDetection: false,
                                    RequiresSession: false
                                }
                            }
                        ]
                    }
                ],
                Logging: {
                    Type: 'File'
                }
            }
        };
        
        // Write config to temporary file
        configFilePath = path.join(os.tmpdir(), `sb-emulator-config-${Date.now()}.json`);
        fs.writeFileSync(configFilePath, JSON.stringify(emulatorConfig, null, 2));
        
        console.log('Starting SQL Edge container...');
        // Start SQL Edge container (required by Service Bus emulator)
        sqlEdgeContainer = await new GenericContainer('mcr.microsoft.com/azure-sql-edge:latest')
            .withNetwork(network)
            .withNetworkAliases('sqledge')
            .withEnvironment({
                'ACCEPT_EULA': 'Y',
                'MSSQL_SA_PASSWORD': 'StrongPassword!1'
            })
            .start();
        
        console.log('SQL Edge container started');
        
        // Wait for SQL Edge to initialize
        await new Promise(resolve => setTimeout(resolve, 15000));
        
        console.log('Starting Service Bus emulator container...');
        // Start Service Bus emulator container using log-based wait strategy
        // Wait for the specific log message indicating the emulator is ready
        serviceBusEmulatorContainer = await new GenericContainer('mcr.microsoft.com/azure-messaging/servicebus-emulator:latest')
            .withNetwork(network)
            .withNetworkAliases('sb-emulator')
            .withBindMounts([{
                source: configFilePath,
                target: '/ServiceBus_Emulator/ConfigFiles/Config.json',
                mode: 'ro'
            }])
            .withExposedPorts(5672)
            .withEnvironment({
                'SQL_SERVER': 'sqledge',
                'MSSQL_SA_PASSWORD': 'StrongPassword!1',
                'ACCEPT_EULA': 'Y'
            })
            .withWaitStrategy(Wait.forLogMessage(/.*Emulator Service is Successfully Up!.*/))
            .withStartupTimeout(300000)  // 5 minutes for full initialization
            .start();
        
        console.log('Service Bus emulator container started');
        
        // Get the mapped port for Service Bus
        const sbPort = serviceBusEmulatorContainer.getMappedPort(5672);
        console.log(`Service Bus emulator mapped to port ${sbPort}`);
        
        // Brief pause to ensure emulator is fully accepting connections after log message
        await new Promise(resolve => setTimeout(resolve, 15000));
        
        console.log('Service Bus emulator should be ready');
        
        serviceBusConnectionString = `Endpoint=sb://localhost:${sbPort};SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;`;
        
        console.log('Test environment ready');
    }, 360000);  // 6 minutes - must be longer than container startup timeout
    
    afterAll(async () => {
        console.log('Cleaning up test environment...');
        
        if (serviceBusEmulatorContainer) {
            await serviceBusEmulatorContainer.stop();
        }
        
        if (sqlEdgeContainer) {
            await sqlEdgeContainer.stop();
        }
        
        if (network) {
            await network.stop();
        }
        
        // Clean up config file
        if (configFilePath && fs.existsSync(configFilePath)) {
            fs.unlinkSync(configFilePath);
        }
        
        console.log('Test environment cleaned up');
    }, 120000);
    
{%- for messagegroupid, messagegroup in messagegroups.items() %}
{%- set pascal_group_name = messagegroupid | pascal %}
{%- set class_name = (pascal_group_name | strip_namespace) + "Producer" %}
{%- set test_group_name = (pascal_group_name | strip_dots) %}
    
    describe('{{ test_group_name }} Message Group', () => {
{%- set message_list = messagegroup.messages.items() | list %}
{%- for messageid, message in message_list %}
{%- set messagename = messageid | strip_namespace | pascal %}
{%- set message_snake = messageid | snake %}
{%- set body_type = util.body_type(data_project_name, root, message) %}
{%- set has_schema = message.dataschemauri or message.dataschema %}
        
        test('should send {{ messagename }} message', async () => {
            console.log('Testing {{ messagename }} message...');
            
            const serviceBusClient = new ServiceBusClient(serviceBusConnectionString);
            const producer = new {{ class_name }}(serviceBusClient, QUEUE_NAME, 'binary');
            
            try {
                // Create test data using generated test helper
                {%- if has_schema %}
                const testData = {{ body_type }}.createInstance();
                {%- else %}
                const testData = {} as {{ body_type }};
                {%- endif %}
                
                // Send 5 messages to test proper message handling
                for (let i = 0; i < 5; i++) {
                    await producer.send{{ messagename }}(
                        testData
                        {%- for attrname, attribute in message.envelopemetadata.items() if attribute.type == "uritemplate" %}
                        {%- for placeholder in attribute.value | regex_search('\\{([A-Za-z0-9_]+)\\}') %},
                        `test_{{ placeholder }}_${i}`
                        {%- endfor %}
                        {%- endfor %}
                    );
                }
                
                console.log('{{ messagename }}: 5 messages sent successfully');
                
                // Verify messages were received
                const receiver = serviceBusClient.createReceiver(QUEUE_NAME);
                
                try {
                    const messages = await receiver.receiveMessages(5, { maxWaitTimeInMs: 10000 });
                    
                    expect(messages.length).toBe(5);
                    console.log(`{{ messagename }}: Received ${messages.length} messages`);
                    
                    // Complete all received messages
                    for (const msg of messages) {
                        {%- if uses_cloudevents_message %}
                        // Verify CloudEvents properties
                        const appProps = msg.applicationProperties || {};
                        const hasCloudEventsType = 'cloudEvents:type' in appProps || 
                                                  'cloudEvents_type' in appProps ||
                                                  'ce_type' in appProps;
                        expect(hasCloudEventsType).toBe(true);
                        {%- endif %}
                        
                        await receiver.completeMessage(msg);
                    }
                    
                    console.log('{{ messagename }}: All messages completed');
                } finally {
                    await receiver.close();
                }
            } catch (error) {
                console.error(`{{ messagename }} test error:`, error);
                if (error instanceof Error) {
                    console.error(`Error message: ${error.message}`);
                    console.error(`Error stack: ${error.stack}`);
                }
                throw error;
            } finally {
                await producer.close();
                await serviceBusClient.close();
            }
        }, 60000);
{%- endfor %}
    });
{%- endfor %}
});
