"""Job executors — bridge between scheduler jobs and existing tool internals.

Each executor wraps an existing tool's `run_*()` function, passing the appropriate
parameters from the job record. This ensures all existing logic (validation, rate
limits, message generation, etc.) is reused — the scheduler just decides WHEN to run.

All executors return the string result from the tool function. Errors are raised
as exceptions for the engine to handle (retry/fail).
"""

from __future__ import annotations

import logging
from typing import Any

from ..constants import (
    JOB_CHECK_REPLIES,
    JOB_ENGAGE,
    JOB_FOLLOWUP,
    JOB_INVITE,
)

logger = logging.getLogger(__name__)


async def execute_job(job: dict[str, Any]) -> str:
    """Route a scheduler job to the appropriate executor.

    Args:
        job: Full job record from scheduler_jobs table.

    Returns:
        String result from the tool execution.

    Raises:
        ValueError: Unknown job type.
        Exception: Any error from the tool execution.
    """
    job_type = job["job_type"]
    executors = {
        JOB_INVITE: _execute_invite,
        JOB_FOLLOWUP: _execute_followup,
        JOB_ENGAGE: _execute_engagement,
        JOB_CHECK_REPLIES: _execute_check_replies,
    }

    executor = executors.get(job_type)
    if not executor:
        raise ValueError(f"Unknown job type: {job_type}")

    return await executor(job)


async def _execute_invite(job: dict[str, Any]) -> str:
    """Execute an invitation job.

    Calls run_generate_and_send() with mode="autopilot" for the campaign.
    The tool internally picks the next pending prospect by fit_score.
    """
    from ..tools.generate_send import run_generate_and_send

    campaign_id = job["campaign_id"]
    logger.info("Scheduler: sending invitation for campaign %s", campaign_id)

    result = await run_generate_and_send(
        campaign_id=campaign_id,
        mode="autopilot",
    )

    # Check if the result indicates an error (tools return error strings)
    if result and ("❌" in result or "failed" in result.lower()):
        raise RuntimeError(f"Invitation failed: {result[:200]}")

    logger.info("Scheduler: invitation result for campaign %s: %s", campaign_id, result[:100])
    return result


async def _execute_followup(job: dict[str, Any]) -> str:
    """Execute a follow-up message job.

    Calls run_send_followup() with mode="autopilot" for a specific outreach.
    If outreach_id is set, it targets that specific outreach.
    """
    from ..tools.send_followup import run_send_followup

    campaign_id = job["campaign_id"]
    outreach_id = job.get("outreach_id", "")
    logger.info("Scheduler: sending follow-up for outreach %s", outreach_id or "next")

    result = await run_send_followup(
        campaign_id=campaign_id,
        outreach_id=outreach_id or "",
        mode="autopilot",
    )

    if result and ("❌" in result or "failed" in result.lower()):
        raise RuntimeError(f"Follow-up failed: {result[:200]}")

    logger.info("Scheduler: follow-up result: %s", result[:100])
    return result


async def _execute_engagement(job: dict[str, Any]) -> str:
    """Execute an engagement warm-up job.

    Calls run_engage_prospect() with mode="autopilot" for a specific outreach.
    Action defaults to "auto" (comment if post has text, react otherwise).
    """
    from ..tools.engage_prospect import run_engage_prospect

    campaign_id = job["campaign_id"]
    outreach_id = job.get("outreach_id", "")
    logger.info("Scheduler: engaging prospect for outreach %s", outreach_id or "next")

    result = await run_engage_prospect(
        campaign_id=campaign_id,
        outreach_id=outreach_id or "",
        action="auto",
        mode="autopilot",
    )

    if result and ("❌" in result or "failed" in result.lower()):
        raise RuntimeError(f"Engagement failed: {result[:200]}")

    logger.info("Scheduler: engagement result: %s", result[:100])
    return result


async def _execute_check_replies(job: dict[str, Any]) -> str:
    """Execute a reply check job.

    Calls run_check_replies() which checks all active campaigns.
    """
    from ..tools.check_replies import run_check_replies

    logger.info("Scheduler: checking replies")

    result = await run_check_replies()

    # Reply checks don't typically fail hard — just log
    logger.info("Scheduler: reply check result: %s", result[:100] if result else "empty")
    return result or "No new replies"
