# 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 Anomalies API Client

This module provides a clean, modern interface for managing anomalies data using the v4 API
with Okta authentication. This replaces the legacy v2/v3 anomalies functions with:
- update_anomalies_batch() -> batch update anomalies with detailed response
- get_reviewable_anomalies() -> get reviewable anomalies data
- get_anomalies_processing_status() -> check processing status
"""

import json

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

from src.api.authenticated import get_authenticated, post_authenticated
from src.exceptions import APIError
from src.utils.data import JSONSanity

console = Console()


# V4 Pydantic Models - Clean, modern API contract matching backend schema
class AnomalyResult(BaseModel):
    """Individual anomaly processing result."""

    model_config = ConfigDict(extra="allow")

    anomaly_run_id: str = Field(..., description="Unique identifier for the anomaly")
    status: str = Field(..., description="Processing status: pending, processed, invalid, failed")
    notification_sent: bool = Field(default=False, description="Whether notification was sent")
    error_message: str | None = Field(None, description="Error message if processing failed")


class AnomalyProcessingResult(BaseModel):
    """Response model for anomaly processing operations matching V4 API spec."""

    model_config = ConfigDict(extra="allow")

    success: bool = Field(..., description="Whether the operation was successful")
    total_received: int = Field(default=0, description="Total number of anomalies received")
    total_processed: int = Field(
        default=0, description="Total number of anomalies processed (deprecated, use total_received)"
    )
    successfully_processed: int = Field(default=0, description="Number of anomalies successfully processed")
    notifications_sent: int = Field(default=0, description="Number of notifications sent")
    validation_errors: int = Field(default=0, description="Number of validation errors")
    processing_errors: int = Field(default=0, description="Number of processing errors")
    error_summary: str | None = Field(None, description="Summary of errors if any occurred")
    dry_run: bool = Field(default=False, description="Whether this was a dry run")
    results: list[AnomalyResult] = Field(default_factory=list, description="Individual anomaly processing results")
    processing_time_ms: int | None = Field(None, description="Processing time in milliseconds")


class AnomalyBatchRequest(BaseModel):
    """Request model for batch anomaly updates - completely flexible to accept any data structure."""

    model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)

    anomalies: list[dict] = Field(..., description="List of anomaly data dictionaries - accepts any structure")
    dry_run: bool = Field(default=False, description="Whether to perform a dry run")
    force_notification: bool | None = Field(None, description="Override individual anomaly notification settings")


class ReviewableAnomaliesRequest(BaseModel):
    """Request model for getting reviewable anomalies."""

    model_config = ConfigDict(extra="allow")

    namespace: str = Field(default="live", description="Namespace to process anomalies for")


class ReviewableAnomaliesResponse(BaseModel):
    """Response model for reviewable anomalies generation."""

    model_config = ConfigDict(extra="allow")

    success: bool = Field(..., description="Whether the request was successful")
    s3_url: str | None = Field(None, description="S3 URL where the data will be stored")
    processing_id: str | None = Field(None, description="ID to track processing status")


class ProcessingStatusResponse(BaseModel):
    """Response model for anomalies processing status."""

    model_config = ConfigDict(extra="allow")

    processing_complete: bool = Field(..., description="Whether processing is complete")
    download_url: str | None = Field(None, description="URL to download the results")
    error_message: str | None = Field(None, description="Error message if processing failed")


def update_anomalies_batch(
    anomalies: list[dict],
    dry_run: bool = False,
    force_notification: bool | None = None,
    debug: bool = False,
    raw: bool = False,
) -> AnomalyProcessingResult | dict:
    """
    Update anomalies in batch using v4 API.

    Args:
        anomalies: List of anomaly data dictionaries to update
        dry_run: Whether to perform a dry run without actual updates
        force_notification: Override individual anomaly notification settings
        debug: Whether to show debug information
        raw: Whether to return raw JSON response instead of parsed Pydantic model

    Returns:
        AnomalyProcessingResult | dict: Detailed processing results or raw JSON response

    Raises:
        APIError: If the API request fails
        ValueError: If the request data is invalid
    """
    if not anomalies:
        raise ValueError("Anomalies list cannot be empty")

    if debug:
        console.print(f"[cyan]DEBUG: Updating {len(anomalies)} anomalies via V4 API[/]")

    # Prepare request payload
    request_data = AnomalyBatchRequest(anomalies=anomalies, dry_run=dry_run, force_notification=force_notification)

    try:
        # Serialize the request data using JSONSanity to handle datetime objects
        json_data = json.loads(json.dumps(request_data.model_dump(exclude_none=True), cls=JSONSanity))

        # Make authenticated request to v4 anomalies batch endpoint
        response_data = post_authenticated(endpoint_path="anomalies/batch", json_data=json_data, debug=debug)

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

        # Return raw response if requested
        if raw:
            return response_data

        # Parse response into Pydantic model
        result = AnomalyProcessingResult(**response_data)

        if debug:
            console.print(
                f"[green]DEBUG: Successfully processed {result.successfully_processed}/{result.total_processed} anomalies[/]"
            )

        return result

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


def get_reviewable_anomalies(namespace: str = "live", debug: bool = False) -> ReviewableAnomaliesResponse:
    """
    Start the reviewable anomalies generation process using v4 API.

    Args:
        namespace: Namespace to process anomalies for
        debug: Whether to show debug information

    Returns:
        ReviewableAnomaliesResponse: Response with processing details

    Raises:
        APIError: If the API request fails
        ValueError: If the namespace is invalid
    """
    if not namespace:
        raise ValueError("Namespace cannot be empty")

    if debug:
        console.print(f"[cyan]DEBUG: Starting reviewable anomalies process for namespace: {namespace}[/]")

    # Prepare request payload
    request_data = ReviewableAnomaliesRequest(namespace=namespace)

    try:
        # Make authenticated request to v4 anomalies reviewable endpoint
        response_data = post_authenticated(
            endpoint_path="anomalies/reviewable", json_data=request_data.model_dump(), debug=debug
        )

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

        # Parse response into Pydantic model
        result = ReviewableAnomaliesResponse(**response_data)

        if debug:
            console.print(f"[green]DEBUG: Started reviewable anomalies process: {result.processing_id}[/]")

        return result

    except APIError:
        # Re-raise API errors as-is
        raise
    except Exception as e:
        # Handle other unexpected errors
        error_msg = f"Failed to start reviewable anomalies process: {str(e)}"
        if debug:
            console.print(f"[red]DEBUG: Unexpected error: {error_msg}[/]")
        raise APIError(error_msg) from e


def get_anomalies_processing_status(s3_url: str, debug: bool = False) -> ProcessingStatusResponse:
    """
    Check the status of anomalies processing using v4 API.

    Args:
        s3_url: S3 URL to check status for
        debug: Whether to show debug information

    Returns:
        ProcessingStatusResponse: Current processing status

    Raises:
        APIError: If the API request fails
        ValueError: If the S3 URL is invalid
    """
    if not s3_url:
        raise ValueError("S3 URL cannot be empty")

    if debug:
        console.print(f"[cyan]DEBUG: Checking processing status for: {s3_url}[/]")

    try:
        # Make authenticated request to v4 anomalies status endpoint
        response_data = get_authenticated(endpoint_path="anomalies/status", params={"s3_url": s3_url}, debug=debug)

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

        # Parse response into Pydantic model
        result = ProcessingStatusResponse(**response_data)

        if debug:
            status = "complete" if result.processing_complete else "in progress"
            console.print(f"[green]DEBUG: Processing status: {status}[/]")

        return result

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