package seller_order_usecase

import (
	_const "chonho/const"
	"chonho/internal/api-v1/entity"
	"chonho/internal/api-v1/repository/order_repository"
	"chonho/internal/api-v1/repository/store_repository"
	"chonho/internal/api-v1/usecase/notification_usecase"
	_error "chonho/pkg/c_error"
	"chonho/pkg/model"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

type SellerOrderUseCase interface {
	QuerySellerOrders(sellerUserId int64, filter order_repository.SellerOrderFilter) ([]entity.SellerOrder, error)
	QuerySellerOrderDetail(sellerUserId, orderId int64) (*entity.SellerOrder, error)
	UpdateOrderStatus(order *model.Order, status int) error
	CancelOrder(sellerUserId int64, orderId int64) error
	ConfirmOrder(sellerUserId int64, orderId int64, request *entity.OrderConfirmRequest) error
	ConfirmOrderPayment(sellerUserId int64, orderId int64) error
	ConfirmOrderDelivered(sellerUserId int64, orderId int64, request *entity.OrderDeliveredRequest) error
}

type useCase struct {
	orderRepo order_repository.OrderRepository
	storeRepo store_repository.StoreRepository
	notify    notification_usecase.NotificationUseCase
}

func NewSellerOrderUseCase(
	orderRepo order_repository.OrderRepository,
	storeRepo store_repository.StoreRepository,
	notify notification_usecase.NotificationUseCase,
) *useCase {
	return &useCase{
		orderRepo: orderRepo,
		storeRepo: storeRepo,
		notify:    notify,
	}
}

func (uc *useCase) QuerySellerOrders(sellerUserId int64, filter order_repository.SellerOrderFilter) ([]entity.SellerOrder, error) {

	data, err := uc.orderRepo.SellerOrderList(sellerUserId, filter)
	if err != nil {
		return nil, _error.New(_const.InternalServerErr, err.Error())
	}
	for i, od := range data {
		data[i].CreatedAtSecond = od.CreatedAt.Unix()
	}
	return data, nil
}

func (uc *useCase) QuerySellerOrderDetail(sellerUserId, orderId int64) (*entity.SellerOrder, error) {

	orders, err := uc.orderRepo.SellerOrderList(sellerUserId, order_repository.SellerOrderFilter{
		OrderIds: []int64{orderId},
	})

	if err != nil {
		return nil, _error.New(_const.InternalServerErr, err.Error())
	}

	if len(orders) == 0 {
		return nil, _error.New(_const.OrderNotFound, "Order not found")
	}
	order := orders[0]
	if order.SellerId != sellerUserId {
		return nil, _error.New(_const.OrderWrongSeller, "Order is not be long to seller")
	}

	orderItems, err := uc.orderRepo.SellerOrderDetail(order.Id)
	if err != nil {
		return nil, _error.New(_const.InternalServerErr, err.Error())
	}

	order.Items = orderItems
	order.CreatedAtSecond = order.CreatedAt.Unix()
	return &order, nil
}

func (uc *useCase) ConfirmOrder(sellerUserId int64, orderId int64, request *entity.OrderConfirmRequest) error {

	order, err := uc.GetAndCheckOrder(sellerUserId, orderId)
	if err != nil {
		return err
	}

	if !order.IsValidStatus(_const.OrderStatusConfirmed) {
		return _error.New(_const.OrderInvalidStatusForUpdate, "new status is invalid")
	}

	// TODO update each product and re calculate order price
	orderItems, err := uc.orderRepo.QueryOrderItem(order_repository.OrderItemFilter{
		OrderId: orderId,
	})
	if err != nil {
		return err
	}

	postProductMapping := make(map[int64]entity.OrderItemConfirmRequest)
	if request != nil && len(request.ProductUpdate) > 0 {
		for _, el := range request.ProductUpdate {
			postProductMapping[el.ProductId] = el
		}
	}

	for _, el := range orderItems {

		if el.IsQuoteAfterOrdering {
			// Quote
			if _, has := postProductMapping[el.ProductId]; !has {
				return _error.New(_const.ProductQuoteRequire, fmt.Sprintf("missing quote of prouduct %v", el.Name))
			}
		}
		// TODO update product
		if postProduct, has := postProductMapping[el.ProductId]; has {
			oldQty := el.Quantity
			// update order item
			el.Quantity = postProduct.Quantity
			el.Status = postProduct.Status

			// update order
			order.SellerChangeOrderItem = true
			// update item price when update qty
			if oldQty != el.Quantity {
				oldPrice := el.SubtotalPrice
				newPrice := el.Quantity * float64(el.Price)
				if postProduct.Status == _const.OrderItemStatusInvalid {
					newPrice = 0
				}
				el.SubtotalPrice = int(newPrice)
				order.Price += int(newPrice) - oldPrice
			}

			// edit note order.SellerChangeNote += fmt.Sprintf("%v : %v", el.Name, postProduct.Note)

			changeNote := make([]interface{}, 0)
			if postProduct.Status == _const.OrderItemStatusInvalid {
				// case out of stock
				changeNote = append(changeNote, gin.H{
					"type":         "2",
					"description":  "cancel item cause of out of stock",
					"product_name": el.Name,
				})
			} else {
				// case quote price
				changeNote = append(changeNote, gin.H{
					"type":         "1",
					"description":  "quote after check product",
					"old_qty":      oldQty,
					"new_qty":      el.Quantity,
					"product_name": el.Name,
				})
			}
			changeNoteData, _ := json.Marshal(changeNote)
			order.SellerChangeNote = string(changeNoteData)

			_, err = uc.orderRepo.UpdateOrderItem(order_repository.OrderItemFilter{
				Id: el.Id,
			}, map[string]interface{}{
				"status":         el.Status,
				"quote_quantity": el.Quantity,
				"subtotal_price": el.SubtotalPrice,
				"note":           el.Note,
			})

			if err != nil {
				return err
			}
		}

	}

	order.Status = _const.OrderStatusConfirmed
	_, err = uc.orderRepo.UpdateOrderInfo(order_repository.OrderFilter{
		Id: orderId,
	}, map[string]interface{}{
		"status":                   order.Status,
		"seller_change_order_item": order.SellerChangeOrderItem,
		"seller_change_note":       order.SellerChangeNote,
		"price":                    order.Price,
	})

	go uc.notify.NotifyConfirmOrder(orderId)
	return err
}

func (uc *useCase) ConfirmOrderPayment(sellerUserId int64, orderId int64) error {
	_, err := uc.GetAndCheckOrder(sellerUserId, orderId)
	if err != nil {
		return err
	}
	_, err = uc.orderRepo.UpdateOrderInfo(order_repository.OrderFilter{
		Id: orderId,
	}, map[string]interface{}{
		"payment_status": _const.PaymentStatusSuccess,
	})
	return err
}

func (uc *useCase) GetAndCheckOrder(sellerUserId int64, orderId int64) (*model.Order, error) {
	order, err := uc.orderRepo.FirstOrders(order_repository.OrderFilter{
		Id: orderId,
	})

	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, _error.New(_const.OrderNotFound, "order not found")
		}
		return nil, _error.New(_const.InternalServerErr, err.Error())
	}

	// validate seller id
	_, err = uc.storeRepo.FirstStore(store_repository.StoreFilter{
		UserId:  sellerUserId,
		StoreId: order.StoreId,
	})

	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, _error.New(_const.OrderWrongSeller, "order is not belong to seller")
		}
		return nil, _error.New(_const.InternalServerErr, err.Error())
	}
	return order, nil
}

func (uc *useCase) UpdateOrderStatus(order *model.Order, status int) error {

	if !order.IsValidStatus(status) {
		return _error.New(_const.OrderInvalidStatusForUpdate, "new status is invalid")
	}

	_, err := uc.orderRepo.UpdateOrderInfo(order_repository.OrderFilter{
		Id: order.Id,
	}, map[string]interface{}{
		"status": status,
	})

	if err != nil {
		return err
	}

	return nil
}

func (uc *useCase) ConfirmOrderDelivered(sellerUserId int64, orderId int64, request *entity.OrderDeliveredRequest) error {
	order, err := uc.GetAndCheckOrder(sellerUserId, orderId)
	if err != nil {
		return err
	}

	newStatus := _const.OrderStatusDelivered
	if request != nil && len(request.DeliveredProductIds) > 0 {
		// update request product to delivered, and check the order still has undelivered product
		_, err := uc.orderRepo.UpdateOrderItem(order_repository.OrderItemFilter{
			OrderId:    orderId,
			ProductIds: request.DeliveredProductIds,
		}, map[string]interface{}{"status": _const.OrderItemStatusDelivered})
		if err != nil {
			return err
		}
		processingItems, err := uc.orderRepo.QueryOrderItem(order_repository.OrderItemFilter{
			OrderId: orderId,
			Status:  []int{_const.OrderItemStatusValid},
		})
		if err != nil {
			return err
		}
		if len(processingItems) > 0 {
			newStatus = _const.OrderStatusConfirmed
		}
	} else {
		// update valid status to delivered
		uc.orderRepo.UpdateOrderItem(order_repository.OrderItemFilter{
			OrderId: orderId,
			Status:  []int{_const.OrderItemStatusValid},
		}, map[string]interface{}{"status": _const.OrderItemStatusDelivered})
	}

	// update order delivered
	err = uc.orderRepo.UpdateDeliveredItem(orderId)
	if err != nil {
		return err
	}
	go uc.notify.NotifyRemindRating(orderId)
	return uc.UpdateOrderStatus(order, newStatus)
}

func (uc *useCase) CancelOrder(sellerUserId int64, orderId int64) error {
	order, err := uc.GetAndCheckOrder(sellerUserId, orderId)
	if err != nil {
		return err
	}
	go uc.notify.NotifyCancelOrder(orderId, notification_usecase.CancelFromSeller)
	return uc.UpdateOrderStatus(order, _const.OrderStatusCanceled)
}
