package paid_order

import (
	"context"
	"gorm.io/gorm"
	"log"

	"github.com/UpmeshLTD/urus-aio/infrastructure/db/entities"
	"github.com/UpmeshLTD/urus-aio/infrastructure/utils"
	"github.com/lib/pq"
)

type Service interface {
	GetList(ctx context.Context, params *entities.GetPaidOrdersParams) ([]*entities.PaidOrder, int64, error)
	GetByCartIDs(ctx context.Context, cartIDs []string) ([]*entities.PaidOrder, error)
	GetByIDs(ctx context.Context, ids []string, workspaceID string) ([]*entities.PaidOrder, error)
	Get(ctx context.Context, cartID string, shippingGroupID *string) (*entities.PaidOrder, error)
	GetArranged(ctx context.Context, params *entities.GetArrangedPaidOrdersParams) ([]*entities.PaidOrder, error)
	GetCount(ctx context.Context, params *entities.GetPaidOrdersParams) (int64, error)

	CreateBatch(ctx context.Context, entities []*entities.PaidOrder) error
	Update(ctx context.Context, id string, entity *entities.PaidOrder) error
	UpdateIncorrectInformation(ctx context.Context, id string, incorrectInformation []string) error
	UpdatePaidOrderShipmentID(ctx context.Context, id string, shipmentID string) error
	UpdateFulfillmentID(ctx context.Context, id string, fulfillmentID *string) error
	UpdateFulfillmentIDBatch(ctx context.Context, ids []string, fulfillmentID *string) error
	UpdateBatch(ctx context.Context, ids []string, data map[string]interface{}) error
	UpdateDimension(ctx context.Context, id string, dimension entities.WeightDimension) error
	ResetAndUpdateNewFulfillmentBatch(ctx context.Context, ids []string, fulfillmentID string) error
	Delete(ctx context.Context, cartId string) error

	CancelArrangedShipPaidOrder(ctx context.Context, id string) error
}

type serviceImpl struct {
	repo Repository
}

func NewService(repo Repository) Service {
	return &serviceImpl{
		repo: repo,
	}
}

func (s serviceImpl) GetList(ctx context.Context, params *entities.GetPaidOrdersParams) ([]*entities.PaidOrder, int64, error) {
	return s.repo.GetList(ctx, params)
}

func (s serviceImpl) GetByCartIDs(ctx context.Context, cartIDs []string) ([]*entities.PaidOrder, error) {
	paidOrders, _, err := s.repo.GetList(ctx, &entities.GetPaidOrdersParams{CartIDs: cartIDs})
	return paidOrders, err
}

func (s serviceImpl) GetArranged(ctx context.Context, params *entities.GetArrangedPaidOrdersParams) ([]*entities.PaidOrder, error) {
	pos, _, err := s.repo.GetList(ctx, params.ToGetPaidOrdersParams())
	if err != nil {
		return nil, err
	}

	maps := map[string]*entities.PaidOrder{}
	for _, v := range pos {
		maps[utils.GenerateKeyFromCartIdAndShippingGroupId(v.CartID, v.ShippingGroupID)] = v
	}

	arrangedPaidOrders := make([]*entities.PaidOrder, 0)
	for _, v := range params.PaidOrders {
		if po, has := maps[utils.GenerateKeyFromCartIdAndShippingGroupId(v.CartID, v.ShippingGroupID)]; has {
			arrangedPaidOrders = append(arrangedPaidOrders, &entities.PaidOrder{
				ID:              po.ID,
				CartID:          v.CartID,
				ShippingGroupID: v.ShippingGroupID,
				FulfillmentID:   po.FulfillmentID,
				ShipmentID:      po.ShipmentID,
			})
		} else
		// For old business, we allow to arrange ship all sub-order at one time
		// The value of shipping_group_id in database in this case will be null
		// So, if the paid order with null shipping_group_id, it will represent all sub order
		if po, has := maps[utils.GenerateKeyFromCartIdAndShippingGroupId(v.CartID, nil)]; has && po.ShippingGroupID == nil {
			arrangedPaidOrders = append(arrangedPaidOrders, &entities.PaidOrder{
				ID:              po.ID,
				CartID:          v.CartID,
				ShippingGroupID: v.ShippingGroupID,
				FulfillmentID:   po.FulfillmentID,
				ShipmentID:      po.ShipmentID,
			})
		}
	}
	return arrangedPaidOrders, nil
}

func (s serviceImpl) GetByIDs(ctx context.Context, ids []string, workspaceID string) ([]*entities.PaidOrder, error) {
	res := make([]*entities.PaidOrder, 0)

	pos, _, err := s.repo.GetList(ctx, &entities.GetPaidOrdersParams{IDs: ids, WorkspaceID: &workspaceID})
	if err != nil {
		return res, err
	}

	for _, po := range pos {
		if po.WorkspaceID == workspaceID {
			res = append(res, po)
		}
	}

	return res, nil
}

func (s serviceImpl) Get(ctx context.Context, cartID string, shippingGroupID *string) (*entities.PaidOrder, error) {
	return s.repo.Get(ctx, &entities.GetPaidOrderParams{
		CartID:          cartID,
		ShippingGroupID: shippingGroupID,
	})
}

func (s serviceImpl) GetCount(ctx context.Context, params *entities.GetPaidOrdersParams) (int64, error) {
	return s.repo.GetCount(ctx, params)
}

func (s serviceImpl) CreateBatch(ctx context.Context, po []*entities.PaidOrder) error {
	return s.repo.CreateBatch(ctx, po)
}

func (s serviceImpl) Update(ctx context.Context, id string, po *entities.PaidOrder) error {
	return s.repo.Update(ctx, id, map[string]interface{}{
		"FulfillmentID":        po.FulfillmentID,
		"ShipmentID":           po.ShipmentID,
		"IncorrectInformation": po.IncorrectInformation,
	})
}

func (s serviceImpl) UpdateIncorrectInformation(ctx context.Context, id string, incorrectInformation []string) error {
	ii := pq.StringArray{}
	for _, v := range incorrectInformation {
		ii = append(ii, v)
	}
	return s.repo.Update(ctx, id, map[string]interface{}{
		"IncorrectInformation": ii,
	})
}

func (s serviceImpl) UpdatePaidOrderShipmentID(ctx context.Context, id string, shipmentID string) error {
	return s.repo.Update(ctx, id, map[string]interface{}{
		"ShipmentID": shipmentID,
	})
}

func (s serviceImpl) UpdateFulfillmentID(ctx context.Context, id string, fulfillmentID *string) error {
	return s.repo.Update(ctx, id, map[string]interface{}{"FulfillmentID": fulfillmentID})
}

func (s serviceImpl) UpdateFulfillmentIDBatch(ctx context.Context, ids []string, fulfillmentID *string) error {
	return s.repo.UpdateBatch(ctx, ids, map[string]interface{}{"FulfillmentID": fulfillmentID})
}

func (s serviceImpl) UpdateBatch(ctx context.Context, ids []string, data map[string]interface{}) error {
	return s.repo.UpdateBatch(ctx, ids, data)
}

func (s serviceImpl) Delete(ctx context.Context, cartId string) error {
	if err := s.repo.Delete(ctx, cartId); err != nil {
		log.Println("[DeletePaidOrder] error: " + err.Error())
		return err
	}
	return nil
}

func (s serviceImpl) CancelArrangedShipPaidOrder(ctx context.Context, id string) error {
	return s.repo.Update(ctx, id, map[string]interface{}{
		"fulfillment_id": gorm.Expr("NULL"),
		"shipment_id":    gorm.Expr("NULL"),
	})
}

func (s serviceImpl) UpdateDimension(ctx context.Context, id string, dimension entities.WeightDimension) error {
	return s.repo.Update(ctx, id, map[string]interface{}{
		"Dimension": dimension,
	})
}

func (s serviceImpl) ResetAndUpdateNewFulfillmentBatch(ctx context.Context, ids []string, fulfillmentID string) error {
	return s.repo.UpdateBatch(ctx, ids, map[string]interface{}{
		"FulfillmentID":        fulfillmentID,
		"IncorrectInformation": pq.StringArray{},
		"Dimension":            entities.WeightDimension{},
	})
}
