# Copyright (c) CloudZero - ALL RIGHTS RESERVED - PROPRIETARY AND CONFIDENTIAL
# Unauthorized copying of this file and/or project, via any medium is strictly prohibited.
# Direct all questions to legal@cloudzero.com
#

"""
V4 System API Client

This module provides a clean, modern interface for system operations using the v4 API
with Okta authentication:
- get_system_metadata() -> returns SystemMetadata (version, runtime, and health)
- post_system_usage(usage_data) -> returns UsageResponse
- get_system_refresh() -> returns dict
"""

import getpass
import sys
from typing import Any

from pydantic import BaseModel, Field, ValidationError
from rich.console import Console

from src.api.authenticated import get_authenticated, post_authenticated
from src.exceptions import APIError

console = Console()


# V4 Pydantic Models - Clean, modern API contract matching backend schema
class HealthCheck(BaseModel):
    """Individual health check result."""

    name: str = Field(..., description="Health check name")
    status: str = Field(..., description="Health check status (healthy/unhealthy/degraded)")
    message: str | None = Field(None, description="Health check message")
    duration_ms: float | None = Field(None, description="Check duration in milliseconds")
    timestamp: str | None = Field(None, description="Check timestamp")


class ServiceHealth(BaseModel):
    """Service health information."""

    service_name: str = Field(..., description="Service name")
    status: str = Field(..., description="Service status (healthy/unhealthy/degraded)")
    checks: list[HealthCheck] = Field(default_factory=list, description="Individual health checks")
    last_check: str | None = Field(None, description="Last health check timestamp")


class SystemMetadata(BaseModel):
    """System metadata response model — includes version, runtime, and health."""

    # Version / build
    cz_ops_services: str | None = Field(None, description="Backend services version")
    cz_ops_cli: str | None = Field(None, description="Latest CLI version from PyPI cache")
    build_commit: str | None = Field(None, description="Git SHA of deployed build")

    # Runtime
    environment: str | None = Field(None, description="Deployment environment (dev/prod)")
    region: str | None = Field(None, description="AWS region")
    deployment: str | None = Field(None, description="Deployment namespace (alfa/live)")
    python_version: str | None = Field(None, description="Python runtime version")
    lambda_function: str | None = Field(None, description="Lambda function name")
    lambda_memory_mb: int | None = Field(None, description="Lambda memory allocation in MB")

    # Health
    status: str | None = Field(None, description="Overall health status")
    services: dict[str, ServiceHealth] | None = Field(None, description="Per-service health")
    checks: list[HealthCheck] | None = Field(None, description="Individual health checks")

    timestamp: str | None = Field(None, description="Response timestamp")


class UsageData(BaseModel):
    """Usage tracking data model."""

    cli_version: str = Field(..., description="CLI version")
    user_email: str = Field(..., description="User email")
    user_command: str = Field(..., description="Command executed")
    args: dict[str, Any] = Field(default_factory=dict, description="Command arguments")
    timestamp: str | None = Field(None, description="Usage timestamp")
    duration: float | None = Field(None, description="Command duration in seconds")
    success: bool | None = Field(None, description="Whether command succeeded")
    error: str | None = Field(None, description="Error message if failed")


class UsageResponse(BaseModel):
    """V4 response model for usage tracking."""

    tracked: bool = Field(..., description="Whether usage was successfully tracked")
    usage_id: str | None = Field(None, description="Unique usage tracking ID")
    message: str | None = Field(None, description="Response message")
    success: bool = Field(default=True, description="Whether the request was successful")


class DropDatabaseResponse(BaseModel):
    """V4 response model for database drop operations."""

    dropped: bool = Field(..., description="Whether drop operation was successful")
    items_deleted: int = Field(default=0, description="Number of items deleted")
    items_failed: int = Field(default=0, description="Number of items that failed to delete")
    total_items: int = Field(default=0, description="Total number of items processed")
    batches_processed: int = Field(default=0, description="Number of batches processed")
    success_rate_percent: float | None = Field(None, description="Success rate percentage")
    failed_items: list[dict] = Field(default_factory=list, description="List of items that failed to delete")
    duration_ms: float | None = Field(None, description="Operation duration in milliseconds")
    operation_id: str | None = Field(None, description="Unique operation identifier")
    table_name: str | None = Field(None, description="Name of the table that was dropped")
    namespace: str | None = Field(None, description="Namespace of the operation")
    message: str | None = Field(None, description="Operation status message")
    success: bool = Field(default=True, description="Whether the request was successful")
    warning: str | None = Field(None, description="Warning message if applicable")


def get_system_metadata(debug: bool = False) -> SystemMetadata:
    """
    Get system metadata using v4 API.

    Args:
        debug: Whether to show debug information

    Returns:
        SystemMetadata: System metadata and version information

    Raises:
        APIError: If the API request fails
        ValueError: If the response format is invalid
    """
    if debug:
        console.print("[cyan]DEBUG: Fetching system metadata via V4 API[/]")

    try:
        # Get CLI version for tracking
        from src import __version__ as cli_version

        # Build query parameters for metadata tracking
        params = {"reported_version": cli_version, "reported_user": getpass.getuser()}

        # Make authenticated request to v4 system metadata endpoint
        response_data = get_authenticated(endpoint_path="system/metadata", params=params, debug=debug)

        if debug:
            console.print(f"[cyan]DEBUG: Raw API response: {response_data}[/]")

        # Handle different response formats from backend
        if isinstance(response_data, dict):
            # If response has metadata wrapper, extract it
            if "metadata" in response_data:
                return SystemMetadata(**response_data["metadata"])
            # Otherwise treat the whole response as metadata
            else:
                return SystemMetadata(**response_data)
        else:
            raise ValueError(f"Invalid response format: {type(response_data)}")

    except APIError:
        # Re-raise API errors as-is
        raise
    except ValidationError as e:
        if debug:
            console.print(f"[red]DEBUG: Response validation failed: {e}[/]")
        raise APIError(f"Invalid response format from v4 API: {e}") from e
    except Exception as e:
        error_msg = f"Failed to get system metadata: {str(e)}"
        if debug:
            console.print(f"[red]DEBUG: Unexpected error: {error_msg}[/]")
        raise APIError(error_msg) from e


def post_system_usage(usage_data: dict[str, Any], debug: bool = False) -> UsageResponse:
    """
    Track system usage using v4 API.

    Args:
        usage_data: Usage tracking data
        debug: Whether to show debug information

    Returns:
        UsageResponse: Usage tracking result

    Raises:
        APIError: If the API request fails
        ValueError: If the usage data is invalid
    """
    if debug:
        console.print("[cyan]DEBUG: Tracking system usage via V4 API[/]")

    try:
        # Get CLI version and user info
        from src import __version__ as cli_version

        # Build the usage payload
        payload = {
            "cli_version": cli_version,
            "user_email": getpass.getuser(),
            "user_command": " ".join(sys.argv),
            "args": {},
            **usage_data,  # Merge provided usage data
        }

        # Build query parameters for tracking
        params = {"reported_version": cli_version, "reported_user": getpass.getuser()}

        if debug:
            console.print(f"[cyan]DEBUG: Usage payload: {payload}[/]")

        # Make authenticated request to v4 system usage endpoint
        response_data = post_authenticated(endpoint_path="system/usage", json_data=payload, params=params, debug=debug)

        if debug:
            console.print(f"[cyan]DEBUG: Raw API response: {response_data}[/]")

        # Validate response with Pydantic model
        return UsageResponse(**response_data)

    except APIError:
        # Re-raise API errors as-is
        raise
    except ValidationError as e:
        if debug:
            console.print(f"[red]DEBUG: Response validation failed: {e}[/]")
        raise APIError(f"Invalid response format from v4 API: {e}") from e
    except Exception as e:
        error_msg = f"Failed to track system usage: {str(e)}"
        if debug:
            console.print(f"[red]DEBUG: Unexpected error: {error_msg}[/]")
        raise APIError(error_msg) from e


_CUR_TYPES_CACHE: dict[str, str] | None = None


def get_cur_types(debug: bool = False) -> dict[str, str]:
    """Return the CUR-type CONST→label map from ``GET /system/cur-types`` (cached per process).

    The enum is maintained backend-side and expected to grow, so clients render a connection's
    ``cur_type`` label from this map rather than hardcoding it. Returns an empty dict on failure
    (callers fall back to showing the bare CONST).
    """
    global _CUR_TYPES_CACHE
    if _CUR_TYPES_CACHE is not None:
        return _CUR_TYPES_CACHE
    try:
        response = get_authenticated(endpoint_path="system/cur-types", debug=debug)
    except Exception as error:
        # Transient failure — return empty WITHOUT caching, so a later call in the same
        # process can retry (don't conflate "fetch failed" with "no cur types").
        if debug:
            console.print(f"[yellow]DEBUG: could not fetch cur-types map: {error}[/]")
        return {}
    # The endpoint returns {"cur_types": {CONST: label}}; never fall back to the raw
    # envelope (an empty cur_types would otherwise poison the map with envelope keys). A
    # legitimately empty map IS cached — only fetch failures above are left uncached.
    _CUR_TYPES_CACHE = response.get("cur_types") or {}
    return _CUR_TYPES_CACHE


def get_system_refresh(debug: bool = False) -> dict:
    """
    Trigger an async system refresh via v4 API.

    The backend always runs refreshes asynchronously in a background Lambda
    and returns a job ID immediately (no API Gateway timeout risk).

    Args:
        debug: Whether to show debug information

    Returns:
        dict: Job start response containing job_id, status, and status_check_url

    Raises:
        APIError: If the API request fails
    """
    if debug:
        console.print("[cyan]DEBUG: Triggering async system refresh via V4 API[/]")

    try:
        response_data = get_authenticated(
            endpoint_path="system/refresh",
            debug=debug,
        )
        return response_data

    except APIError:
        raise
    except Exception as e:
        error_msg = f"Failed to trigger system refresh: {str(e)}"
        if debug:
            console.print(f"[red]DEBUG: Unexpected error: {error_msg}[/]")
        raise APIError(error_msg) from e


def get_refresh_status(job_id: str, debug: bool = False) -> dict:
    """
    Check the status of a background refresh job.

    Args:
        job_id: Job ID returned from get_system_refresh()
        debug: Whether to show debug information

    Returns:
        dict: Job status with keys like job_id, status, message, and result details when completed

    Raises:
        APIError: If the API request fails
    """
    if debug:
        console.print(f"[cyan]DEBUG: Checking refresh job status: {job_id}[/]")

    try:
        response_data = get_authenticated(
            endpoint_path=f"system/refresh/status/{job_id}",
            debug=debug,
        )
        return response_data

    except APIError:
        raise
    except Exception as e:
        error_msg = f"Failed to check refresh status: {str(e)}"
        if debug:
            console.print(f"[red]DEBUG: Unexpected error: {error_msg}[/]")
        raise APIError(error_msg) from e


def get_materialization_stats(debug: bool = False) -> dict:
    """
    Get system materialization statistics showing data freshness and processing status.

    Args:
        debug: Whether to show debug information

    Returns:
        dict: Materialization statistics including last refresh time and processing metrics

    Raises:
        APIError: If the API request fails
        ValueError: If the response format is invalid
    """
    if debug:
        console.print("[cyan]DEBUG: Fetching materialization stats via V4 API[/]")

    try:
        # Make authenticated request to v4 system materialization stats endpoint
        response_data = get_authenticated(endpoint_path="system/materialization/stats", debug=debug)
        return response_data

    except APIError:
        # Re-raise API errors as-is
        raise
    except Exception as e:
        error_msg = f"Failed to get materialization stats: {str(e)}"
        if debug:
            console.print(f"[red]DEBUG: Unexpected error: {error_msg}[/]")
        raise APIError(error_msg) from e


def drop_system_database(confirm: bool = False, force: bool = False, debug: bool = False) -> DropDatabaseResponse:
    """⚠️ EXTREMELY DANGEROUS: Drop all data from the system database using v4 API.

    This function permanently deletes ALL data from the cz-{namespace}-ops-services-data
    DynamoDB table. This operation is IRREVERSIBLE and will cause complete data loss.

    ⚠️ WARNING: This will delete all:
    - Organization data
    - Connection information
    - Historical records
    - Configuration settings
    - Everything in the database

    Use with extreme caution and only in development/testing environments.

    Args:
        confirm: Must be True to proceed with the destructive operation
        force: Set to True to bypass additional safety checks
        debug: Whether to show debug information

    Returns:
        DropDatabaseResponse: Drop operation results with statistics

    Raises:
        APIError: If the API request fails
        ValueError: If confirmation is not provided or operation fails
    """
    if not confirm:
        raise ValueError("Database drop operation requires explicit confirmation. Set confirm=True to proceed.")

    if debug:
        console.print("[red]⚠️ DEBUG: DANGEROUS DATABASE DROP OPERATION REQUESTED[/]")
        console.print("[yellow]DEBUG: This will permanently delete ALL system data[/]")

    try:
        # Build query parameters - using DELETE method requires params, not JSON body
        # Note: The request validation is handled by the function parameters, not a Pydantic model
        params = {"confirm": "true"}
        if force:
            params["force"] = "true"

        if debug:
            console.print(f"[red]DEBUG: Drop database request params: {params}[/]")

        # Make authenticated DELETE request to v4 system data endpoint
        # Note: Using DELETE method with query parameters as per API spec
        from src.api.authenticated import delete_authenticated

        response_data = delete_authenticated(endpoint_path="system/data", params=params, debug=debug)

        if debug:
            console.print(f"[cyan]DEBUG: Raw API response: {response_data}[/]")

        # Validate response with Pydantic model
        drop_response = DropDatabaseResponse(**response_data)

        if debug:
            if drop_response.dropped:
                console.print(f"[yellow]DEBUG: Successfully dropped {drop_response.items_deleted} items[/]")
                if drop_response.items_failed > 0:
                    console.print(f"[red]DEBUG: {drop_response.items_failed} items failed to delete[/]")
            else:
                console.print(f"[red]DEBUG: Database drop operation failed: {drop_response.message}[/]")

        return drop_response

    except APIError:
        # Re-raise API errors as-is
        raise
    except ValidationError as e:
        if debug:
            console.print(f"[red]DEBUG: Response validation failed: {e}[/]")
        raise APIError(f"Invalid response format from v4 API: {e}") from e
    except Exception as e:
        error_msg = f"Failed to drop system database: {str(e)}"
        if debug:
            console.print(f"[red]DEBUG: Unexpected error: {error_msg}[/]")
        raise APIError(error_msg) from e
