package order_repository

import (
	_const "chonho/const"
	"chonho/internal/api-v1/entity"
	_error "chonho/pkg/c_error"
	"chonho/pkg/model"
	"chonho/util"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/jackc/pgconn"
)

func (r *repository) CreateOrder(order *model.Order, retryTime int) error {
	order.CreateOrderCode()
	err := r.db.Create(order).Error
	fmt.Println(order.Code)
	if err != nil {
		if pgError := err.(*pgconn.PgError); errors.Is(err, pgError) {
			if pgError.Code == "23505" {
				if retryTime >= 3 {
					return err
				}
				return r.CreateOrder(order, retryTime+1)
			}
		}
		return err
	}
	return nil
}

func (r *repository) AddOrderItem(orderItems *[]model.OrderItem) error {
	return r.db.Create(orderItems).Error
}

func (r *repository) SaveOrderHistory(his *model.OrderHistory) error {
	return r.db.Create(his).Error
}

func (r *repository) UpdateOrderInfo(filter OrderFilter, info map[string]interface{}) (int64, error) {
	builder := r.db.Model(&model.Order{})
	if filter.Id != 0 {
		builder = builder.Where("id = ? ", filter.Id)
	}
	if filter.UserId != 0 {
		builder = builder.Where("user_id = ? ", filter.UserId)
	}
	db := builder.Updates(info)
	return db.RowsAffected, db.Error
}

func (r *repository) UpdateOrders(filter OrderFilter, info map[string]interface{}) error {
	builder := r.db.Model(&model.Order{})
	if len(filter.OrderIds) > 0 {
		builder = builder.Where("id IN ? ", filter.OrderIds)
	}
	if filter.UserId != 0 {
		builder = builder.Where("user_id = ? ", filter.UserId)
	}
	return builder.Updates(info).Error
}

func (r *repository) GetOrders(filter OrderFilter) ([]model.Order, error) {
	builder := r.db.Model(&model.Order{})
	if filter.Id != 0 {
		builder = builder.Where("id = ? ", filter.Id)
	}
	if filter.UserId != 0 {
		builder = builder.Where("user_id = ? ", filter.UserId)
	}
	var orders []model.Order
	err := builder.Scan(&orders).Error
	return orders, err
}

func (r *repository) GetBuyerOrder(filter BuyerOrderFilter) (*entity.BuyerOrderDetail, error) {
	builder := r.db.Table("orders AS o").
		Joins("JOIN stores AS s ON s.id = o.store_id").
		Joins("JOIN users AS u ON u.id = s.user_id").
		Joins("JOIN user_shipping_addresses AS a ON a.id = o.shipping_address_id").
		Select(`
			o.id,
			o.code,
			o.status,
			o.price,
			o.total_items,
			o.seller_change_order_item,
			o.seller_change_note,
			a.receiver_name,
			a.receiver_phone,
			a.apartment_house_number,
			o.store_id,
			s.name AS store_name,
			u.phone AS store_phone,
			s.payment,
			o.payment_status,
			o.payment_proof,
			o.note,
			o.canceled_reason,
			o.created_at,
			o.is_quote_after_ordering
		`)
	if filter.Id > 0 {
		builder.Where("o.id = ?", filter.Id)
	}
	if filter.UserId > 0 {
		builder.Where("o.user_id = ?", filter.UserId)
	}

	var order *entity.BuyerOrderDetail
	err := builder.First(&order).Error
	if err != nil {
		return nil, _error.New(_const.InternalServerErr, err.Error())
	}
	_ = json.Unmarshal([]byte(order.Payment), &order.PaymentInfo)
	err = r.db.Table("order_items").Where("order_id = ?", filter.Id).Find(&order.Items).Error
	if err != nil {
		return nil, _error.New(_const.InternalServerErr, err.Error())
	}
	order.CreatedTimestamp = order.CreatedAt.Unix()
	order.GroupStatus = r.statusToGroupStatus(order.Status, order.PaymentStatus)
	order.IsCancelable = r.isCancelable(order.Status)
	order.PaymentInfo = entity.AppendStorePaymentCOD(order.PaymentInfo)
	return order, nil
}

func (r *repository) GetBuyerOrders(filter BuyerOrderFilter) ([]entity.BuyerOrderList, error) {
	var orders []entity.BuyerOrderList
	builder := r.db.Table("orders AS o").
		Joins("JOIN stores AS s ON s.id = o.store_id").
		Joins("JOIN users AS u ON u.id = s.user_id").
		Select("o.id, o.code, o.status, o.payment_status, o.price, o.total_items, o.store_id, o.seller_change_order_item, o.seller_change_note, s.name AS store_name, u.phone AS store_phone, o.created_at, o.is_quote_after_ordering")
	if filter.UserId > 0 {
		builder.Where("o.user_id = ?", filter.UserId)
	}
	if filter.GroupStatus == _const.OrderGroupStatusPending {
		builder.Where("o.status = ? ", _const.OrderStatusNew)

	} else if filter.GroupStatus == _const.OrderGroupStatusDelivering {
		builder.Where("o.status IN ? ", []int{
			_const.OrderStatusConfirmed,
			_const.OrderStatusReadyToShip,
			_const.OrderStatusInTransit})
	} else if filter.GroupStatus == _const.OrderGroupStatusCompleted {
		builder.Where("o.status IN ? ", []int{
			_const.OrderStatusDelivered,
			_const.OrderStatusReceived},
		)
	}
	err := builder.Order("o.created_at DESC").Find(&orders).Error
	if err != nil {
		return nil, _error.New(_const.InternalServerErr, err.Error())
	}
	for i, e := range orders {
		orders[i].CreatedTimestamp = e.CreatedAt.Unix()
		orders[i].GroupStatus = filter.GroupStatus
		orders[i].IsCancelable = r.isCancelable(e.Status)
	}
	return orders, nil
}

func (r *repository) statusToGroupStatus(orderStatus int, paymentStatus int) int {
	if (util.IsInArray(orderStatus, []int{
		_const.OrderStatusDelivered,
		_const.OrderStatusReceived,
	}) && paymentStatus == _const.PaymentStatusSuccess) || orderStatus == _const.OrderStatusCanceled {
		return _const.OrderGroupStatusCompleted
	}

	if util.IsInArray(orderStatus, []int{
		_const.OrderStatusConfirmed,
		_const.OrderStatusReadyToShip,
		_const.OrderStatusInTransit,
	}) && paymentStatus == _const.PaymentStatusSuccess {
		return _const.OrderGroupStatusDelivering
	}

	return _const.OrderGroupStatusPending
}

func (r *repository) isCancelable(status int) bool {
	return status == _const.OrderStatusNew || status == _const.OrderStatusRequestCancel
}
