package paymentapiimp

import (
	"net/http"
	"strconv"
	"strings"

	"github.com/tech/sendico/pkg/api/http/response"
	"github.com/tech/sendico/pkg/merrors"
	"github.com/tech/sendico/pkg/model"
	"github.com/tech/sendico/pkg/mutil/mzap"
	paginationv1 "github.com/tech/sendico/pkg/proto/common/pagination/v1"
	orchestratorv1 "github.com/tech/sendico/pkg/proto/payments/orchestrator/v1"
	"github.com/tech/sendico/server/interface/api/sresponse"
	mutil "github.com/tech/sendico/server/internal/mutil/param"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.uber.org/zap"
)

const maxInt32 = int64(1<<31 - 1)

func (a *PaymentAPI) listPayments(r *http.Request, account *model.Account, token *sresponse.TokenData) http.HandlerFunc {
	orgRef, err := a.oph.GetRef(r)
	if err != nil {
		a.logger.Warn("Failed to parse organization reference for payments list", zap.Error(err), mutil.PLog(a.oph, r))
		return response.BadReference(a.logger, a.Name(), a.oph.Name(), a.oph.GetID(r), err)
	}

	ctx := r.Context()
	allowed, err := a.enf.Enforce(ctx, a.permissionRef, account.ID, orgRef, primitive.NilObjectID, model.ActionRead)
	if err != nil {
		a.logger.Warn("Failed to check payments access permissions", zap.Error(err), mutil.PLog(a.oph, r))
		return response.Auto(a.logger, a.Name(), err)
	}
	if !allowed {
		a.logger.Debug("Access denied when listing payments", mutil.PLog(a.oph, r))
		return response.AccessDenied(a.logger, a.Name(), "payments read permission denied")
	}

	req := &orchestratorv1.ListPaymentsRequest{
		Meta: &orchestratorv1.RequestMeta{
			OrganizationRef: orgRef.Hex(),
		},
	}

	if page, err := listPaymentsPage(r); err != nil {
		return response.Auto(a.logger, a.Name(), err)
	} else if page != nil {
		req.Page = page
	}

	query := r.URL.Query()
	if sourceRef := strings.TrimSpace(query.Get("source_ref")); sourceRef != "" {
		req.SourceRef = sourceRef
	}
	if destinationRef := strings.TrimSpace(query.Get("destination_ref")); destinationRef != "" {
		req.DestinationRef = destinationRef
	}

	if states, err := parsePaymentStateFilters(r); err != nil {
		return response.Auto(a.logger, a.Name(), err)
	} else if len(states) > 0 {
		req.FilterStates = states
	}

	resp, err := a.client.ListPayments(ctx, req)
	if err != nil {
		a.logger.Warn("Failed to list payments", zap.Error(err), mzap.ObjRef("organization_ref", orgRef))
		return response.Auto(a.logger, a.Name(), err)
	}

	return sresponse.PaymentsListResponse(a.logger, resp, token)
}

func listPaymentsPage(r *http.Request) (*paginationv1.CursorPageRequest, error) {
	query := r.URL.Query()
	cursor := strings.TrimSpace(query.Get("cursor"))
	limitRaw := strings.TrimSpace(query.Get("limit"))

	var limit int64
	hasLimit := false
	if limitRaw != "" {
		parsed, err := strconv.ParseInt(limitRaw, 10, 32)
		if err != nil {
			return nil, merrors.InvalidArgument("invalid limit", "limit")
		}
		limit = parsed
		hasLimit = true
	}

	if cursor == "" && !hasLimit {
		return nil, nil
	}

	page := &paginationv1.CursorPageRequest{
		Cursor: cursor,
	}
	if hasLimit {
		if limit < 0 {
			limit = 0
		} else if limit > maxInt32 {
			limit = maxInt32
		}
		page.Limit = int32(limit)
	}

	return page, nil
}

func parsePaymentStateFilters(r *http.Request) ([]orchestratorv1.PaymentState, error) {
	query := r.URL.Query()
	values := append([]string{}, query["state"]...)
	values = append(values, query["states"]...)
	values = append(values, query["filter_states"]...)
	if len(values) == 0 {
		return nil, nil
	}

	states := make([]orchestratorv1.PaymentState, 0, len(values))
	for _, raw := range values {
		for _, part := range strings.Split(raw, ",") {
			trimmed := strings.TrimSpace(part)
			if trimmed == "" {
				continue
			}
			state, ok := paymentStateFromString(trimmed)
			if !ok {
				return nil, merrors.InvalidArgument("unsupported payment state: "+trimmed, "state")
			}
			states = append(states, state)
		}
	}

	if len(states) == 0 {
		return nil, nil
	}
	return states, nil
}

func paymentStateFromString(value string) (orchestratorv1.PaymentState, bool) {
	upper := strings.ToUpper(strings.TrimSpace(value))
	if upper == "" {
		return 0, false
	}
	if !strings.HasPrefix(upper, "PAYMENT_STATE_") {
		upper = "PAYMENT_STATE_" + upper
	}
	enumValue, ok := orchestratorv1.PaymentState_value[upper]
	if !ok {
		return 0, false
	}
	return orchestratorv1.PaymentState(enumValue), true
}
