"""MoEngage MCP Server - Main server implementation.

This module provides the MCP server that exposes MoEngage Stats API
as tools that can be called by LLMs via the Model Context Protocol.

Supports two deployment modes:
1. Single-tenant: Credentials from environment variables (default)
2. Multi-tenant: Credentials passed as tool parameters (for distributed deployment)

Usage:
    # Single-tenant with stdio transport (for Claude Desktop/CLI)
    moengage-mcp

    # Single-tenant with HTTP transport (for remote connections)
    moengage-mcp --transport http --port 8000

    # Multi-tenant mode (no env credentials required)
    moengage-mcp --multi-tenant --transport http --port 8000
"""

import logging
import os
import sys
from contextlib import asynccontextmanager
from dataclasses import dataclass, field
from datetime import date
from typing import AsyncIterator

from mcp.server.fastmcp import Context, FastMCP
from mcp.server.session import ServerSession
from pydantic import ValidationError

from .client import MoEngageClient, MoEngageAPIError
from .config import DataCenter, Settings, get_settings
from .models import (
    AttributionType,
    CampaignSearchResponse,
    MetricType,
    StatsRequest,
    StatsResponse,
)

# Get logger for this module
# NOTE: Logging is configured in main() for CLI usage.
# When used as a library, the application should configure logging.
logger = logging.getLogger(__name__)

# Multi-tenant mode flag (set via CLI or environment)
MULTI_TENANT_MODE = os.environ.get("MCP_MULTI_TENANT", "false").lower() == "true"


@dataclass
class AppContext:
    """Application context holding shared resources.

    In single-tenant mode: holds a shared client with env credentials.
    In multi-tenant mode: client is None, created per-request.
    """

    client: MoEngageClient | None
    settings: Settings | None
    multi_tenant: bool = False
    _client_cache: dict = field(default_factory=dict)

    async def get_client(
        self,
        workspace_id: str | None = None,
        campaign_reports_api_key: str | None = None,
        data_center: str = "01",
    ) -> MoEngageClient:
        """Get a MoEngage client, creating one if needed for multi-tenant mode.

        Args:
            workspace_id: MoEngage Workspace ID (required in multi-tenant mode)
            campaign_reports_api_key: MoEngage Campaign Reports API Key (required in multi-tenant mode)
            data_center: Data center identifier (01-06)

        Returns:
            MoEngageClient instance

        Raises:
            ValueError: If credentials missing in multi-tenant mode
        """
        # Single-tenant mode: use shared client
        if not self.multi_tenant and self.client:
            return self.client

        # Multi-tenant mode: need credentials
        if not workspace_id or not campaign_reports_api_key:
            if self.client:
                # Fall back to env credentials if available
                return self.client
            raise ValueError(
                "Multi-tenant mode requires workspace_id and campaign_reports_api_key parameters. "
                "Please provide your MoEngage credentials."
            )

        # Create or get cached client for this workspace
        cache_key = f"{workspace_id}:{data_center}"
        if cache_key not in self._client_cache:
            settings = Settings(
                workspace_id=workspace_id,
                campaign_reports_api_key=campaign_reports_api_key,
                data_center=data_center,
            )
            self._client_cache[cache_key] = MoEngageClient(settings)
            logger.info("Created client for workspace %s (DC%s)", settings.masked_workspace_id, data_center)

        return self._client_cache[cache_key]

    async def close_all(self) -> None:
        """Close all clients."""
        if self.client:
            await self.client.close()
        for client in self._client_cache.values():
            await client.close()
        self._client_cache.clear()


@asynccontextmanager
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
    """Manage application lifecycle.

    Creates and cleans up shared resources:
    - MoEngage API client with connection pooling (single-tenant)
    - Or empty context for multi-tenant mode

    Args:
        server: The FastMCP server instance

    Yields:
        AppContext with initialized resources
    """
    global MULTI_TENANT_MODE

    logger.info("Starting MoEngage MCP Server...")
    logger.info("Multi-tenant mode: %s", MULTI_TENANT_MODE)

    client = None
    settings = None

    if not MULTI_TENANT_MODE:
        # Single-tenant: load credentials from environment
        try:
            settings = get_settings()
            logger.info(
                "Configured for data center: %s (%s)",
                settings.data_center.name,
                settings.api_base_url,
            )
            client = MoEngageClient(settings)
        except ValidationError as e:
            logger.warning("No environment credentials found: %s", e)
            logger.info(
                "Running without default credentials. "
                "Clients must provide credentials via tool parameters."
            )
            MULTI_TENANT_MODE = True

    if MULTI_TENANT_MODE:
        logger.info(
            "Multi-tenant mode enabled. Clients must provide credentials:\n"
            "  - workspace_id: MoEngage Workspace ID\n"
            "  - campaign_reports_api_key: MoEngage Campaign Reports API Key\n"
            "  - data_center: Data center (01-06), optional"
        )

    context = AppContext(
        client=client,
        settings=settings,
        multi_tenant=MULTI_TENANT_MODE,
    )

    try:
        yield context
    finally:
        logger.info("Shutting down MoEngage MCP Server...")
        await context.close_all()


# Server configuration from environment
import os

_server_host = os.environ.get("MCP_SERVER_HOST", "127.0.0.1")
_server_port = int(os.environ.get("MCP_SERVER_PORT", "8000"))

# Build instructions based on mode
_instructions = """
MoEngage MCP Server

This server provides access to MoEngage APIs, enabling LLMs to interact
with the MoEngage customer engagement platform.

Available tools:

1. get_campaign_stats - Fetch campaign performance statistics
   - Delivery metrics: attempted, sent, failed
   - Engagement metrics: impressions, clicks, CTR
   - Rate metrics: delivery_rate, sent_rate, failure_rate

2. get_email_campaigns - Get Email campaign details
   - Campaign configuration and metadata
   - Email content (subject, from, variations)
   - Supports filtering by status and pagination

3. get_push_campaigns - Get Push notification campaign details
   - Campaign configuration and metadata
   - Push content (title, message, variations)
   - Supports filtering by status and pagination

4. get_sms_campaigns - Get SMS campaign details
   - Campaign configuration and metadata
   - SMS content and connector info
   - Supports filtering by status and pagination

Authentication:
- Single-tenant mode: Credentials from environment variables
- Multi-tenant mode: Pass credentials as tool parameters:
  - workspace_id: Your MoEngage Workspace ID
  - campaign_reports_api_key: Your MoEngage Campaign Reports API Key
  - data_center: Data center (01-06), defaults to 01
"""

# Create the MCP server instance
mcp = FastMCP(
    name="MoEngage MCP Server",
    instructions=_instructions,
    lifespan=app_lifespan,
    host=_server_host,
    port=_server_port,
)


@mcp.tool()
async def get_campaign_stats(
    campaign_ids: list[str],
    start_date: str,
    end_date: str,
    attribution_type: str = "VIEW_THROUGH",
    metric_type: str = "TOTAL",
    request_id: str = "mcp-request",
    workspace_id: str | None = None,
    campaign_reports_api_key: str | None = None,
    data_center: str = "01",
    ctx: Context[ServerSession, AppContext] | None = None,
) -> StatsResponse:
    """Fetch campaign statistics from MoEngage Stats API.

    Retrieves performance metrics for specified campaigns within a date range.
    Stats include delivery metrics (sent, failed), engagement metrics
    (impressions, clicks, CTR), and rate calculations.

    Args:
        campaign_ids: List of campaign IDs to fetch stats for (max 50).
            Example: ["6643773d4fe37fe7a10b718e", "6645fbae1101e4c6611b15c7"]
        start_date: Start date in YYYY-MM-DD format.
            Example: "2024-12-01"
        end_date: End date in YYYY-MM-DD format.
            Example: "2024-12-31"
        attribution_type: Attribution model - "VIEW_THROUGH" or "CLICK_THROUGH".
            Defaults to "VIEW_THROUGH".
        metric_type: Metric aggregation - "TOTAL" or "UNIQUE".
            Defaults to "TOTAL".
        request_id: Optional request ID for tracking.
            Defaults to "mcp-request".
        workspace_id: MoEngage Workspace ID (required in multi-tenant mode).
        campaign_reports_api_key: MoEngage Campaign Reports API Key (required in multi-tenant mode).
        data_center: MoEngage Data Center (01-06). Defaults to "01".
        ctx: MCP context (injected automatically).

    Returns:
        StatsResponse with campaign statistics including:
        - success: Whether the request succeeded
        - campaigns: List of campaign stats with metrics
        - error_message: Error details if request failed
        - rate_limit_remaining: Remaining API calls this minute

    Raises:
        ValueError: If parameters are invalid
        MoEngageAPIError: If API request fails

    Example:
        >>> result = await get_campaign_stats(
        ...     campaign_ids=["abc123", "def456"],
        ...     start_date="2024-12-01",
        ...     end_date="2024-12-31"
        ... )
        >>> for campaign in result.campaigns:
        ...     print(f"{campaign.campaign_id}: {campaign.all_variations.sent} sent")
    """
    if ctx is None:
        raise RuntimeError("Context not available - server not properly initialized")

    app_ctx = ctx.request_context.lifespan_context
    if app_ctx is None:
        raise RuntimeError("Application context not available")

    # Log the request
    await ctx.info(f"Fetching stats for {len(campaign_ids)} campaign(s)")
    logger.info(
        "Stats request: campaigns=%d, dates=%s to %s",
        len(campaign_ids),
        start_date,
        end_date,
    )

    # Get client (uses provided credentials in multi-tenant mode)
    try:
        client = await app_ctx.get_client(workspace_id, campaign_reports_api_key, data_center)
    except ValueError as e:
        return StatsResponse(
            request_id=request_id,
            success=False,
            error_message=str(e),
        )

    try:
        # Parse and validate dates
        try:
            parsed_start = date.fromisoformat(start_date)
            parsed_end = date.fromisoformat(end_date)
        except ValueError as e:
            return StatsResponse(
                request_id=request_id,
                success=False,
                error_message=f"Invalid date format. Use YYYY-MM-DD. Error: {e}",
            )

        # Parse attribution type
        try:
            parsed_attribution = AttributionType(attribution_type.upper())
        except ValueError:
            return StatsResponse(
                request_id=request_id,
                success=False,
                error_message=(
                    f"Invalid attribution_type: {attribution_type}. "
                    "Must be VIEW_THROUGH or CLICK_THROUGH."
                ),
            )

        # Parse metric type
        try:
            parsed_metric = MetricType(metric_type.upper())
        except ValueError:
            return StatsResponse(
                request_id=request_id,
                success=False,
                error_message=(
                    f"Invalid metric_type: {metric_type}. " "Must be TOTAL or UNIQUE."
                ),
            )

        # Build and validate request
        stats_request = StatsRequest(
            request_id=request_id,
            campaign_ids=campaign_ids,
            start_date=parsed_start,
            end_date=parsed_end,
            attribution_type=parsed_attribution,
            metric_type=parsed_metric,
        )

        # Report progress
        await ctx.report_progress(progress=0.3, total=1.0)

        # Make API call
        result = await client.get_campaign_stats(stats_request)

        # Report completion
        await ctx.report_progress(progress=1.0, total=1.0)

        if result.success:
            await ctx.info(
                f"Successfully retrieved stats for {len(result.campaigns)} campaign(s)"
            )
        else:
            await ctx.warning(f"Stats request failed: {result.error_message}")

        return result

    except ValidationError as e:
        logger.error("Validation error: %s", e)
        return StatsResponse(
            request_id=request_id,
            success=False,
            error_message=f"Invalid request parameters: {e}",
        )
    except MoEngageAPIError as e:
        logger.error("API error: %s", e)
        return StatsResponse(
            request_id=request_id,
            success=False,
            error_message=str(e),
        )
    except Exception as e:
        logger.exception("Unexpected error in get_campaign_stats")
        return StatsResponse(
            request_id=request_id,
            success=False,
            error_message=f"Unexpected error: {type(e).__name__}: {e}",
        )


@mcp.tool()
async def get_email_campaigns(
    campaign_id: str | None = None,
    campaign_name: str | None = None,
    status: str | None = None,
    limit: int = 10,
    page: int = 1,
    include_archived: bool = False,
    workspace_id: str | None = None,
    campaign_reports_api_key: str | None = None,
    data_center: str = "01",
    ctx: Context[ServerSession, AppContext] | None = None,
) -> "CampaignSearchResponse":
    """Get Email campaign details from MoEngage.

    Retrieves a list of Email campaigns with their configuration,
    content, and metadata.

    Args:
        campaign_id: Optional specific campaign ID to fetch.
        campaign_name: Optional campaign name to filter by.
        status: Optional filter by status (DRAFT, SCHEDULED, ACTIVE, PAUSED, COMPLETED, STOPPED).
        limit: Number of campaigns to return per page (1-15, default 10).
        page: Page number for pagination (default 1).
        include_archived: Include archived campaigns (default False).
        workspace_id: MoEngage Workspace ID (required in multi-tenant mode).
        campaign_reports_api_key: MoEngage Campaign Reports API Key (required in multi-tenant mode).
        data_center: MoEngage Data Center (01-06). Defaults to "01".
        ctx: MCP context (injected automatically).

    Returns:
        CampaignSearchResponse with Email campaign details including:
        - campaign_id, channel, status, delivery_type
        - basic_details (name, team, tags)
        - variations (subject, from_name, from_email, etc.)
        - utm_params, connector info
    """
    from .models import (
        CampaignChannel,
        CampaignSearchRequest,
        CampaignSearchResponse,
        CampaignStatus,
    )

    if ctx is None:
        raise RuntimeError("Context not available")

    app_ctx = ctx.request_context.lifespan_context
    if app_ctx is None:
        raise RuntimeError("Application context not available")

    await ctx.info(f"Fetching Email campaigns (page {page}, limit {limit})")

    try:
        # Get client (uses provided credentials in multi-tenant mode)
        client = await app_ctx.get_client(workspace_id, campaign_reports_api_key, data_center)

        # Parse status filter if provided
        status_filter = None
        if status:
            try:
                status_filter = [CampaignStatus(status.upper())]
            except ValueError:
                return CampaignSearchResponse(
                    request_id="mcp-email-campaigns",
                    success=False,
                    error_message=f"Invalid status: {status}. Must be one of: DRAFT, SCHEDULED, ACTIVE, PAUSED, COMPLETED, STOPPED",
                )

        request = CampaignSearchRequest(
            request_id="mcp-email-campaigns",
            channels=[CampaignChannel.EMAIL],
            campaign_id=campaign_id,
            campaign_name=campaign_name,
            campaign_status=status_filter,
            limit=limit,
            page=page,
            include_archive_campaigns=include_archived,
        )

        result = await client.search_campaigns(request)

        if result.success:
            await ctx.info(f"Found {result.total_campaigns} Email campaign(s)")
        else:
            await ctx.warning(f"Email campaign search failed: {result.error_message}")

        return result

    except ValueError as e:
        return CampaignSearchResponse(
            request_id="mcp-email-campaigns",
            success=False,
            error_message=str(e),
        )
    except Exception as e:
        logger.exception("Error in get_email_campaigns")
        return CampaignSearchResponse(
            request_id="mcp-email-campaigns",
            success=False,
            error_message=f"Unexpected error: {type(e).__name__}: {e}",
        )


@mcp.tool()
async def get_push_campaigns(
    campaign_id: str | None = None,
    campaign_name: str | None = None,
    status: str | None = None,
    limit: int = 10,
    page: int = 1,
    include_archived: bool = False,
    workspace_id: str | None = None,
    campaign_reports_api_key: str | None = None,
    data_center: str = "01",
    ctx: Context[ServerSession, AppContext] | None = None,
) -> "CampaignSearchResponse":
    """Get Push notification campaign details from MoEngage.

    Retrieves a list of Push campaigns with their configuration,
    content, and metadata.

    Args:
        campaign_id: Optional specific campaign ID to fetch.
        campaign_name: Optional campaign name to filter by.
        status: Optional filter by status (DRAFT, SCHEDULED, ACTIVE, PAUSED, COMPLETED, STOPPED).
        limit: Number of campaigns to return per page (1-15, default 10).
        page: Page number for pagination (default 1).
        include_archived: Include archived campaigns (default False).
        workspace_id: MoEngage Workspace ID (required in multi-tenant mode).
        campaign_reports_api_key: MoEngage Campaign Reports API Key (required in multi-tenant mode).
        data_center: MoEngage Data Center (01-06). Defaults to "01".
        ctx: MCP context (injected automatically).

    Returns:
        CampaignSearchResponse with Push campaign details including:
        - campaign_id, channel, platform, status, delivery_type
        - basic_details (name, team, tags)
        - variations (title, message, etc.)
        - utm_params, connector info
    """
    from .models import (
        CampaignChannel,
        CampaignSearchRequest,
        CampaignSearchResponse,
        CampaignStatus,
    )

    if ctx is None:
        raise RuntimeError("Context not available")

    app_ctx = ctx.request_context.lifespan_context
    if app_ctx is None:
        raise RuntimeError("Application context not available")

    await ctx.info(f"Fetching Push campaigns (page {page}, limit {limit})")

    try:
        # Get client (uses provided credentials in multi-tenant mode)
        client = await app_ctx.get_client(workspace_id, campaign_reports_api_key, data_center)

        # Parse status filter if provided
        status_filter = None
        if status:
            try:
                status_filter = [CampaignStatus(status.upper())]
            except ValueError:
                return CampaignSearchResponse(
                    request_id="mcp-push-campaigns",
                    success=False,
                    error_message=f"Invalid status: {status}. Must be one of: DRAFT, SCHEDULED, ACTIVE, PAUSED, COMPLETED, STOPPED",
                )

        request = CampaignSearchRequest(
            request_id="mcp-push-campaigns",
            channels=[CampaignChannel.PUSH],
            campaign_id=campaign_id,
            campaign_name=campaign_name,
            campaign_status=status_filter,
            limit=limit,
            page=page,
            include_archive_campaigns=include_archived,
        )

        result = await client.search_campaigns(request)

        if result.success:
            await ctx.info(f"Found {result.total_campaigns} Push campaign(s)")
        else:
            await ctx.warning(f"Push campaign search failed: {result.error_message}")

        return result

    except ValueError as e:
        return CampaignSearchResponse(
            request_id="mcp-push-campaigns",
            success=False,
            error_message=str(e),
        )
    except Exception as e:
        logger.exception("Error in get_push_campaigns")
        return CampaignSearchResponse(
            request_id="mcp-push-campaigns",
            success=False,
            error_message=f"Unexpected error: {type(e).__name__}: {e}",
        )


@mcp.tool()
async def get_sms_campaigns(
    campaign_id: str | None = None,
    campaign_name: str | None = None,
    status: str | None = None,
    limit: int = 10,
    page: int = 1,
    include_archived: bool = False,
    workspace_id: str | None = None,
    campaign_reports_api_key: str | None = None,
    data_center: str = "01",
    ctx: Context[ServerSession, AppContext] | None = None,
) -> "CampaignSearchResponse":
    """Get SMS campaign details from MoEngage.

    Retrieves a list of SMS campaigns with their configuration,
    content, and metadata.

    Args:
        campaign_id: Optional specific campaign ID to fetch.
        campaign_name: Optional campaign name to filter by.
        status: Optional filter by status (DRAFT, SCHEDULED, ACTIVE, PAUSED, COMPLETED, STOPPED).
        limit: Number of campaigns to return per page (1-15, default 10).
        page: Page number for pagination (default 1).
        include_archived: Include archived campaigns (default False).
        workspace_id: MoEngage Workspace ID (required in multi-tenant mode).
        campaign_reports_api_key: MoEngage Campaign Reports API Key (required in multi-tenant mode).
        data_center: MoEngage Data Center (01-06). Defaults to "01".
        ctx: MCP context (injected automatically).

    Returns:
        CampaignSearchResponse with SMS campaign details including:
        - campaign_id, channel, status, delivery_type
        - basic_details (name, team, tags)
        - variations (message content, etc.)
        - connector info (SMS provider)
    """
    from .models import (
        CampaignChannel,
        CampaignSearchRequest,
        CampaignSearchResponse,
        CampaignStatus,
    )

    if ctx is None:
        raise RuntimeError("Context not available")

    app_ctx = ctx.request_context.lifespan_context
    if app_ctx is None:
        raise RuntimeError("Application context not available")

    await ctx.info(f"Fetching SMS campaigns (page {page}, limit {limit})")

    try:
        # Get client (uses provided credentials in multi-tenant mode)
        client = await app_ctx.get_client(workspace_id, campaign_reports_api_key, data_center)

        # Parse status filter if provided
        status_filter = None
        if status:
            try:
                status_filter = [CampaignStatus(status.upper())]
            except ValueError:
                return CampaignSearchResponse(
                    request_id="mcp-sms-campaigns",
                    success=False,
                    error_message=f"Invalid status: {status}. Must be one of: DRAFT, SCHEDULED, ACTIVE, PAUSED, COMPLETED, STOPPED",
                )

        request = CampaignSearchRequest(
            request_id="mcp-sms-campaigns",
            channels=[CampaignChannel.SMS],
            campaign_id=campaign_id,
            campaign_name=campaign_name,
            campaign_status=status_filter,
            limit=limit,
            page=page,
            include_archive_campaigns=include_archived,
        )

        result = await client.search_campaigns(request)

        if result.success:
            await ctx.info(f"Found {result.total_campaigns} SMS campaign(s)")
        else:
            await ctx.warning(f"SMS campaign search failed: {result.error_message}")

        return result

    except ValueError as e:
        return CampaignSearchResponse(
            request_id="mcp-sms-campaigns",
            success=False,
            error_message=str(e),
        )
    except Exception as e:
        logger.exception("Error in get_sms_campaigns")
        return CampaignSearchResponse(
            request_id="mcp-sms-campaigns",
            success=False,
            error_message=f"Unexpected error: {type(e).__name__}: {e}",
        )


@mcp.resource("moengage://config")
def get_server_config() -> str:
    """Get current server configuration (non-sensitive).

    Returns information about the configured data center
    and server settings without exposing credentials.
    """
    try:
        settings = get_settings()
        return f"""MoEngage MCP Server Configuration
================================
Data Center: {settings.data_center.name} ({settings.data_center.value})
API Base URL: {settings.api_base_url}
Stats API URL: {settings.stats_api_url}
Timeout: {settings.timeout}s
Max Retries: {settings.max_retries}
Workspace ID: {settings.masked_workspace_id}
API Key: {settings.masked_api_key}
"""
    except Exception as e:
        return f"Configuration not available: {e}"


@mcp.prompt()
def analyze_campaign_performance(campaign_id: str, date_range: str = "last 7 days") -> str:
    """Generate a prompt for analyzing campaign performance.

    Args:
        campaign_id: The campaign ID to analyze
        date_range: Human-readable date range description

    Returns:
        A prompt that guides the LLM to analyze campaign stats
    """
    return f"""Please analyze the performance of MoEngage campaign {campaign_id}
for the {date_range}.

First, fetch the campaign statistics using the get_campaign_stats tool.
Then provide insights on:

1. **Delivery Performance**
   - How many messages were attempted vs sent?
   - What is the delivery rate and failure rate?
   - Are there any delivery issues to investigate?

2. **Engagement Metrics**
   - What is the click-through rate (CTR)?
   - How does engagement compare to industry benchmarks?
   - Which variations (if any) performed best?

3. **Recommendations**
   - What optimizations could improve performance?
   - Are there any red flags or concerns?
   - What A/B tests might be worth running?

Please present the analysis in a clear, actionable format.
"""


def main() -> None:
    """Main entry point for the MoEngage MCP Server.

    Supports multiple transport modes:
    - stdio (default): For Claude Desktop and CLI integration
    - http: For remote HTTP connections
    - sse: For Server-Sent Events transport

    And deployment modes:
    - single-tenant (default): Credentials from environment variables
    - multi-tenant: Credentials passed as tool parameters
    """
    global MULTI_TENANT_MODE
    import argparse

    parser = argparse.ArgumentParser(
        description="MoEngage MCP Server - Access MoEngage APIs via Model Context Protocol",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Deployment Modes:
  Single-tenant (default):
    - Credentials from environment variables
    - All clients use same MoEngage account
    - Best for: Internal use, single organization

  Multi-tenant (--multi-tenant):
    - Credentials passed as tool parameters
    - Each client can use different MoEngage accounts
    - Best for: SaaS deployment, multiple customers

Environment Variables (single-tenant mode):
  MOENGAGE_WORKSPACE_ID              Your MoEngage Workspace ID (required)
  MOENGAGE_CAMPAIGN_REPORTS_API_KEY  Your MoEngage Campaign Reports API Key (required)
  MOENGAGE_DATA_CENTER               Data center: 01, 02, 03, 04, 05, 06 (default: 01)
  MOENGAGE_TIMEOUT                   API timeout in seconds (default: 30)
  MOENGAGE_MAX_RETRIES               Max retry attempts (default: 3)

Examples:
  # Single-tenant with stdio for Claude Desktop
  moengage-mcp

  # Single-tenant with HTTP transport
  moengage-mcp --transport http --port 8000

  # Multi-tenant mode (no env credentials needed)
  moengage-mcp --multi-tenant --transport http --port 8000

  # Multi-tenant: Clients pass credentials as parameters:
  # workspace_id, campaign_reports_api_key, data_center
""",
    )

    parser.add_argument(
        "--transport",
        choices=["stdio", "http", "sse"],
        default="stdio",
        help="Transport protocol (default: stdio)",
    )
    parser.add_argument(
        "--host",
        default="127.0.0.1",
        help="Host to bind to for HTTP/SSE (default: 127.0.0.1)",
    )
    parser.add_argument(
        "--port",
        type=int,
        default=8000,
        help="Port to bind to for HTTP/SSE (default: 8000)",
    )
    parser.add_argument(
        "--multi-tenant",
        action="store_true",
        help="Enable multi-tenant mode (credentials via tool parameters)",
    )
    parser.add_argument(
        "--debug",
        action="store_true",
        help="Enable debug logging",
    )

    args = parser.parse_args()

    # Configure logging for CLI usage
    # This is only done when running as CLI, not when used as a library
    log_level = logging.DEBUG if args.debug else logging.INFO
    logging.basicConfig(
        level=log_level,
        format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
        handlers=[logging.StreamHandler(sys.stderr)],
    )

    # Set multi-tenant mode if requested
    if args.multi_tenant:
        MULTI_TENANT_MODE = True
        os.environ["MCP_MULTI_TENANT"] = "true"

    logger.info(
        "Starting MoEngage MCP Server with transport=%s, multi_tenant=%s",
        args.transport,
        MULTI_TENANT_MODE,
    )

    # Set host/port via environment for the FastMCP instance
    if args.host != "127.0.0.1":
        os.environ["MCP_SERVER_HOST"] = args.host
    if args.port != 8000:
        os.environ["MCP_SERVER_PORT"] = str(args.port)

    if args.transport == "stdio":
        mcp.run(transport="stdio")
    elif args.transport == "http":
        mcp.run(transport="streamable-http")
    elif args.transport == "sse":
        mcp.run(transport="sse")


if __name__ == "__main__":
    main()
