from typing import Any, Dict, List, Optional

from django.http import JsonResponse

from constants import cowenums


class ErrorVO:
    def __init__(self, service: Optional[str] = None, component: Optional[str] = None, error_type: Optional[cowenums.ErrorType] = None, specific_issue: Optional[str] = None, retryable: bool = False, criticality: Optional[str] = None, message: Optional[str] = None, description: Optional[str] = None, error_details: Optional[List["ErrorDetailVO"]] = None, debug_id: Optional[str] = None):
        self.service: Optional[str] = service
        self.component: Optional[str] = component
        self.error_type: Optional[cowenums.ErrorType] = error_type
        self.specific_issue: Optional[str] = specific_issue
        self.retryable: bool = retryable

        self.criticality: Optional[str] = criticality
        self.message: Optional[str] = message
        self.description: Optional[str] = description
        self.error_details: List["ErrorDetailVO"] = error_details or []
        self.debug_id: Optional[str] = debug_id

    def __str__(self) -> str:
        error_code_slice = [
            self.service or "NA",
            self.component or "NA",
            self.error_type or "NA",
            self.specific_issue or "NA",
            "Y" if self.retryable else "N",
        ]
        error_code_str = "-".join(error_code_slice)
        return f"{error_code_str} | Criticality: {self.criticality} | Message: {self.message}"

    def to_dict(self) -> Dict[str, Any]:
        return {
            "service": self.service,
            "component": self.component,
            "errorType": self.error_type,
            "specificIssue": self.specific_issue,
            "retryable": self.retryable,
            "Criticality": self.criticality,
            "Message": self.message,
            "Description": self.description,
            "ErrorDetails": [detail.to_dict() for detail in self.error_details],
            "DebugID": self.debug_id,
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "ErrorVO":
        error_details = [ErrorDetailVO.from_dict(
            detail) for detail in data.get("errorDetails", data.get("ErrorDetails", []))]
        return cls(
            service=data.get("service", None),
            component=data.get("component", None),
            error_type=data.get("errorType", None),
            specific_issue=data.get("specificIssue", None),
            retryable=data.get("retryable", False),
            criticality=data.get("Criticality", None),
            message=data.get("Message", None),
            description=data.get("Description", None),
            error_details=error_details,
            debug_id=data.get("DebugID", None),
        )


class ErrorDetailVO:
    def __init__(self, field: Optional[str] = None, value: Optional[Any] = None, location: Optional[str] = None, issue: Optional[str] = None):
        self.field: Optional[str] = field
        self.value: Optional[Any] = value
        self.location: Optional[str] = location
        self.issue: Optional[str] = issue

    def to_dict(self) -> Dict[str, Any]:
        return {
            "Field": self.field,
            "Value": self.value,
            "Location": self.location,
            "Issue": self.issue,
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "ErrorDetailVO":
        return cls(
            field=data.get("Field", None),
            value=data.get("Value", None),
            location=data.get("Location", None),
            issue=data.get("Issue", None),
        )


class ErrorResponseVO:
    def __init__(self, error=None, status_code=None):
        self.error = error or ErrorVO()
        self.status_code = status_code

    def to_dict(self) -> Dict[str, Any]:
        return {
            "Error": self.error,
            "StatusCode": self.status_code,
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "ErrorDetailVO":
        return cls(
            field=data.get("Error", None),
            value=data.get("StatusCode", None),
        )


class CCowExceptionVO(Exception):
    def __init__(self, message: str = None, status_code: int = None, error_vo: "ErrorVO" = None, other_info: dict = None):
        # super().__init__(message)
        self.message = message
        self.status_code = status_code
        self.error_vo = error_vo
        self.other_info = other_info

    def to_dict(self) -> dict:
        if self.error_vo:
            if not self.error_vo.message and self.message:
                self.error_vo.message = self.message
        return self.error_vo.to_dict()

    def __str__(self):
        base_message = super().__str__()
        if self.status_code:
            base_message += f" (Status code: {self.status_code})"
        if self.error_vo and self.error_vo.description:
            base_message += f" - Details: {self.error_vo.description}"
        if self.error_vo and self.error_vo.message:
            base_message += f" - Message: {self.error_vo.message}"

        return base_message

    # def to_json_response(self) -> JsonResponse:
    #     return JsonResponse(remove_none_values(self.error_vo.to_dict()), safe=False, status=self.status_code)

    def to_json_response(self) -> dict:
        """Return error as a dictionary suitable for JSON serialization"""
        return {
            "error": remove_none_values(self.error_vo.to_dict()),
            "status_code": self.status_code
        }


def remove_none_values(d):
    if isinstance(d, dict):
        return {k: remove_none_values(v) for k, v in d.items() if v is not None and v != {} and v != []}
    elif isinstance(d, list):
        return [remove_none_values(i) for i in d if i is not None and i != [] and i != {}]
    else:
        return d
