package stream_events_update

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/google/uuid"
	rpcprotocol "go.avito.ru/gl/rpc-protocol"

	"go.avito.ru/bill/service-billing-processing/internal/domain"
	"go.avito.ru/bill/service-billing-processing/internal/generated/rpc/service/dto"
	"go.avito.ru/bill/service-billing-processing/internal/repository"
	"go.avito.ru/bill/service-billing-processing/internal/repository/entity"
	"go.avito.ru/bill/service-billing-processing/pkg/utils"
)

type Handler struct {
	dao repository.DAO
}

func New(
	dao repository.DAO,
) *Handler {
	return &Handler{
		dao: dao,
	}
}

// Handle Обновит поля stream event
func (h *Handler) Handle(ctx context.Context, in *dto.StreamEventsUpdateIn, out *dto.StreamEventsUpdateOut) error {
	if utils.IsProd() {
		out.Status = dto.ResponseStatus_FAILED
		return nil
	}

	if len(in.ExternalID) == 0 {
		return rpcprotocol.NewValidationError("externalId required")
	}
	holdKey, err := domain.HoldKey{}.FromString(in.HoldKey)
	if err != nil {
		return rpcprotocol.NewValidationError(fmt.Sprintf("invalid hold key: %v", err.Error()))
	}
	kind, err := domain.Kind.Parse("", in.Kind)
	if err != nil {
		return rpcprotocol.NewValidationError(fmt.Sprintf("invalid kind: %v", err))
	}

	var groupID *uuid.UUID
	if in.GroupID != nil {
		g, err := uuid.Parse(*in.GroupID)
		if err != nil {
			return rpcprotocol.NewValidationError("invalid group id")
		}
		groupID = &g
	}
	var status domain.StatusType
	if in.Status != nil {
		v := int16(*in.Status)
		status, err = status.FromValue(&v)
		if err != nil {
			return rpcprotocol.NewValidationError("invalid status")
		}
	}

	var context domain.EventContext
	if in.Context != nil {
		context, err = h.parseContext(*in.Context)
		if err != nil {
			return rpcprotocol.NewValidationError("invalid context")
		}
	}

	streamEventQuery := h.dao.NewStreamEventQuery(ctx)
	events, err := streamEventQuery.GetByPK(entity.StreamEventPK{
		ExternalID: in.ExternalID,
		HoldKey:    holdKey.String(),
		Kind:       kind.String(),
	})
	if err != nil {
		return rpcprotocol.NewValidationError(fmt.Sprintf("get event: %v", err.Error()))
	}

	if len(events) == 0 {
		out.Status = dto.ResponseStatus_FAILED
		return nil
	}
	event, err := events[0].ToDomainEvent()
	if err != nil {
		return rpcprotocol.NewValidationError(fmt.Sprintf("convert event: %v", err.Error()))
	}

	if in.OperationID != nil {
		event.OperationID = in.OperationID
	}
	if groupID != nil {
		event.GroupID = groupID
	}
	if in.Status != nil {
		event.StatusID = status
	}
	if in.Rub != nil {
		event.Amount.RUB = *in.Rub
	}
	if in.Bonus != nil {
		event.Amount.Bonus = *in.Bonus
	}
	if in.Context != nil {
		event.Context = context
	}
	if in.AppleidAt != nil {
		event.AppliedAt = in.AppleidAt
	}

	updateEvent, err := entity.StreamEvent{}.FromDomainEvent(event)
	if err != nil {
		return rpcprotocol.NewValidationError(fmt.Sprintf("convert event: %v", err.Error()))
	}

	_, err = streamEventQuery.Update(updateEvent)
	if err != nil {
		return rpcprotocol.NewValidationError(fmt.Sprintf("update event: %v", err.Error()))
	}

	out.Status = dto.ResponseStatus_ACCEPTED
	return nil
}

func (h *Handler) parseContext(s string) (domain.EventContext, error) {
	type jsonContext struct {
		VerticalID        *int16     `json:"vertical_id"`
		SilenceRefundedAt *time.Time `json:"silence_refunded_at"`
	}

	var context jsonContext
	if err := json.Unmarshal([]byte(s), &context); err != nil {
		return domain.EventContext{}, err
	}
	return domain.EventContext{
		VerticalID:        context.VerticalID,
		SilenceRefundedAt: context.SilenceRefundedAt,
	}, nil
}


вынеси всю валидацию в приватный метод