package errors

import (
	"errors"
	"fmt"
)

// ErrorType представляет тип ошибки
type ErrorType string

const (
	ErrorTypeInvalid      ErrorType = "INVALID"
	ErrorTypeNotFound     ErrorType = "NOT_FOUND"
	ErrorTypeConflict     ErrorType = "CONFLICT"
	ErrorTypeInternal     ErrorType = "INTERNAL"
	ErrorTypeUnauthorized ErrorType = "UNAUTHORIZED"
	ErrorTypeForbidden    ErrorType = "FORBIDDEN"
)

// AppError - кастомная ошибка приложения
type AppError struct {
	Type    ErrorType `json:"type"`
	Message string    `json:"message"`
	Code    int       `json:"code,omitempty"`
	Err     error     `json:"-"`
}

// Error реализует интерфейс error
func (e *AppError) Error() string {
	if e.Err != nil {
		return fmt.Sprintf("%s: %v", e.Message, e.Err)
	}
	return e.Message
}

// Unwrap для поддержки errors.Unwrap
func (e *AppError) Unwrap() error {
	return e.Err
}

// WithCode добавляет HTTP код к ошибке
func (e *AppError) WithCode(code int) *AppError {
	e.Code = code
	return e
}

// Wrap оборачивает существующую ошибку
func (e *AppError) Wrap(err error) *AppError {
	e.Err = err
	return e
}

// Конструкторы ошибок
func NewInvalidError(message string) *AppError {
	return &AppError{
		Type:    ErrorTypeInvalid,
		Message: message,
		Code:    400,
	}
}

func NewNotFoundError(message string) *AppError {
	return &AppError{
		Type:    ErrorTypeNotFound,
		Message: message,
		Code:    404,
	}
}

func NewInternalError(message string) *AppError {
	return &AppError{
		Type:    ErrorTypeInternal,
		Message: message,
		Code:    500,
	}
}

func NewConflictError(message string) *AppError {
	return &AppError{
		Type:    ErrorTypeConflict,
		Message: message,
		Code:    409,
	}
}

// Вспомогательные функции для проверки типов ошибок
func IsNotFound(err error) bool {
	var appErr *AppError
	if errors.As(err, &appErr) {
		return appErr.Type == ErrorTypeNotFound
	}
	return false
}

func IsInvalid(err error) bool {
	var appErr *AppError
	if errors.As(err, &appErr) {
		return appErr.Type == ErrorTypeInvalid
	}
	return false
}

func IsConflict(err error) bool {
	var appErr *AppError
	if errors.As(err, &appErr) {
		return appErr.Type == ErrorTypeConflict
	}
	return false
}

// Wrap оборачивает ошибку с сообщением
func Wrap(err error, message string) error {
	return fmt.Errorf("%s: %w", message, err)
}

func Wrapf(err error, format string, args ...interface{}) error {
	return fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err)
}
