﻿# edited by glg
import re
from typing import Any, Dict


class ErrorEnvelopeService:
    """
    Utilitas standar payload error lintas service.
    Kontrak minimum: status, error_code, reason, trace_id.
    """

    @staticmethod
    def _safe_text(value: Any, default: str = "") -> str:
        if value is None:
            return str(default or "")
        text = str(value).strip()
        return text if text else str(default or "")

    @classmethod
    def reason_to_error_code(cls, reason: str, prefix: str = "APP") -> str:
        raw = cls._safe_text(reason, "unknown_error").lower()
        normalized = re.sub(r"[^a-z0-9]+", "_", raw).strip("_").upper()
        if not normalized:
            normalized = "UNKNOWN_ERROR"
        if normalized[0].isdigit():
            normalized = f"{cls._safe_text(prefix, 'APP').upper()}_{normalized}"
        return normalized

    @classmethod
    def build_error(
        cls,
        *,
        reason: str = "",
        error_code: str = "",
        trace_id: str = "",
        message: str = "",
        status: int = 0,
        payload: Dict[str, Any] = None,
        code_prefix: str = "APP",
    ) -> Dict[str, Any]:
        data: Dict[str, Any] = dict(payload or {})

        status_value = status if status is not None else data.get("status", 0)
        try:
            status_int = int(status_value)
        except (TypeError, ValueError):
            status_int = 0
        data["status"] = status_int

        resolved_reason = cls._safe_text(
            data.get("reason")
            or reason
            or data.get("error")
            or data.get("message"),
            "unknown_error",
        )
        data["reason"] = resolved_reason

        resolved_error_code = cls._safe_text(error_code or data.get("error_code"), "")
        if not resolved_error_code:
            resolved_error_code = cls.reason_to_error_code(resolved_reason, prefix=code_prefix)
        data["error_code"] = resolved_error_code

        resolved_trace_id = cls._safe_text(data.get("trace_id") or trace_id, "")
        if resolved_trace_id:
            data["trace_id"] = resolved_trace_id

        resolved_message = cls._safe_text(message or data.get("message"), "")
        if resolved_message:
            data["message"] = resolved_message

        return data

    @classmethod
    def normalize_failure_payload(
        cls,
        payload: Dict[str, Any],
        *,
        fallback_reason: str = "",
        fallback_error_code: str = "",
        trace_id: str = "",
        code_prefix: str = "APP",
    ) -> Dict[str, Any]:
        data = dict(payload or {})
        if "status" not in data:
            data["status"] = 0
        fallback_reason_text = cls._safe_text(fallback_reason, "")
        existing_reason = cls._safe_text(data.get("reason"), "")
        existing_error = cls._safe_text(data.get("error"), "")
        existing_message = cls._safe_text(data.get("message"), "")
        if existing_reason or existing_error or existing_message:
            fallback_reason_text = ""
        return cls.build_error(
            payload=data,
            reason=fallback_reason_text,
            error_code=fallback_error_code,
            trace_id=trace_id,
            status=data.get("status", 0),
            code_prefix=code_prefix,
        )
