# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
"""Devin reminders: workflow dispatch for scheduling and cancelling agent reminders.

This module provides the core logic for the `set_devin_reminder` and
`cancel_devin_reminder` MCP tools. It dispatches the `devin-reminders.yml`
workflow with `action=put` or `action=cancel`, which manages reminders
stored as GitHub Actions artifacts and posts notifications to
`#devin-reminders` in Slack.

The reminder fires on the next cron cycle (every 30 minutes) after the
scheduled time, injecting a message back into the originating Devin session
via the `devin-reminders-action`.
"""

from __future__ import annotations

import json
import logging
from datetime import datetime, timedelta, timezone

from airbyte_ops_mcp.github_actions import (
    WorkflowDispatchResult,
    trigger_workflow_dispatch,
)
from airbyte_ops_mcp.github_api import resolve_ci_trigger_github_token

logger = logging.getLogger(__name__)

REMINDERS_REPO_OWNER = "airbytehq"
REMINDERS_REPO_NAME = "airbyte-ops-mcp"
REMINDERS_WORKFLOW_FILE = "devin-reminders.yml"
REMINDERS_DEFAULT_BRANCH = "main"


MAX_DELAY_MINUTES = 3 * 24 * 60
CRON_INTERVAL_MINUTES = 30


def validate_delay_minutes(delay_minutes: int) -> None:
    """Validate that delay_minutes is a supported 30-minute interval.

    Args:
        delay_minutes: Number of minutes until the reminder fires.

    Raises:
        ValueError: If delay_minutes is not a positive multiple of 30,
            or exceeds the 3-day maximum.
    """
    if delay_minutes <= 0:
        raise ValueError(f"delay_minutes must be positive, got {delay_minutes}")
    if delay_minutes % CRON_INTERVAL_MINUTES != 0:
        raise ValueError(
            f"delay_minutes must be a multiple of {CRON_INTERVAL_MINUTES}, "
            f"got {delay_minutes}"
        )
    if delay_minutes > MAX_DELAY_MINUTES:
        raise ValueError(
            f"delay_minutes must be at most {MAX_DELAY_MINUTES} (3 days), "
            f"got {delay_minutes}"
        )


def compute_remind_at(delay_minutes: int) -> str:
    """Compute an ISO 8601 remind_at timestamp from a delay in minutes.

    Args:
        delay_minutes: Number of minutes from now until the reminder fires.

    Returns:
        ISO 8601 timestamp string with timezone offset
        (e.g. `2026-02-20T17:00:00+00:00`).
    """
    now = datetime.now(tz=timezone.utc)
    fire_time = now + timedelta(minutes=delay_minutes)
    return fire_time.isoformat()


def dispatch_reminder(
    delay_minutes: int,
    reminder_message: str,
    agent_session_url: str,
    slack_users_cc: str | None = None,
) -> WorkflowDispatchResult:
    """Dispatch a Devin reminder via the devin-reminders-command workflow.

    Triggers the `devin-reminders.yml` workflow with `action=put`.
    The workflow stores the reminder as a GitHub Actions artifact and posts
    a confirmation to `#devin-reminders`.

    Args:
        delay_minutes: Minutes until the reminder fires. Must be a positive
            multiple of 30, up to 4320 (3 days).
        reminder_message: The reminder message to deliver.
        agent_session_url: Devin session URL to inject the reminder into.
        slack_users_cc: Optional comma-delimited Slack user tags to CC.

    Returns:
        WorkflowDispatchResult with workflow URL and optionally run ID/URL.

    Raises:
        ValueError: If delay_minutes is invalid or no GitHub token is found.
        requests.HTTPError: If the workflow dispatch API call fails.
    """
    validate_delay_minutes(delay_minutes)
    token = resolve_ci_trigger_github_token()

    remind_at = compute_remind_at(delay_minutes)

    workflow_inputs: dict[str, str] = {
        "action": "put",
        "reminder_message": reminder_message,
        "remind_at": remind_at,
        "agent_session_url": agent_session_url,
    }

    if slack_users_cc:
        workflow_inputs["slack_users_cc"] = slack_users_cc

    return trigger_workflow_dispatch(
        owner=REMINDERS_REPO_OWNER,
        repo=REMINDERS_REPO_NAME,
        workflow_file=REMINDERS_WORKFLOW_FILE,
        ref=REMINDERS_DEFAULT_BRANCH,
        inputs=workflow_inputs,
        token=token,
    )


def dispatch_cancel_reminder(
    agent_session_url: str,
    cancel_guids: list[str],
) -> WorkflowDispatchResult:
    """Cancel pending Devin reminders via the devin-reminders workflow.

    Triggers the `devin-reminders.yml` workflow with `action=cancel`.
    The workflow removes matching reminders from the GitHub Actions artifact
    and posts a confirmation to `#devin-reminders`.

    Both `agent_session_url` and `cancel_guids` are required. The action
    filters by both the session URL and the provided GUIDs.

    Args:
        agent_session_url: Devin session URL that owns the reminders.
        cancel_guids: List of specific reminder GUIDs to cancel.

    Returns:
        WorkflowDispatchResult with workflow URL and optionally run ID/URL.

    Raises:
        ValueError: If agent_session_url or cancel_guids is empty,
            or if no GitHub token is found.
        requests.HTTPError: If the workflow dispatch API call fails.
    """
    if not agent_session_url:
        raise ValueError("agent_session_url is required for cancel.")
    if not cancel_guids:
        raise ValueError("cancel_guids is required for cancel.")

    token = resolve_ci_trigger_github_token()

    workflow_inputs: dict[str, str] = {
        "action": "cancel",
        "agent_session_url": agent_session_url,
        "cancel_guids": json.dumps(cancel_guids),
    }

    return trigger_workflow_dispatch(
        owner=REMINDERS_REPO_OWNER,
        repo=REMINDERS_REPO_NAME,
        workflow_file=REMINDERS_WORKFLOW_FILE,
        ref=REMINDERS_DEFAULT_BRANCH,
        inputs=workflow_inputs,
        token=token,
    )
