package quote_idempotency_service

import (
	"context"
	"errors"
	"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 ReuseInput struct {
	OrganizationID bson.ObjectID
	IdempotencyKey string
	Fingerprint    string
	Shape          QuoteShape
}

type CreateInput struct {
	Record *model.PaymentQuoteRecord
	Reuse  ReuseInput
}

func (s *QuoteIdempotencyService) TryReuse(
	ctx context.Context,
	quotesStore quotestorage.QuotesStore,
	in ReuseInput,
) (*model.PaymentQuoteRecord, bool, error) {

	if quotesStore == nil {
		return nil, false, merrors.InvalidArgument("quotes store is required")
	}
	if in.OrganizationID == bson.NilObjectID {
		return nil, false, merrors.InvalidArgument("organization_id is required")
	}

	idempotencyKey := strings.TrimSpace(in.IdempotencyKey)
	if idempotencyKey == "" {
		return nil, false, merrors.InvalidArgument("idempotency_key is required")
	}
	fingerprint := strings.TrimSpace(in.Fingerprint)
	if fingerprint == "" {
		return nil, false, merrors.InvalidArgument("fingerprint is required")
	}

	record, err := quotesStore.GetByIdempotencyKey(ctx, in.OrganizationID, idempotencyKey)
	if err != nil {
		if errors.Is(err, quotestorage.ErrQuoteNotFound) || errors.Is(err, merrors.ErrNoData) {
			return nil, false, nil
		}
		return nil, false, err
	}
	if record == nil {
		return nil, false, nil
	}

	if !shapeMatches(record, in.Shape) {
		return nil, false, ErrIdempotencyShapeMismatch
	}
	if strings.TrimSpace(record.Hash) != fingerprint {
		return nil, false, ErrIdempotencyParamMismatch
	}

	return record, true, nil
}

func (s *QuoteIdempotencyService) CreateOrReuse(
	ctx context.Context,
	quotesStore quotestorage.QuotesStore,
	in CreateInput,
) (*model.PaymentQuoteRecord, bool, error) {

	if quotesStore == nil {
		return nil, false, merrors.InvalidArgument("quotes store is required")
	}
	if in.Record == nil {
		return nil, false, merrors.InvalidArgument("record is required")
	}

	if err := quotesStore.Create(ctx, in.Record); err != nil {
		if !errors.Is(err, quotestorage.ErrDuplicateQuote) {
			return nil, false, err
		}

		record, reused, reuseErr := s.TryReuse(ctx, quotesStore, in.Reuse)
		if reuseErr != nil {
			return nil, false, reuseErr
		}
		if reused {
			return record, true, nil
		}
		return nil, false, err
	}

	return in.Record, false, nil
}

func shapeMatches(record *model.PaymentQuoteRecord, shape QuoteShape) bool {
	if record == nil {
		return false
	}

	switch shape {
	case QuoteShapeSingle:
		return record.RequestShape == model.QuoteRequestShapeSingle && len(record.Items) == 1
	case QuoteShapeBatch:
		return record.RequestShape == model.QuoteRequestShapeBatch && len(record.Items) > 0
	default:
		return record.RequestShape != model.QuoteRequestShapeUnspecified
	}
}
