import logging
import os
import sys
import time
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent.parent))  # run from repo root


from azure.communication.messages.aio import NotificationMessagesClient
from dotenv import load_dotenv

from whatsapp import Bot
from whatsapp.bindings.azure import AzureBindingClient
from whatsapp.messages import TextMessage
from whatsapp.messages.dialogs import Dialog, DialogContext, DialogTurnResult, DialogTurnStatus

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

load_dotenv()

STACK_DEPTH = 50

# mute azure logs
logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel(logging.WARNING)
logging.getLogger("aiohttp.access").setLevel(logging.WARNING)


@Dialog.set(name="StressTestRoot", main=True)
class StressTestRoot(Dialog):
    @Dialog.step()
    async def start(self, dc: DialogContext):
        dc.set_value("root_secret", "I am at the bottom of the stack")

        await dc.message.send(
            TextMessage(dc.ctx, f"Starting Stack Stress Test...\nBuilding stack depth: {STACK_DEPTH}")
        )

        # begin the recursive chain
        return await dc.begin_dialog("RecursiveLayer", {"current_depth": 1, "target_depth": STACK_DEPTH})


@Dialog.set(name="RecursiveLayer")
class RecursiveLayer(Dialog):
    @Dialog.step()
    async def process_layer(self, dc: DialogContext):
        options = dc.active_dialog.state.get("options", {})
        current = options.get("current_depth", 1)
        target = options.get("target_depth", 10)

        # add size to the state
        dc.set_value(f"layer_data_{current}", "x" * 50)

        if current < target:
            # push next layer
            return await dc.begin_dialog("RecursiveLayer", {"current_depth": current + 1, "target_depth": target})

        return await self.run_benchmark(dc)

    async def run_benchmark(self, dc: DialogContext):
        # 1. O(1) Lookup: value in current active dialog
        dc.set_value("top_secret", "I am at the top of the stack")

        start = time.perf_counter_ns()
        _ = dc.get_value("top_secret", active_dialog=True)
        end = time.perf_counter_ns()
        t_o1 = end - start

        # 2. O(n) Lookup: value in root dialog (bottom of stack)
        start = time.perf_counter_ns()
        _ = dc.get_value("root_secret")  # must traverse entire stack
        end = time.perf_counter_ns()
        t_on = end - start

        # 3. Session Retrieval Time (from middleware/binding)
        t_session = getattr(dc.ctx, "_retrieval_time_ns", 0)

        report = (
            f"# *Stress Test Results*\n"
            f"Stack Depth: {len(dc.stack)}\n\n"
            f"*O(1) Local Lookup:* {t_o1} ns\n"
            f"*O(n) Deep Lookup:* {t_on} ns\n"
            f"*Session Retrieval:* {t_session} ns\n"
            f"Ratio (On/O1): {t_on / t_o1:.2f}x"
        )

        await dc.message.send(TextMessage(dc.ctx, report))
        return DialogTurnResult(DialogTurnStatus.Waiting)


# Create Bot instance
bot = Bot(port=6969)  # used for my local cloudflared tunnel, don't mind the funny number

# Load Dialogs
bot.load_dialog(StressTestRoot)
bot.load_dialog(RecursiveLayer)

# Initialize Azure client
endpoint = os.getenv("AZURE_COMMUNICATION_ENDPOINT")
notification_client = NotificationMessagesClient.from_connection_string(endpoint)

# Create Azure binding
azure_binding = AzureBindingClient(
    ctx=bot,
    client=notification_client,
    route="/api/whatsapp/events",
)

# Pass Bot to Binding and Run
bot.run(azure_binding)
