package quote_persistence_service

import (
	"context"
	"strings"

	"github.com/tech/sendico/payments/storage/model"
	quotestorage "github.com/tech/sendico/payments/storage/quote"
	"github.com/tech/sendico/pkg/merrors"
	"go.mongodb.org/mongo-driver/v2/bson"
)

type QuotePersistenceService struct{}

func New() *QuotePersistenceService {
	return &QuotePersistenceService{}
}

func (s *QuotePersistenceService) Persist(
	ctx context.Context,
	quotesStore quotestorage.QuotesStore,
	in PersistInput,
) (*model.PaymentQuoteRecord, error) {
	if quotesStore == nil {
		return nil, merrors.InvalidArgument("quotes store is required")
	}

	record, err := s.BuildRecord(in)
	if err != nil {
		return nil, err
	}

	if err := quotesStore.Create(ctx, record); err != nil {
		return nil, err
	}
	return record, nil
}

func (s *QuotePersistenceService) BuildRecord(in PersistInput) (*model.PaymentQuoteRecord, error) {
	if in.OrganizationID == bson.NilObjectID {
		return nil, merrors.InvalidArgument("organization_id is required")
	}
	if strings.TrimSpace(in.QuoteRef) == "" {
		return nil, merrors.InvalidArgument("quote_ref is required")
	}
	if strings.TrimSpace(in.IdempotencyKey) == "" {
		return nil, merrors.InvalidArgument("idempotency_key is required")
	}
	if strings.TrimSpace(in.Hash) == "" {
		return nil, merrors.InvalidArgument("hash is required")
	}
	if in.ExpiresAt.IsZero() {
		return nil, merrors.InvalidArgument("expires_at is required")
	}
	switch in.RequestShape {
	case model.QuoteRequestShapeSingle:
		if len(in.Items) != 1 {
			return nil, merrors.InvalidArgument("single shape requires exactly one item")
		}
	case model.QuoteRequestShapeBatch:
		if len(in.Items) == 0 {
			return nil, merrors.InvalidArgument("batch shape requires at least one item")
		}
	default:
		return nil, merrors.InvalidArgument("request_shape is required")
	}

	record := &model.PaymentQuoteRecord{
		QuoteRef:       strings.TrimSpace(in.QuoteRef),
		IdempotencyKey: strings.TrimSpace(in.IdempotencyKey),
		RequestShape:   in.RequestShape,
		Hash:           strings.TrimSpace(in.Hash),
		ExpiresAt:      in.ExpiresAt,
		Items:          make([]*model.PaymentQuoteItemV2, 0, len(in.Items)),
	}
	record.SetID(bson.NewObjectID())
	record.SetOrganizationRef(in.OrganizationID)

	for idx, item := range in.Items {
		if item.Intent == nil {
			return nil, merrors.InvalidArgument("items[" + itoa(idx) + "].intent is required")
		}
		if item.Quote == nil {
			return nil, merrors.InvalidArgument("items[" + itoa(idx) + "].quote is required")
		}
		status, err := mapStatusInput(item.Status)
		if err != nil {
			return nil, merrors.InvalidArgument("items[" + itoa(idx) + "]." + err.Error())
		}
		record.Items = append(record.Items, &model.PaymentQuoteItemV2{
			Intent: item.Intent,
			Quote:  item.Quote,
			Status: status,
		})
	}
	return record, nil
}
