import asyncio
import random

from actions.media_sender import (
    MediaSender
)

from simulator.simulator_manager import (
    simulator_manager
)

from simulator.text_pool import (
    text_pool
)

from simulator.caption_pool import (
    caption_pool
)

from simulator.micro_offline_engine import (
    micro_offline_engine
)

from core.delay_engine import (
    delay_engine
)


class SafeAsyncLooper:

    def __init__(self):

        self._running = True

        # error backoff
        self._error_backoff = 30
        self._max_backoff = 600

        # pacing delay
        self._cycle_delay_range = (120, 300)

    # ---------- STOP ----------
    def stop(self):
        self._running = False

    # ---------- ENSURE ONLINE ----------
    async def _ensure_online(self, phone, app):

        while not app.is_connected:

            print(
                f"[{phone}] "
                f"Waiting circadian wake..."
            )

            await asyncio.sleep(60)

        micro_offline_engine.mark_online(
            phone
        )

    # ---------- LOOP ----------
    async def run_session_loop(
        self,
        phone,
        app
    ):

        media = MediaSender(app, phone)

        target_chat = "@telegram"

        while self._running:

            try:

                # -------------------------
                # WAIT ONLINE
                # -------------------------
                await self._ensure_online(
                    phone,
                    app,
                )

                # -------------------------
                # MICRO OFFLINE CHECK
                # -------------------------
                await micro_offline_engine.execute(
                    phone,
                    app
                )

                # -------------------------
                # GLOBAL DELAY
                # -------------------------
                await asyncio.sleep(
                    random.uniform(
                        *self._cycle_delay_range
                    )
                )

                # -------------------------
                # ONLINE PULSE
                # -------------------------
                await simulator_manager.online_pulse(
                    app
                )

                # -------------------------
                # TEXT MESSAGE
                # -------------------------
                text = text_pool.get()

                await simulator_manager.typing(
                    app,
                    target_chat,
                    duration_range=(4, 9)
                )

                await app.send_message(
                    target_chat,
                    text,
                    disable_notification=True
                )

                await delay_engine.wait()

                # -------------------------
                # MEDIA
                # -------------------------
                caption = caption_pool.get()

                await simulator_manager.typing(
                    app,
                    target_chat,
                    duration_range=(5, 11)
                )

                await media.send(
                    chat_id=target_chat,
                    file_path="media/sample.jpg",
                    media_type="photo",
                    caption=caption,
                    silent=True
                )

                # reset backoff
                self._error_backoff = 30

            except Exception as e:

                print(
                    f"[{phone}] Loop Error → {e}"
                )

                await asyncio.sleep(
                    self._error_backoff
                )

                self._error_backoff = min(
                    self._error_backoff * 2,
                    self._max_backoff
                )

    # ---------- RUN ALL ----------
    async def run_all(self, clients):

        tasks = []

        for phone, app in clients:

            tasks.append(
                asyncio.create_task(
                    self.run_session_loop(
                        phone,
                        app
                    )
                )
            )

        await asyncio.gather(*tasks)
