package fulfillment

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

	"github.com/UpmeshLTD/urus-aio/infrastructure/common"
	"github.com/UpmeshLTD/urus-aio/infrastructure/db/entities"
	"github.com/UpmeshLTD/urus-aio/paid-order/db/gormdb"
	"github.com/UpmeshLTD/urus-aio/paid-order/service/dto"
)

type Repository interface {
	GetList(ctx context.Context, workspaceID string, req *dto.GetFulfillmentsRequest) ([]*entities.Fulfillment, int64, error)
	Get(ctx context.Context, id string) (*entities.Fulfillment, error)
	GetWithAssociates(ctx context.Context, id string) (*entities.Fulfillment, error)
	GetByIDs(ctx context.Context, ids []string) ([]*entities.Fulfillment, error)
	Create(ctx context.Context, fulfillment *entities.Fulfillment) (*entities.Fulfillment, error)
	Update(ctx context.Context, id string, maps map[string]interface{}) error
	Delete(ctx context.Context, id string) error
	DeleteBatch(ctx context.Context, ids []string) error

	GetFulfillmentEstimatedFees(ctx context.Context, courierDeliveryPlanID int64) ([]*entities.FulfillmentEstimatedFee, error)
}

type repositoryImpl struct {
	db gormdb.GormDB
}

func NewRepository(db gormdb.GormDB) Repository {
	return &repositoryImpl{
		db: db,
	}
}

func (r *repositoryImpl) GetList(ctx context.Context, workspaceID string, req *dto.GetFulfillmentsRequest) ([]*entities.Fulfillment, int64, error) {
	var (
		results    []*entities.Fulfillment
		builder    = r.db.DB().Where("workspace_id = ?", workspaceID)
		pagination = &req.Pagination
	)

	if !req.Deactivate {
		builder = builder.Where("deactivate = false")
	}

	builder = builder.Where("draft = true")
	builder.Scopes(common.Paginate(results, pagination, builder))

	if err := builder.Preload(clause.Associations).Find(&results).Error; err != nil {
		return nil, 0, err
	}
	return results, pagination.Total, nil
}

func (r *repositoryImpl) Get(ctx context.Context, id string) (*entities.Fulfillment, error) {
	res := &entities.Fulfillment{}
	if err := r.db.DB().First(res, "id = ?", id).Error; err != nil {
		return nil, err
	}
	return res, nil
}

func (r *repositoryImpl) GetWithAssociates(ctx context.Context, id string) (*entities.Fulfillment, error) {
	res := &entities.Fulfillment{}
	if err := r.db.DB().Preload("PaidOrders", func(db *gorm.DB) *gorm.DB {
		return db.Order("paid_orders.created_at asc")
	}).First(res, "id = ?", id).Error; err != nil {
		return nil, err
	}
	return res, nil
}

func (r *repositoryImpl) GetByIDs(ctx context.Context, ids []string) ([]*entities.Fulfillment, error) {
	var res []*entities.Fulfillment
	if err := r.db.DB().Model(&entities.Fulfillment{}).Where("id IN ?", ids).Find(&res).Error; err != nil {
		return nil, err
	}
	return res, nil
}

func (r *repositoryImpl) Create(ctx context.Context, fulfillment *entities.Fulfillment) (*entities.Fulfillment, error) {
	if err := r.db.DB().Create(&fulfillment).Error; err != nil {
		return nil, err
	}
	return fulfillment, nil
}

func (r *repositoryImpl) Update(ctx context.Context, id string, maps map[string]interface{}) error {
	return r.db.DB().Model(&entities.Fulfillment{}).Where("id = ?", id).Updates(&maps).Error
}

func (r *repositoryImpl) Delete(ctx context.Context, id string) error {
	return r.db.DB().Delete(&entities.Fulfillment{}, "id = ?", id).Error
}

func (r *repositoryImpl) DeleteBatch(ctx context.Context, ids []string) error {
	return r.db.DB().Delete(&entities.Fulfillment{}, "id in ?", ids).Error
}

func (r *repositoryImpl) GetFulfillmentEstimatedFees(ctx context.Context, courierDeliveryPlanID int64) ([]*entities.FulfillmentEstimatedFee, error) {
	var res []*entities.FulfillmentEstimatedFee
	if err := r.db.DB().Model(&entities.FulfillmentEstimatedFee{}).Where("courier_delivery_plan_id = ? AND deactivate = false", courierDeliveryPlanID).Order("max_weight ASC, max_dimension ASC").Find(&res).Error; err != nil {
		return nil, err
	}
	return res, nil
}
