# Copyright 2026 Datastrato, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""ADP-specific exceptions."""

from typing import Any

from adp_sdk.types import (
    EXECUTION_FAILED,
    INTERNAL_ERROR,
    INVALID_PARAMS,
    INVALID_REQUEST,
    METHOD_NOT_FOUND,
    PARSE_ERROR,
    RESOURCE_NOT_FOUND,
    UNAUTHORIZED,
    VALIDATION_FAILED,
)


class ADPError(Exception):
    """Base exception for all ADP errors."""

    def __init__(
        self,
        message: str,
        code: int = INTERNAL_ERROR,
        data: Any | None = None,
    ) -> None:
        super().__init__(message)
        self.message = message
        self.code = code
        self.data = data

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}(code={self.code}, message={self.message!r})"


class ParseError(ADPError):
    """Invalid JSON was received."""

    def __init__(self, message: str = "Parse error", data: Any | None = None) -> None:
        super().__init__(message, PARSE_ERROR, data)


class InvalidRequestError(ADPError):
    """The JSON sent is not a valid Request object."""

    def __init__(self, message: str = "Invalid request", data: Any | None = None) -> None:
        super().__init__(message, INVALID_REQUEST, data)


class MethodNotFoundError(ADPError):
    """The method does not exist or is not available."""

    def __init__(self, message: str = "Method not found", data: Any | None = None) -> None:
        super().__init__(message, METHOD_NOT_FOUND, data)


class InvalidParamsError(ADPError):
    """Invalid method parameter(s)."""

    def __init__(self, message: str = "Invalid params", data: Any | None = None) -> None:
        super().__init__(message, INVALID_PARAMS, data)


class InternalError(ADPError):
    """Internal JSON-RPC error."""

    def __init__(self, message: str = "Internal error", data: Any | None = None) -> None:
        super().__init__(message, INTERNAL_ERROR, data)


class ResourceNotFoundError(ADPError):
    """The requested resource was not found."""

    def __init__(self, message: str = "Resource not found", data: Any | None = None) -> None:
        super().__init__(message, RESOURCE_NOT_FOUND, data)


class ValidationFailedError(ADPError):
    """Intent validation failed."""

    def __init__(self, message: str = "Validation failed", data: Any | None = None) -> None:
        super().__init__(message, VALIDATION_FAILED, data)


class UnauthorizedError(ADPError):
    """The request is not authorized."""

    def __init__(self, message: str = "Unauthorized", data: Any | None = None) -> None:
        super().__init__(message, UNAUTHORIZED, data)


class ExecutionFailedError(ADPError):
    """Intent execution failed."""

    def __init__(self, message: str = "Execution failed", data: Any | None = None) -> None:
        super().__init__(message, EXECUTION_FAILED, data)


_ERROR_CODE_MAP: dict[int, type[ADPError]] = {
    PARSE_ERROR: ParseError,
    INVALID_REQUEST: InvalidRequestError,
    METHOD_NOT_FOUND: MethodNotFoundError,
    INVALID_PARAMS: InvalidParamsError,
    INTERNAL_ERROR: InternalError,
    RESOURCE_NOT_FOUND: ResourceNotFoundError,
    VALIDATION_FAILED: ValidationFailedError,
    UNAUTHORIZED: UnauthorizedError,
    EXECUTION_FAILED: ExecutionFailedError,
}


def get_exception_for_error_code(
    code: int,
    message: str,
    data: Any | None = None,
) -> ADPError:
    """Create an appropriate ADPError subclass for the given error code.

    Args:
        code: The JSON-RPC error code.
        message: The error message.
        data: Additional error data.

    Returns:
        An ADPError subclass instance matching the error code,
        or a generic ADPError if the code is not recognized.
    """
    exc_cls = _ERROR_CODE_MAP.get(code)
    if exc_cls is None:
        return ADPError(message=message, code=code, data=data)
    return exc_cls(message=message, data=data)
