package command

import (
	"context"

	"github.com/zitadel/zitadel/internal/domain"
	"github.com/zitadel/zitadel/internal/errors"
	"github.com/zitadel/zitadel/internal/eventstore"
	"github.com/zitadel/zitadel/internal/repository/feature"
)

type FeatureWriteModel[T feature.SetEventType] struct {
	eventstore.WriteModel

	feature domain.Feature

	Value T
}

func NewFeatureWriteModel[T feature.SetEventType](instanceID, resourceOwner string, feature domain.Feature) (*FeatureWriteModel[T], error) {
	wm := &FeatureWriteModel[T]{
		WriteModel: eventstore.WriteModel{
			InstanceID:    instanceID,
			ResourceOwner: resourceOwner,
		},
		feature: feature,
	}
	if wm.Value.FeatureType() != feature.Type() {
		return nil, errors.ThrowPreconditionFailed(nil, "FEAT-AS4k1", "Errors.Feature.InvalidValue")
	}
	return wm, nil
}
func (wm *FeatureWriteModel[T]) Set(ctx context.Context, value T) (event *feature.SetEvent[T], err error) {
	if wm.Value == value {
		return nil, nil
	}
	return feature.NewSetEvent[T](
		ctx,
		&feature.NewAggregate(wm.AggregateID, wm.ResourceOwner).Aggregate,
		wm.eventType(),
		value,
	), nil
}

func (wm *FeatureWriteModel[T]) Query() *eventstore.SearchQueryBuilder {
	return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
		AddQuery().
		AggregateTypes(feature.AggregateType).
		EventTypes(wm.eventType()).
		Builder()
}

func (wm *FeatureWriteModel[T]) Reduce() error {
	for _, event := range wm.Events {
		switch e := event.(type) {
		case *feature.SetEvent[T]:
			wm.Value = e.Value
		default:
			return errors.ThrowPreconditionFailed(nil, "FEAT-SDfjk", "Errors.Feature.TypeNotSupported")
		}
	}
	return wm.WriteModel.Reduce()
}

func (wm *FeatureWriteModel[T]) eventType() eventstore.EventType {
	return feature.EventTypeFromFeature(wm.feature)
}

type InstanceFeatureWriteModel[T feature.SetEventType] struct {
	FeatureWriteModel[T]
}

func NewInstanceFeatureWriteModel[T feature.SetEventType](instanceID string, feature domain.Feature) (*InstanceFeatureWriteModel[T], error) {
	wm, err := NewFeatureWriteModel[T](instanceID, instanceID, feature)
	if err != nil {
		return nil, err
	}
	return &InstanceFeatureWriteModel[T]{
		FeatureWriteModel: *wm,
	}, nil
}