#!/usr/bin/env python3
"""
Example: External tool triggers maintenance via ss-tools API.

This script demonstrates how an ETL pipeline (Airflow DAG, cron job, CI/CD)
can start and end maintenance banners on Superset dashboards using the
ss-tools API Key authentication.

Requirements: Python 3.10+, requests (pip install requests)

Usage:
  # Start maintenance
  python maintenance-api-python.py start \
    --api-key ssk_M7xqaP2zL9vR4nF8kC1bH5jT6dW0yA3 \
    --base-url https://ss-tools.example.com \
    --tables public.messages,public.users \
    --duration-hours 4 \
    --message "Scheduled ETL refresh"

  # End maintenance by event ID
  python maintenance-api-python.py end \
    --api-key ssk_M7xqaP2zL9vR4nF8kC1bH5jT6dW0yA3 \
    --base-url https://ss-tools.example.com \
    --event-id m-abc123

  # End ALL maintenance (use with caution!)
  python maintenance-api-python.py end-all \
    --api-key ssk_M7xqaP2zL9vR4nF8kC1bH5jT6dW0yA3 \
    --base-url https://ss-tools.example.com \
    --environment ss-dev
"""

import argparse
import sys
from datetime import datetime, timezone, timedelta

try:
    import requests
except ImportError:
    print("Error: 'requests' library is required. Install with: pip install requests")
    sys.exit(1)


API_KEY_HEADER = "X-API-Key"


def start_maintenance(base_url: str, api_key: str, tables: list[str],
                      environment_id: str, duration_hours: int = 4,
                      message: str | None = None,
                      timeout: int = 30) -> dict:
    """
    Start a maintenance event.

    Args:
        base_url: ss-tools base URL (e.g. https://ss-tools.example.com)
        api_key: API key with 'maintenance:start' permission
        tables: List of table names to flag (e.g. ["public.messages"])
        environment_id: Superset environment (e.g. "ss-dev", "ss-prod")
        duration_hours: How long the maintenance window lasts
        message: Optional custom message for the banner
        timeout: Request timeout in seconds

    Returns:
        API response dict with task_id and maintenance_id

    Raises:
        requests.HTTPError: On 4xx/5xx response with error details
    """
    now = datetime.now(timezone.utc)
    start_time = now.isoformat()
    end_time = (now + timedelta(hours=duration_hours)).isoformat()

    payload = {
        "tables": tables,
        "start_time": start_time,
        "end_time": end_time,
        "environment_id": environment_id,
    }
    if message:
        payload["message"] = message

    print(f"[maintenance] Starting maintenance for tables: {tables}")
    print(f"[maintenance]   Window: {start_time} → {end_time}")
    print(f"[maintenance]   Environment: {environment_id}")

    response = requests.post(
        f"{base_url}/api/maintenance/start",
        json=payload,
        headers={API_KEY_HEADER: api_key},
        timeout=timeout,
    )

    if response.status_code == 202:
        result = response.json()
        print(f"[maintenance] ✅ Event created (id={result['maintenance_id']})")
        print(f"[maintenance]    Task: {result.get('task_id', 'N/A')}")
        return result

    if response.status_code == 400:
        error = _format_error(response)
        print(f"[maintenance] ❌ Validation error: {error}")
    elif response.status_code == 401:
        print("[maintenance] ❌ Authentication failed: invalid or revoked API key")
    elif response.status_code == 403:
        print("[maintenance] ❌ Permission denied: API key lacks 'maintenance:start'")
    else:
        print(f"[maintenance] ❌ Unexpected error ({response.status_code}): {response.text[:500]}")

    response.raise_for_status()
    return {}  # unreachable


def end_maintenance(base_url: str, api_key: str, event_id: str,
                    timeout: int = 30) -> dict:
    """
    End a specific maintenance event and remove its banners.

    Args:
        base_url: ss-tools base URL
        api_key: API key with 'maintenance:end' permission
        event_id: The event ID returned by start_maintenance
        timeout: Request timeout in seconds

    Returns:
        API response dict

    Raises:
        requests.HTTPError: On error
    """
    print(f"[maintenance] Ending maintenance event: {event_id}")

    response = requests.post(
        f"{base_url}/api/maintenance/{event_id}/end",
        headers={API_KEY_HEADER: api_key},
        timeout=timeout,
    )

    if response.status_code == 202:
        result = response.json()
        print(f"[maintenance] ✅ Event end scheduled (task={result.get('task_id', 'N/A')})")
        return result

    if response.status_code == 404:
        data = response.json()
        if data.get("status") == "already_completed":
            print("[maintenance] ℹ️  Event was already completed (idempotent)")
            return data
        print(f"[maintenance] ❌ Event not found: {event_id}")
    elif response.status_code == 401:
        print("[maintenance] ❌ Authentication failed: invalid or revoked API key")
    elif response.status_code == 403:
        print("[maintenance] ❌ Permission denied: API key lacks 'maintenance:end'")
    else:
        print(f"[maintenance] ❌ Unexpected error ({response.status_code}): {response.text[:500]}")

    response.raise_for_status()
    return {}


def end_all_maintenance(base_url: str, api_key: str,
                        environment_id: str | None = None,
                        timeout: int = 30) -> dict:
    """
    End ALL active maintenance events in the given environment.
    USE WITH CAUTION — this removes banners from all dashboards.

    Args:
        base_url: ss-tools base URL
        api_key: API key with 'maintenance:end_all' permission
        environment_id: If set, only end events in this environment.
                        If None, uses the API key's default scope.
        timeout: Request timeout in seconds

    Returns:
        API response dict

    Raises:
        requests.HTTPError: On error
    """
    print(f"[maintenance] Ending ALL maintenance events "
          f"(env={environment_id or 'key-scope'})")
    print("[maintenance] ⚠️  This will remove banners from ALL affected dashboards!")

    payload = {}
    if environment_id:
        payload["environment_id"] = environment_id

    response = requests.post(
        f"{base_url}/api/maintenance/end-all",
        json=payload,
        headers={API_KEY_HEADER: api_key},
        timeout=timeout,
    )

    if response.status_code == 202:
        result = response.json()
        print(f"[maintenance] ✅ All events end scheduled (task={result.get('task_id', 'N/A')})")
        return result

    if response.status_code == 401:
        print("[maintenance] ❌ Authentication failed: invalid or revoked API key")
    elif response.status_code == 403:
        print("[maintenance] ❌ Permission denied: API key lacks 'maintenance:end_all'")
    elif response.status_code == 400:
        print(f"[maintenance] ❌ {_format_error(response)}")
    else:
        print(f"[maintenance] ❌ Unexpected error ({response.status_code}): {response.text[:500]}")

    response.raise_for_status()
    return {}


def _format_error(response: requests.Response) -> str:
    """Extract human-readable error from API response."""
    try:
        data = response.json()
        if "detail" in data:
            detail = data["detail"]
            if isinstance(detail, list):
                return "; ".join(d.get("msg", str(d)) for d in detail)
            return str(detail)
        return response.text[:200]
    except Exception:
        return response.text[:200]


def main():
    parser = argparse.ArgumentParser(
        description="ss-tools Maintenance Banner CLI",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Start 4-hour maintenance on public.messages
  %(prog)s start --api-key ssk_... --tables public.messages \\\\
      --environment ss-dev --duration-hours 4

  # End maintenance by event ID
  %(prog)s end --api-key ssk_... --event-id m-abc123

  # Emergency: end ALL maintenance in ss-dev
  %(prog)s end-all --api-key ssk_... --environment ss-dev
        """,
    )
    parser.add_argument(
        "--base-url",
        default="http://localhost:8000",
        help="ss-tools base URL (default: %(default)s)",
    )
    parser.add_argument(
        "--api-key",
        required=True,
        help="API key with required permissions",
    )

    subparsers = parser.add_subparsers(dest="command", required=True)

    # start
    start_parser = subparsers.add_parser("start", help="Start maintenance")
    start_parser.add_argument(
        "--tables",
        required=True,
        help="Comma-separated table names (e.g. 'public.messages,public.users')",
    )
    start_parser.add_argument(
        "--environment",
        required=True,
        dest="environment_id",
        help="Superset environment ID (e.g. 'ss-dev', 'ss-prod')",
    )
    start_parser.add_argument(
        "--duration-hours",
        type=int,
        default=4,
        help="Maintenance window in hours (default: %(default)s)",
    )
    start_parser.add_argument(
        "--message",
        default=None,
        help="Optional message for the banner",
    )

    # end
    end_parser = subparsers.add_parser("end", help="End a specific maintenance event")
    end_parser.add_argument(
        "--event-id",
        required=True,
        help="Maintenance event ID (from start response)",
    )

    # end-all
    end_all_parser = subparsers.add_parser(
        "end-all", help="End ALL maintenance events (use with caution)"
    )
    end_all_parser.add_argument(
        "--environment",
        dest="environment_id",
        default=None,
        help="Limit to specific environment (optional)",
    )

    args = parser.parse_args()

    try:
        if args.command == "start":
            tables = [t.strip() for t in args.tables.split(",")]
            start_maintenance(
                base_url=args.base_url,
                api_key=args.api_key,
                tables=tables,
                environment_id=args.environment_id,
                duration_hours=args.duration_hours,
                message=args.message,
            )
        elif args.command == "end":
            end_maintenance(
                base_url=args.base_url,
                api_key=args.api_key,
                event_id=args.event_id,
            )
        elif args.command == "end-all":
            end_all_maintenance(
                base_url=args.base_url,
                api_key=args.api_key,
                environment_id=args.environment_id,
            )
    except requests.HTTPError:
        sys.exit(1)


if __name__ == "__main__":
    main()
