package derrs

import (
	"errors"
	"fmt"
	"strings"
)

type Code int

const (
	CodeUnknown                        Code = 1
	CodeIncorrectParams                Code = 2
	CodeExists                         Code = 3
	CodeCommandNotFound                Code = 4
	CodeNotImplemented                 Code = 5
	CodeConnectionLost                 Code = 7
	CodeUnknownHeaderType              Code = 8
	CodeDataNotFound                   Code = 9
	CodeRead                           Code = 10
	CodeReadedDataSizeNotEqualDataSize Code = 11
	CodeEventSend                      Code = 12
	CodeEventExecution                 Code = 13
	CodeContextDone                    Code = 14
)

var Join = errors.Join

type Error struct {
	code    Code
	message string
	details string
}

func (e *Error) Error() string {
	var sb strings.Builder
	if len(e.message) > 0 {
		sb.WriteString(e.message)
	} else {
		sb.WriteString(fmt.Sprintf("%d", e.code))
	}
	if len(e.details) > 0 {
		sb.WriteString(": ")
		sb.WriteString(e.details)
	}
	return sb.String()
}

func NewError(code Code, message string, details ...string) *Error {
	detail := ""
	if len(details) > 0 {
		detail = details[0]
	}
	return &Error{
		code:    code,
		message: message,
		details: detail,
	}
}

func NewUnknownError(details ...string) *Error {
	return NewError(CodeUnknown, "unknown error", details...)
}

func NewIncorrectParamsError(details ...string) *Error {
	return NewError(CodeIncorrectParams, "incorrect parameters", details...)
}

func NewExistError(details ...string) *Error {
	return NewError(CodeExists, "entity already exists", details...)
}

func NewCommandNotFoundError(details ...string) *Error {
	return NewError(CodeCommandNotFound, "command not found", details...)
}

func NewNotImplementedError(details ...string) *Error {
	return NewError(CodeNotImplemented, "not implemented", details...)
}

func NewConnectionLostError(details ...string) *Error {
	return NewError(CodeConnectionLost, "connection lost", details...)
}

func NewUnknownHeaderTypeError(details ...string) *Error {
	return NewError(CodeUnknownHeaderType, "unknown header type", details...)
}

func NewDataNotFoundError(details ...string) *Error {
	return NewError(CodeDataNotFound, "data not found", details...)
}

func NewReadError(details ...string) *Error {
	return NewError(CodeRead, "read error", details...)
}

func NewReadedDataSizeNotEqualDataSizeError(details ...string) *Error {
	return NewError(CodeReadedDataSizeNotEqualDataSize, "readed data size not equal data size", details...)
}

func NewEventSendError(details ...string) *Error {
	return NewError(CodeEventSend, "event send error", details...)
}

func NewEventExecutionError(details ...string) *Error {
	return NewError(CodeEventExecution, "event execution error", details...)
}

func NewContextDoneError(details ...string) *Error {
	return NewError(CodeContextDone, "context done", details...)
}
