package customerrs

import (
	"fmt"
	"reflect"

	"github.com/openconfig/ygot/ygot"
)

// NilClientError implements the Error interface and is called if a GNMI Client is nil.
type NilClientError struct {
}

func (e *NilClientError) Error() string {
	return fmt.Sprint("client cannot be nil")
}

// NilError implements the Error interface and is called if a struct is nil.
type NilError struct {
}

func (e *NilError) Error() string {
	return fmt.Sprint("struct cannot be nil")
}

// AlreadyExistsError implements the Error interface and is called if a specific ID
// of a storable item already exists.
type AlreadyExistsError struct {
	Item interface{}
}

func (e *AlreadyExistsError) Error() string {
	return fmt.Sprintf("%T %v already exists", e.Item, e.Item)
}

// InvalidUUIDError implements the Error interface and is called if a UUID is not valid.
type InvalidUUIDError struct {
	NetworkElementName string
}

func (e *InvalidUUIDError) Error() string {
	return fmt.Sprint("UUID not valid")
}

// InvalidTypeAssertionError implements the Error interface and is called if the
// type of a storable item does not correspond to the expected type.
type InvalidTypeAssertionError struct {
	Value interface{}
	Type  interface{}
}

func (e InvalidTypeAssertionError) Error() string {
	return fmt.Sprintf("%v does not implement %v", reflect.TypeOf(e.Value).Elem(), reflect.TypeOf(e.Type).Elem())
}

// UnsupportedPathError implements the Error interface and is called if the
// given path is not supported.
type UnsupportedPathError struct {
	Path interface{}
}

func (e UnsupportedPathError) Error() string {
	return fmt.Sprintf("path %v is not supported", e.Path)
}

// PathNotFoundError implements the Error interface and is called if the
// given path is not supported.
type PathNotFoundError struct {
	Path interface{}
	Err  error
}

func (e PathNotFoundError) Error() string {
	return fmt.Sprintf("path %v not found: %v", e.Path, e.Err)
}

// NotYetImplementedError implements the Error interface and is called if a function
// is not implemented yet.
type NotYetImplementedError struct{}

func (e NotYetImplementedError) Error() string {
	return fmt.Sprint("function not yet implemented")
}

// InvalidParametersError implements the Error interface and is called if the wrong
// or no parameters have been provided.
type InvalidParametersError struct {
	Func  interface{}
	Param interface{}
}

func (e InvalidParametersError) Error() string {
	return fmt.Sprintf("invalid parameters for %v: %v", e.Func, e.Param)
}

// InvalidTransportOptionsError implements the Error interface and is called if the
// wrong TransportOptions have been provided.
type InvalidTransportOptionsError struct {
	Opt interface{}
}

func (e InvalidTransportOptionsError) Error() string {
	return fmt.Sprintf("invalid transport options: %v", reflect.TypeOf(e.Opt))
}

// OperationNotSupportedError implements the Error interface and is called if the
// wrong Operation has been provided.
type OperationNotSupportedError struct {
	Op interface{}
}

func (e OperationNotSupportedError) Error() string {
	return fmt.Sprintf("transport operation not supported: %v", reflect.TypeOf(e.Op))
}

// UnsupportedSbiTypeError implements the Error interface and is called if the
// wrong Type for a SBI has been provided.
type UnsupportedSbiTypeError struct {
	Type interface{}
}

func (e UnsupportedSbiTypeError) Error() string {
	return fmt.Sprintf("SBI type not supported: %v", e.Type)
}

// PluginVersionError implements the Error interface and is called if the Version
// of a Plugin is older than a Plugin in use.
type PluginVersionError struct {
	PlugID, ProvidedVer, UsedVer string
}

func (e PluginVersionError) Error() string {
	return fmt.Sprintf("Version of Plugin: %s is older than the one in use. Provided: %s, in use: %s", e.PlugID, e.ProvidedVer, e.UsedVer)
}

// CombinedErrListError implements the Error interface and is called if a slice of errors
// should be returned. The slice of errors is combined into a single error
// message and returned.
type CombinedErrListError struct {
	Errors []error
}

func (e CombinedErrListError) Error() string {
	combinedErrString := "Errors found:"
	for i, err := range e.Errors {
		errString := fmt.Sprintf("\n %v. %v", i+1, err)
		combinedErrString = combinedErrString + errString
	}
	return combinedErrString
}

// TypeNotSupportedError implements the Error interface and is called if the
// wrong Type has been provided.
type TypeNotSupportedError struct {
	Type interface{}
}

func (e TypeNotSupportedError) Error() string {
	return fmt.Sprintf("type not supported: %v", reflect.TypeOf(e.Type))
}

// CouldNotMarshallError implements Error interface and is called if a
// database response can not be parsed.
type CouldNotMarshallError struct {
	Identifier any
	Type       any
	Err        error
}

func (e CouldNotMarshallError) Error() string {
	return fmt.Sprintf("could not marshall Identifier: %v of Type: %T, Internal error: %v", e.Identifier, e.Type, e.Err)
}

// CouldNotUpdateError implements the Error interface and is called if a
// stored item can not be updated.
type CouldNotUpdateError struct {
	Identifier any
	Type       any
	Err        error
}

func (e CouldNotUpdateError) Error() string {
	return fmt.Sprintf("could not update Identifier: %v of Type: %T, Internal error: %v", e.Identifier, e.Type, e.Err)
}

// CouldNotFindError implements the Error interface and is called if a
// stored item can not be found.
type CouldNotFindError struct {
	ID   any
	Name string
}

func (e CouldNotFindError) Error() string {
	return fmt.Sprintf("ID: %v or Name: %v not found", e.ID, e.Name)
}

// CouldNotCreateError implements the Error interface and is called if a
// stored item can not be found.
type CouldNotCreateError struct {
	Identifier any
	Type       any
	Err        error
}

func (e CouldNotCreateError) Error() string {
	return fmt.Sprintf("could not create Identifier: %v of Type: %T, Internal error: %v", e.Identifier, e.Type, e.Err)
}

// CouldNotDeleteError implements the Error interface and is called if a
// stored item can not be deleted.
type CouldNotDeleteError struct {
	Identifier any
	Type       any
	Err        error
}

func (e CouldNotDeleteError) Error() string {
	return fmt.Sprintf("could not delete Identifier: %v of Type: %T, Internal error: %v", e.Identifier, e.Type, e.Err)
}

// NoNewChangesError implements the Error interface and is called if a the
// gNMI-Notification created from ygot.Diff does not contain any `updates` or
// `deletes`.
type NoNewChangesError struct {
	Original ygot.GoStruct
	Modified ygot.GoStruct
}

func (e NoNewChangesError) Error() string {
	return fmt.Sprintf("There are no changes between %v and %v", e.Original, e.Modified)
}

// AMQPInitFailError implements the Error interface and is called if there is any issue related to
// the setup of the event management.
type AMQPInitFailError struct {
	Action string
	Err    error
}

func (e AMQPInitFailError) Error() string {
	return fmt.Sprintf("Action: %s, Internal error: %v", e.Action, e.Err)
}

// AMQPMessageFailError implements the Error interface and is called if there is any issue with sending
// or receiving messages.
type AMQPMessageFailError struct {
	Action string
	Err    error
}

func (e AMQPMessageFailError) Error() string {
	return fmt.Sprintf("Action: %s, Internal error: %v", e.Action, e.Err)
}

// SubscribeResponseError implements the Error interface and is called if there is an issue during a ongoing
// gNMI Subscription.
type SubscribeResponseError struct {
	PndID              string
	NetworkElementID   string
	NetworkElementName string
	Err                string
}

func (e SubscribeResponseError) Error() string {
	return fmt.Sprintf("Subscribe failed, PndID: %s, NetworkElementID: %s, NetworkElementName: %s, Internal error: %s", e.PndID, e.NetworkElementID, e.NetworkElementName, e.Err)
}

// SubscribeSyncResponseError implements the Error interface and is called if there is an issue syncing a
// gNMI Subscription.
type SubscribeSyncResponseError struct {
	PndID              string
	NetworkElementID   string
	NetworkElementName string
}

func (e SubscribeSyncResponseError) Error() string {
	return fmt.Sprintf("Sync failed, PndID: %s, NetworkElementID: %s, NetworkElementName: %s", e.PndID, e.NetworkElementID, e.NetworkElementName)
}