{%- 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 datetime
import tempfile
import pytest
import pytest_asyncio
from typing import Optional, Dict
from azure.servicebus.aio import ServiceBusClient
from azure.servicebus import ServiceBusMessage
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 ) + "MessageDispatcher" %}
from {{main_project_name}}.dispatcher 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 dispatcher_class_name = ( groupname | strip_dots ) + "MessageDispatcher" %}
{%- for messageid, message in messagegroup.messages.items() %}
{%- set messagename = messageid | pascal | strip_dots | strip_namespace %}
{%- 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"]
    
    {%- if type_name != "object" %}
    # Create valid test data using the test helper
    event_data = Test_{{ type_name | pascal | strip_namespace }}.create_instance()
    {%- else %}
    event_data = {}
    {%- endif %}
    
    # Serialize event data to dict with proper nested object/enum handling
    # dataclasses-json's to_json() properly serializes nested objects and enums,
    # so we convert to JSON and back to get a pure dict
    if event_data:
        if hasattr(event_data, 'to_json'):
            # dataclasses-json objects: serialize to JSON then parse back to dict
            json_str = event_data.to_json()
            serialized_data = json.loads(json_str)
        elif isinstance(event_data, dict):
            serialized_data = event_data
        else:
            serialized_data = {}
    else:
        serialized_data = {}
    
    # Create CloudEvent with required attributes
    attributes = {
        "specversion": "1.0",
        "type": "{{ messageid }}",
        "source": "test",
        {%- for attrname, attribute in message.envelopemetadata.items() if attribute.required and attribute.value is not defined %}
        "{{ attrname }}": {{ "'test'" if not attrname == 'time' else 'datetime.datetime.now().isoformat()' }},
        {%- endfor %}
        "datacontenttype": "application/json"
    }
    
    # Create CloudEvent and send via Service Bus
    from cloudevents.http import CloudEvent
    from cloudevents.conversion import to_structured
    cloud_event = CloudEvent(attributes, serialized_data)
    
    # Convert to structured mode for Service Bus
    headers, body = to_structured(cloud_event)
    
    # Send message via Service Bus
    async with ServiceBusClient.from_connection_string(connection_string) as client:
        sender = client.get_queue_sender(queue_name)
        async with sender:
            message = ServiceBusMessage(body=body)
            if "Content-Type" in headers:
                message.content_type = headers["Content-Type"]
            # Add CloudEvents headers as application properties
            if message.application_properties is None:
                message.application_properties = {}
            for key, value in headers.items():
                if key.lower() != "content-type":
                    message.application_properties[key] = value
            
            await sender.send_messages(message)
    
    # Give dispatcher time to process
    await asyncio.sleep(2)
    
    # Verify the message was processed (dispatcher should have handled it)
    # This is a basic test - in real scenarios you'd verify via callback or state
    assert True  # If we got here without exceptions, test passed

{%- endfor %}
{%- endfor %}
