# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
"""Slack notification dispatch: workflow dispatch and person resolution.

This module provides the core logic for dispatching Slack notifications via
GitHub Actions. It is used by both the `escalate_to_human` and
`devin_session_feedback` MCP tools.

The Slack API call happens inside CI (not at MCP runtime) so that Slack tokens
are never exposed to the calling agent. The workflow resolves person identifiers
to Slack user IDs using the internal team roster artifact.

Person identifier conventions:
- `@username` → GitHub handle (the leading `@` is stripped)
- `U` followed by uppercase alphanumeric (e.g. `U05AKF1BCC9`) → Slack user ID
- Contains `@` with a domain part (e.g. `aj@airbyte.io`) → email address
"""

from __future__ import annotations

import json
import logging
import re

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

logger = logging.getLogger(__name__)

HITL_REPO_OWNER = "airbytehq"
HITL_REPO_NAME = "airbyte-ops-mcp"
HITL_WORKFLOW_FILE = "human-in-the-loop.yml"
HITL_DEFAULT_BRANCH = "main"

WORKFLOW_TRIGGER_TOKEN_ENV_VARS = [
    "GITHUB_CI_WORKFLOW_TRIGGER_PAT",
    "GITHUB_TOKEN",
]

_SLACK_ID_PATTERN = re.compile(r"^U[A-Z0-9]{8,}$")


def classify_person_id(identifier: str) -> str:
    """Classify a person identifier string.

    Args:
        identifier: A person identifier (email, GitHub handle, or Slack ID).

    Returns:
        One of `"github_handle"`, `"slack_id"`, or `"email"`.
    """
    identifier = identifier.strip()
    if identifier.startswith("@"):
        return "github_handle"
    if _SLACK_ID_PATTERN.match(identifier):
        return "slack_id"
    if "@" in identifier and "." in identifier.split("@", 1)[-1]:
        return "email"
    return "github_handle"


def normalize_person_id(identifier: str) -> str:
    """Normalize a person identifier for workflow input.

    Strips leading `@` from GitHub handles and trims whitespace.

    Args:
        identifier: Raw person identifier string.

    Returns:
        Normalized identifier string.
    """
    identifier = identifier.strip()
    if identifier.startswith("@"):
        return identifier[1:]
    return identifier


def dispatch_escalation(
    target_person: str,
    message: str,
    agent_session_url: str,
    cc: list[str] | None = None,
    pr_url: str | None = None,
    issue_url: str | None = None,
    additional_actions: dict[str, str] | None = None,
    approval_requested: bool = False,
    approval_request_summary: str | None = None,
    approval_request_detail_url: str | None = None,
    channel_override: str | None = None,
    header_emoji: str | None = None,
    header_label: str | None = None,
    context_footer: str | None = None,
    connector_name: str | None = None,
) -> WorkflowDispatchResult:
    """Dispatch a Slack notification via the GitHub Actions workflow.

    Triggers the `human-in-the-loop.yml` workflow which resolves person
    identifiers to Slack user IDs (using the roster artifact) and posts
    a formatted message to Slack.

    The `channel_override`, `header_emoji`, `header_label`, and
    `context_footer` parameters are set by the calling MCP tool and
    are NOT exposed to agents directly.
    """
    token = resolve_github_token(preferred_env_vars=WORKFLOW_TRIGGER_TOKEN_ENV_VARS)

    workflow_inputs: dict[str, str] = {
        "target_person": normalize_person_id(target_person),
        "message": message,
        "agent_session_url": agent_session_url,
    }

    if cc:
        normalized_cc = [normalize_person_id(person) for person in cc]
        workflow_inputs["cc_persons"] = ",".join(normalized_cc)

    if pr_url:
        workflow_inputs["pr_url"] = pr_url

    if issue_url:
        workflow_inputs["issue_url"] = issue_url

    if additional_actions:
        workflow_inputs["additional_actions"] = json.dumps(additional_actions)

    if approval_requested:
        workflow_inputs["approval_requested"] = "true"

    if approval_request_summary:
        workflow_inputs["approval_request_summary"] = approval_request_summary

    if approval_request_detail_url:
        workflow_inputs["approval_request_detail_url"] = approval_request_detail_url

    if channel_override:
        workflow_inputs["channel_override"] = channel_override

    if header_emoji:
        workflow_inputs["header_emoji"] = header_emoji

    if header_label:
        workflow_inputs["header_label"] = header_label

    if context_footer:
        workflow_inputs["context_footer"] = context_footer

    if connector_name:
        workflow_inputs["connector_name"] = connector_name

    return trigger_workflow_dispatch(
        owner=HITL_REPO_OWNER,
        repo=HITL_REPO_NAME,
        workflow_file=HITL_WORKFLOW_FILE,
        ref=HITL_DEFAULT_BRANCH,
        inputs=workflow_inputs,
        token=token,
    )
