{%- import "util.include.jinja" as util -%}
# pylint: disable=missing-function-docstring, wrong-import-position, import-error, no-name-in-module, import-outside-toplevel, no-member, redefined-outer-name, unused-argument, unused-variable, invalid-name, redefined-outer-name, missing-class-docstring

import asyncio
import logging
import os
import sys
import json
import tempfile
import pytest
import pytest_asyncio
from azure.servicebus.aio import ServiceBusClient
from azure.servicebus import ServiceBusReceivedMessage
from testcontainers.core.container import DockerContainer
from testcontainers.core.network import Network
from testcontainers.core.waiting_utils import wait_for_logs

sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../{{data_project_name}}/src')))
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../{{data_project_name}}/tests')))
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../{{main_project_name}}/src')))

{%- set imports = [] %}
{%- set test_imports = [] %}
{%- for messagegroupid, messagegroup in root.messagegroups.items() %}
{%- set groupname = messagegroupid | pascal %}
{%- set class_name = ( groupname | strip_dots ) + "EventProducer" %}
from {{main_project_name}}.producer import {{class_name}}
{%- for messageid, message in messagegroup.messages.items() %}
{%- set type_name = util.DeclareDataType( data_project_name, root, message ) %}
{%- if type_name != "object" %}
{%- set import_statement = "from " + (data_project_name | dotunderscore | lower) + " import " + (type_name | pascal | strip_namespace) %}
{%- if import_statement not in imports %}
{%- set _ = imports.append(import_statement) %}
{{ import_statement }}
{%- endif %}
{%- set test_class_name = "Test_" + (type_name | pascal | strip_namespace) %}
{%- set test_module_name = "test_" + (type_name | dotunderscore | lower) %}
{%- set test_import_statement = "from " + test_module_name + " import " + test_class_name %}
{%- if test_import_statement not in test_imports %}
{%- set _ = test_imports.append(test_import_statement) %}
{{ test_import_statement }}
{%- endif %}
{%- endif %}
{%- endfor %}
{%- endfor %}

@pytest_asyncio.fixture(scope="module")
async def service_bus_emulator():
    """Set up Service Bus emulator with SQL Edge"""
    network = Network()
    network.create()
    
    # Service Bus emulator configuration
    config_content = {
        "UserConfig": {
            "Namespaces": [
                {
                    "Name": "sbemulatorns",
                    "Queues": [
                        {
                            "Name": "myqueue",
                            "Properties": {
                                "DeadLetteringOnMessageExpiration": False,
                                "DefaultMessageTimeToLive": "PT1H",
                                "LockDuration": "PT1M",
                                "MaxDeliveryCount": 10,
                                "RequiresDuplicateDetection": False,
                                "RequiresSession": False
                            }
                        }
                    ]
                }
            ],
            "Logging": {
                "Type": "File"
            }
        }
    }
    
    config_file = tempfile.NamedTemporaryFile(delete=False, mode='w', suffix='.json')
    json.dump(config_content, config_file)
    config_file.flush()
    config_file.close()
    config_file_path = config_file.name
    os.chmod(config_file_path, 0o666)
    
    try:
        # Start SQL Edge container
        sql_edge_container = DockerContainer("mcr.microsoft.com/azure-sql-edge:latest") \
            .with_env("ACCEPT_EULA", "Y") \
            .with_env("MSSQL_SA_PASSWORD", "StrongPassword!1") \
            .with_network(network) \
            .with_network_aliases("sqledge")
        
        sql_edge_container.start()
        logging.info("SQL Edge container started")
        await asyncio.sleep(10)  # Wait for SQL Edge to initialize
        
        # Start Service Bus emulator container
        sb_emulator_container = DockerContainer("mcr.microsoft.com/azure-messaging/servicebus-emulator:latest") \
            .with_volume_mapping(config_file_path, "/ServiceBus_Emulator/ConfigFiles/Config.json") \
            .with_bind_ports(5672, 5672) \
            .with_network(network) \
            .with_network_aliases("sb-emulator") \
            .with_env("SQL_SERVER", "sqledge") \
            .with_env("MSSQL_SA_PASSWORD", "StrongPassword!1") \
            .with_env("ACCEPT_EULA", "Y")
        
        sb_emulator_container.start()
        logging.info("Service Bus emulator container started")
        
        try:
            wait_for_logs(sb_emulator_container, ".*Emulator Service is Successfully Up!", timeout=60)
        except TimeoutError as e:
            stdoutlog, stderrlog = sb_emulator_container.get_logs()
            logging.error("stdout: %s", stdoutlog)
            logging.error("stderr: %s", stderrlog)
            raise e
        
        # Extended wait for Service Bus emulator to fully initialize
        await asyncio.sleep(45)
        
        connection_string = "Endpoint=sb://localhost;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;"
        
        yield {
            "connection_string": connection_string,
            "queue_name": "myqueue",
            "network": network,
            "sql_edge_container": sql_edge_container,
            "sb_emulator_container": sb_emulator_container
        }
        
        # Cleanup
        sb_emulator_container.stop()
        sql_edge_container.stop()
        network.remove()
    finally:
        os.unlink(config_file_path)

{%- for messagegroupid, messagegroup in root.messagegroups.items() %}
{%- set groupname = messagegroupid | pascal %}
{%- set class_name = ( groupname | strip_dots ) + "EventProducer" %}
{%- for messageid, message in messagegroup.messages.items() if (message | exists("envelope","CloudEvents/1.0")) %}
{%- set messagename = messageid | pascal | strip_dots | strip_namespace %}
{%- set message_snake = messageid | dotunderscore | snake %}
{%- set test_function_name = "test_" + groupname | lower | replace(" ", "_") + "_" + messagename | lower | replace(" ", "_") %}
{%- set type_name = util.DeclareDataType( data_project_name, root, message ) %}

@pytest.mark.asyncio
async def {{ test_function_name | dotunderscore }}(service_bus_emulator):
    """Test the {{ messagename }} event from the {{ groupname }} message group"""
    
    connection_string = service_bus_emulator["connection_string"]
    queue_name = service_bus_emulator["queue_name"]
    
    # Create producer
    producer = {{class_name}}.from_connection_string(connection_string, queue_name)
    
    {%- if type_name != "object" %}
    # Create valid test data using the test helper
    test_data = Test_{{ type_name | pascal | strip_namespace }}.create_instance()
    {%- else %}
    test_data = None
    {%- endif %}
    
    # Send 5 messages to test message settlement and ordering
    for i in range(5):
        await producer.send_{{ message_snake.split('.')[-1] }}(
            {%- for attrname, attribute in message.envelopemetadata.items() if attribute.required and attribute.value is not defined %}
            _{{ attrname }}=f"test_{{ attrname }}_{i}",
            {%- endfor %}
            {%- for attrname, attribute in message.envelopemetadata.items() if attribute.type == "uritemplate" %}
            {%- for placeholder in attribute.value | regex_search('\\{([A-Za-z0-9_]+)\\}') %}
            _{{ placeholder | snake }}=f"test_{{ placeholder }}_{i}",
            {%- endfor %}
            {%- endfor %}
            data=test_data
        )
    
    # Verify all 5 messages were sent by receiving them
    servicebus_client = ServiceBusClient.from_connection_string(conn_str=connection_string, logging_enable=True)
    async with servicebus_client:
        receiver = servicebus_client.get_queue_receiver(queue_name=queue_name)
        async with receiver:
            received_msgs = await receiver.receive_messages(max_wait_time=10, max_message_count=5)
            assert len(received_msgs) == 5, f"Should receive exactly 5 messages, got {len(received_msgs)}"
            
            # Verify all messages and complete them
            for msg in received_msgs:
                # Check CloudEvent properties
                # For structured mode, CloudEvents are in the JSON body
                # For binary mode, CloudEvents are in application_properties
                import json
                # Try structured mode first (body contains CloudEvents)
                try:
                    body_data = json.loads(str(msg))
                    if "type" in body_data:
                        # Structured mode
                        assert body_data["type"] == "{{ messageid }}"
                    else:
                        # Binary mode - check application_properties
                        app_props = msg.application_properties or {}
                        assert "cloudEvents:type" in app_props or "cloudEvents_type" in app_props, f"CloudEvents type not found in application_properties: {app_props}"
                        ce_type = app_props.get("cloudEvents:type") or app_props.get("cloudEvents_type")
                        assert ce_type == "{{ messageid }}"
                except (json.JSONDecodeError, UnicodeDecodeError):
                    # Binary mode - check application_properties
                    app_props = msg.application_properties or {}
                    assert "cloudEvents:type" in app_props or "cloudEvents_type" in app_props, f"CloudEvents type not found in application_properties: {app_props}"
                    ce_type = app_props.get("cloudEvents:type") or app_props.get("cloudEvents_type")
                    assert ce_type == "{{ messageid }}"
                
                await receiver.complete_message(msg)

{% endfor %}
{% endfor %}
