# trigger_loop.py
import asyncio
import threading
import time
from pathlib import Path

from Core import settings as config
from Core.Logger import Logs
from Server.capture_service import capture_with_replay_sync, replay_timing_profile


sanad_logger = Logs()
sanad_logger.LogEngine("G1_Logs", "trigger_loop")

async def _play_prompt(voice, prompt_key: str, fallback_text: str) -> bool:
    try:
        return bool(await voice.play_prompt_key(prompt_key, fallback_text))
    except Exception as e:
        sanad_logger.print_and_log(f"⚠️ Prompt playback failed for {prompt_key}: {e}", message_type="warning")
        return False


async def _play_capture_prompt(voice, prompt_key: str, fallback_text: str) -> bool:
    try:
        return bool(
            await voice.play_prompt_key(
                prompt_key,
                fallback_text="",
                allow_gemini_fallback=False,
                mode_override="audio",
            )
        )
    except Exception as e:
        sanad_logger.print_and_log(f"⚠️ Capture prompt playback failed for {prompt_key}: {e}", message_type="warning")
        return False


async def _play_manual_capture_prompt_sequence(
    voice,
    shot_delay_sec: float,
    cancel_event: threading.Event,
):
    start_ts = time.time()
    await _play_capture_prompt(
        voice,
        "countdown_intro",
        "Look at the camera, stay ready, hold your pose with me, keep still, keep your smile soft, and in a moment I will count down for the photo.",
    )
    announced = set()

    while True:
        if cancel_event.is_set():
            raise asyncio.CancelledError

        remaining = max(0.0, shot_delay_sec - (time.time() - start_ts))
        sec_left = int(round(remaining))

        if sec_left in (3, 2, 1) and sec_left not in announced:
            announced.add(sec_left)
            await _play_capture_prompt(voice, f"count_{sec_left}", f"{sec_left}...")
        elif sec_left <= 0 and 0 not in announced:
            announced.add(0)
            await _play_capture_prompt(voice, "smile", "Smile.")
            return

        await asyncio.sleep(0.05)


async def trigger_loop(
    hub,
    replay,
    voice,
    take_photo_sync_callable=None,
    ws=None,
):
    """
    R2+X:
      - play the fixed capture prompt sequence (recorded audio by default)
      - run unified capture pipeline (replay + timed capture/upload)
    R2+L1:
      - cancels pending delayed photo (replay cancel handled in replay engine)
    Debounce:
      - must release R2+X to re-trigger
      - blocks retrigger for PHOTO_TOTAL_SEC window
    """
    r2x_prev = False
    waiting_release = False
    photo_busy_until = 0.0

    pending_photo_task: asyncio.Task | None = None
    pending_audio_task: asyncio.Task | None = None
    pending_capture_cancel: threading.Event | None = None

    def _hard_cancel_pressed() -> bool:
        if hasattr(hub, "hard_cancel_combo"):
            return bool(hub.hard_cancel_combo())
        return bool(hub.combo_r2l1())

    async def delayed_photo(delay_sec: float, cancel_event: threading.Event):
        # Unified pipeline: replay + trigger/fallback capture + upload touch.
        base_prefix = None  # keep existing default naming behavior
        photo_result = await asyncio.to_thread(
            capture_with_replay_sync,
            replay_runner=replay,
            replay_file=config.REPLAY_FILE,
            home_file=config.HOME_FILE,
            delay_sec=delay_sec,
            prefix=base_prefix,
            speed=1.0,
            cancel_event=cancel_event,
        )
        sanad_logger.print_and_log(f"📸 Capture pipeline result: {photo_result}", message_type="info")
        if cancel_event.is_set():
            return
        if isinstance(photo_result, str) and not photo_result.startswith("[ERR]"):
            await _play_capture_prompt(
                voice,
                "photo_saved_thanks",
                "Thank you. Photo saved. Do not forget to check your photos.",
            )

    while True:
        await asyncio.sleep(0.02)
        if not hub.first_state:
            continue

        # Cancel combo: cancel pending photo
        if _hard_cancel_pressed():
            if pending_capture_cancel is not None:
                pending_capture_cancel.set()
            if pending_photo_task and not pending_photo_task.done():
                pending_photo_task.cancel()
            if pending_audio_task and not pending_audio_task.done():
                pending_audio_task.cancel()
            sanad_logger.print_and_log("🛑 HARD CANCEL: pending photo cancelled (R2+L1).", message_type="warning")

        r2x_now = hub.combo_r2x()

        # Require release before next trigger
        if waiting_release:
            if not r2x_now:
                waiting_release = False
            r2x_prev = r2x_now
            continue

        # Do not retrigger while replay is playing
        if replay.is_playing:
            r2x_prev = r2x_now
            continue

        now = time.time()

        # Rising edge trigger + not inside busy window
        if r2x_now and (not r2x_prev) and (now >= photo_busy_until):
            waiting_release = True

            # 1) schedule manual countdown speech using the same prompt keys as AI mode
            if pending_photo_task and not pending_photo_task.done():
                if pending_capture_cancel is not None:
                    pending_capture_cancel.set()
                pending_photo_task.cancel()
            if pending_audio_task and not pending_audio_task.done():
                pending_audio_task.cancel()

            timing = replay_timing_profile(config.REPLAY_FILE)
            delay = float(timing.get("capture_offset_sec") or max(0.0, min(config.PHOTO_DELAY_SEC, config.PHOTO_TOTAL_SEC)))
            replay_duration = float(timing.get("duration_sec") or 0.0)
            photo_busy_until = now + max(config.PHOTO_TOTAL_SEC, replay_duration + 3.2, delay + config.PHOTO_THANKS_SEC)
            sanad_logger.print_and_log(
                "🎯 Manual capture timing: "
                f"replay={Path(config.REPLAY_FILE).name} "
                f"duration={replay_duration:.3f}s "
                f"shot_at={delay:.3f}s "
                f"source={timing.get('capture_source', 'config_fallback')}",
                message_type="info",
            )
            pending_capture_cancel = threading.Event()
            pending_audio_task = asyncio.create_task(
                _play_manual_capture_prompt_sequence(voice, delay, pending_capture_cancel)
            )
            pending_photo_task = asyncio.create_task(delayed_photo(delay, pending_capture_cancel))

        r2x_prev = r2x_now
