package middleware

import (
	"context"

	"git.perx.ru/perxis/perxis-go/id"
	"git.perx.ru/perxis/perxis-go/pkg/collections"
	"git.perx.ru/perxis/perxis-go/pkg/schema"
	logzap "git.perx.ru/perxis/perxis-go/zap"
	"go.uber.org/zap"
)

type loggingMiddleware struct {
	logger *zap.Logger
	next   collections.Collections
}

func LoggingMiddleware(logger *zap.Logger) Middleware {
	return func(next collections.Collections) collections.Collections {
		return &loggingMiddleware{
			next:   next,
			logger: logger.With(logzap.Component("Collections")),
		}
	}
}

func (m *loggingMiddleware) Create(ctx context.Context, collection *collections.Collection) (created *collections.Collection, err error) {
	logger := m.logger.With(
		logzap.CallerFromContext(ctx),
		logzap.Event(collections.EventCollectionCreate),
	)

	created, err = m.next.Create(ctx, collection)
	if err != nil {
		logger.Error("Failed to create", zap.Error(err), logzap.Object(collection), logzap.Channels(logzap.Userlog, logzap.Syslog))
		return
	}

	logger.Info("Collection created", logzap.Object(created), logzap.Channels(logzap.Userlog))
	return created, err
}

func (m *loggingMiddleware) Delete(ctx context.Context, spaceId string, envId string, collectionId string) (err error) {
	logger := m.logger.With(
		logzap.CallerFromContext(ctx),
		logzap.Event(collections.EventCollectionDelete),
		logzap.Object(id.NewCollectionId(spaceId, envId, collectionId)),
	)

	err = m.next.Delete(ctx, spaceId, envId, collectionId)
	if err != nil {
		logger.Error("Failed to delete", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
		return
	}

	logger.Info("Collection deleted", logzap.Channels(logzap.Userlog))
	return err
}

func (m *loggingMiddleware) Get(ctx context.Context, spaceId string, envId string, collectionId string, options ...*collections.GetOptions) (collection *collections.Collection, err error) {
	logger := m.logger.With(
		logzap.CallerFromContext(ctx),
	)

	collection, err = m.next.Get(ctx, spaceId, envId, collectionId, options...)
	if err != nil {
		logger.Error("Failed to get", zap.Error(err))
		return
	}

	return collection, err
}

func (m *loggingMiddleware) List(ctx context.Context, spaceId string, envId string, filter *collections.Filter) (collections []*collections.Collection, err error) {
	logger := m.logger.With(
		logzap.CallerFromContext(ctx),
	)

	collections, err = m.next.List(ctx, spaceId, envId, filter)
	if err != nil {
		logger.Error("Failed to list", zap.Error(err))
		return
	}

	return collections, err
}

func (m *loggingMiddleware) SetSchema(ctx context.Context, spaceId string, envId string, collectionId string, schema *schema.Schema) (err error) {
	logger := m.logger.With(
		logzap.CallerFromContext(ctx),
		logzap.Event(collections.EventCollectionSetSchema),
		logzap.Object(id.NewCollectionId(spaceId, envId, collectionId)),
	)

	err = m.next.SetSchema(ctx, spaceId, envId, collectionId, schema)
	if err != nil {
		logger.Error("Failed to set schema", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
		return
	}

	logger.Info("Set collection's schema", logzap.Channels(logzap.Userlog))
	return err
}

func (m *loggingMiddleware) SetState(ctx context.Context, spaceId string, envId string, collectionId string, state *collections.StateInfo) (err error) {
	logger := m.logger.With(
		logzap.CallerFromContext(ctx),
	)

	err = m.next.SetState(ctx, spaceId, envId, collectionId, state)
	if err != nil {
		logger.Error("Failed to set state", zap.Error(err))
		return
	}

	logger.Info("Set collection's state", logzap.Channels(logzap.Userlog))
	return err
}

func (m *loggingMiddleware) Update(ctx context.Context, coll *collections.Collection) (err error) {
	logger := m.logger.With(
		logzap.CallerFromContext(ctx),
		logzap.Event(collections.EventCollectionUpdate),
		logzap.Object(coll),
	)

	err = m.next.Update(ctx, coll)
	if err != nil {
		logger.Error("Failed to update", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
		return
	}

	logger.Info("Collection updated", logzap.Channels(logzap.Userlog))
	return err
}