#!/usr/bin/env python3
"""
JSON Serialization Utilities

This module provides JSON serialization utilities for Oracle database types,
particularly for handling DATE and TIMESTAMP types.
"""

import json
import datetime
from decimal import Decimal
from typing import Any, Dict, List, Union

import cx_Oracle


class OracleJSONEncoder(json.JSONEncoder):
    """
    Custom JSON encoder for Oracle database types.
    """

    def default(self, obj: Any) -> Union[str, float, int, Dict, List, None]:
        """
        Convert Oracle-specific types to JSON-serializable formats.

        Args:
            obj: Object to serialize

        Returns:
            JSON-serializable representation of the object
        """
        # Handle datetime objects (prefer Python datetime; cx_Oracle may not expose its own type)
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()
        # Fallback for driver-specific datetime type if present
        try:
            if hasattr(cx_Oracle, "Datetime") and isinstance(obj, cx_Oracle.Datetime):
                return obj.isoformat()
        except Exception:
            pass

        if isinstance(obj, datetime.date):
            return obj.isoformat()

        if isinstance(obj, datetime.time):
            return obj.isoformat()

        # Handle Oracle LOB types
        if isinstance(obj, cx_Oracle.LOB):
            try:
                return obj.read()
            except Exception:
                return "[LOB read error]"

        # Handle Decimal
        if isinstance(obj, Decimal):
            return float(obj)

        # Handle other types
        return super().default(obj)


def serialize_oracle_data(data: Any) -> str:
    """
    Serialize Oracle database data to JSON string.

    Args:
        data: Data to serialize (can be dict, list, or single value)

    Returns:
        str: JSON string representation
    """
    return json.dumps(data, cls=OracleJSONEncoder, ensure_ascii=False, indent=2)


def format_query_result(result: Dict[str, Any]) -> Dict[str, Any]:
    """
    Format query result for JSON serialization.

    Args:
        result: Raw query result from OracleExecutor

    Returns:
        dict: Formatted result ready for JSON serialization
    """
    if result.get("status") != "success":
        return result

    # Process rows to ensure all values are JSON-serializable
    formatted_rows = []
    for row in result.get("rows", []):
        formatted_row = {}
        for key, value in row.items():
            formatted_row[key] = _format_value(value)
        formatted_rows.append(formatted_row)

    # Update result with formatted rows
    formatted_result = result.copy()
    formatted_result["rows"] = formatted_rows

    return formatted_result


def _format_value(value: Any) -> Any:
    """
    Format a single value for JSON serialization.

    Args:
        value: Value to format

    Returns:
        Formatted value
    """
    if value is None:
        return None

    # Handle datetime objects (prefer Python datetime; cx_Oracle may not expose its own type)
    if isinstance(value, datetime.datetime):
        return value.isoformat()
    try:
        if hasattr(cx_Oracle, "Datetime") and isinstance(value, cx_Oracle.Datetime):
            return value.isoformat()
    except Exception:
        pass

    if isinstance(value, datetime.date):
        return value.isoformat()

    if isinstance(value, datetime.time):
        return value.isoformat()

    # Handle Oracle LOB types
    if isinstance(value, cx_Oracle.LOB):
        try:
            lob_value = value.read()
            if isinstance(lob_value, bytes):
                lob_value = lob_value.decode("utf-8", errors="replace")
            return lob_value
        except Exception:
            return "[LOB read error]"

    # Handle Decimal
    if isinstance(value, Decimal):
        return float(value)

    # Handle strings (truncate if too long)
    if isinstance(value, str):
        max_size = int(os.getenv("MCP_MAX_TEXT_SIZE", "5000"))
        if len(value) > max_size:
            return value[:max_size] + "... [truncated]"
        return value

    # Handle bytes
    if isinstance(value, bytes):
        try:
            return value.decode("utf-8", errors="replace")
        except Exception:
            return "[binary data]"

    return value


def safe_json_dumps(data: Any, indent: int = 2) -> str:
    """
    Safely serialize data to JSON, handling Oracle types.

    Args:
        data: Data to serialize
        indent: JSON indentation level

    Returns:
        str: JSON string
    """
    try:
        return json.dumps(
            data, cls=OracleJSONEncoder, ensure_ascii=False, indent=indent
        )
    except Exception as e:
        # Fallback for problematic data
        return json.dumps(
            {
                "error": f"JSON serialization error: {str(e)}",
                "data_type": str(type(data)),
                "data_repr": repr(data)[:500],  # Limit representation length
            },
            ensure_ascii=False,
            indent=indent,
        )


def parse_json_safely(json_str: str, default: Any = None) -> Any:
    """
    Safely parse JSON string.

    Args:
        json_str: JSON string to parse
        default: Default value if parsing fails

    Returns:
        Parsed JSON data or default value
    """
    try:
        return json.loads(json_str)
    except Exception:
        return default


# Import os for environment variables
import os
