from typing import Any, Type, TypeVar
from pydantic import BaseModel, Field
from datetime import datetime, timezone

SchemaT = TypeVar("SchemaT", bound=BaseModel)

class Error(BaseModel):
    code: str = Field(..., description="Código de erro categórico (ex.: VALIDATION_ERROR, AUTH_ERROR)")
    message: str = Field(..., description="Mensagem resumida do erro")
    details: Any | None = Field(None, description="Detalhes adicionais do erro")

class ErrorResponse(BaseModel):
    error: Error = Field(..., description="Objeto com informações do erro")
    requestId: str | None = Field(None, description="Identificador da requisição para rastreio")
    timestamp: str = Field(..., description="Timestamp ISO8601 da ocorrência do erro")

class SuccessResponse(BaseModel):
    message: str | None = Field(None, description="Mensagem amigável sobre o resultado")
    data: Any | None = Field(None, description="Carga útil com o resultado da operação")
    requestId: str | None = Field(None, description="Identificador da requisição para rastreio")
    timestamp: str = Field(..., description="Timestamp ISO8601 do processamento")

def build_success_payload(
    data: Any | None = None,
    *,
    message: str | None = None,
    request_id: str | None = None,
    timestamp_iso: str | None = None
) -> dict[str, Any]:
    """
    Constrói um payload de sucesso padronizado para respostas de API.
    - `data`: carga útil da resposta.
    - `message`: mensagem amigável.
    - `request_id`: identificador da requisição.
    - `timestamp_iso`: timestamp opcional (ISO8601).
    """
    ts = timestamp_iso or datetime.now(timezone.utc).isoformat()
    return SuccessResponse(
        message=message,
        data=data,
        requestId=request_id,
        timestamp=ts
    ).model_dump(by_alias=True, exclude_unset=True)


def build_error_payload(
    code: str,
    message: str,
    details: Any | None = None,
    *,
    request_id: str | None = None,
    timestamp_iso: str | None = None
) -> dict[str, Any]:
    """
    Constrói um payload de erro padronizado para respostas de API.
    - `code`: código do erro (ex.: VALIDATION_ERROR)
    - `message`: mensagem resumida
    - `details`: detalhes adicionais
    - `request_id`: identificador da requisição
    - `timestamp_iso`: timestamp opcional (ISO8601)
    """
    ts = timestamp_iso or datetime.now(timezone.utc).isoformat()
    error = Error(code=code, message=message, details=details)
    return ErrorResponse(
        error=error,
        requestId=request_id,
        timestamp=ts
    ).model_dump(by_alias=True, exclude_unset=True)


def to_schema(schema_cls: Type[SchemaT], obj: Any) -> SchemaT:
    """
    Converte um objeto ORM ou dict para o schema Pydantic correspondente.
    Funciona para:
        - dict
        - ORM com from_attributes=True
        - objetos normais (usa __dict__)
    """
    import logging
    logger = logging.getLogger(__name__)
    
    if isinstance(obj, dict):
        try:
            return schema_cls(**obj)
        except Exception as e:
            logger.error(f"Erro ao converter dict para schema: {e}")
            raise

    model_validate = getattr(schema_cls, "model_validate", None)
    if callable(model_validate):
        try:
            return model_validate(obj)
        except Exception as e:
            logger.debug(f"model_validate falhou, tentando outros métodos: {e}")

    from_orm = getattr(schema_cls, "from_orm", None)
    if callable(from_orm):
        try:
            return from_orm(obj)
        except Exception as e:
            logger.debug(f"from_orm falhou, tentando __dict__: {e}")

    data = getattr(obj, "__dict__", None)
    if isinstance(data, dict):
        cleaned = {k: v for k, v in data.items() if not k.startswith("_sa_")}
        try:
            return schema_cls(**cleaned)
        except Exception as e:
            logger.error(
                f"Erro ao converter objeto para schema {schema_cls.__name__}:\n"
                f"  Erro: {e}\n"
                f"  Campos problemáticos podem estar nos dados: {list(cleaned.keys())[:10]}..."
            )
            raise

    try:
        return schema_cls(**(obj or {}))
    except Exception as e:
        logger.error(f"Erro ao criar schema com dados vazios: {e}")
        raise

def to_schema_dict(schema_cls: Type[SchemaT], obj: Any) -> dict[str, Any]:
    """
    Converte um objeto para schema Pydantic e retorna como dict pronto para API.
    Usa `by_alias=True` e `exclude_unset=True` para manter os aliases corretos.
    """
    instance = to_schema(schema_cls, obj)
    return instance.model_dump(by_alias=True, exclude_unset=True)
