package service

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

	gin2 "github.com/UpmeshLTD/urus-aio/infrastructure/server/transport/http/gin"
	"github.com/UpmeshLTD/urus-aio/infrastructure/server/transport/http/gin/middleware"
	"gorm.io/gorm"

	"github.com/UpmeshLTD/urus-aio/infrastructure/db/entities"
	"github.com/UpmeshLTD/urus-aio/mdm/server/http/dto"
	"github.com/UpmeshLTD/urus-aio/mdm/service/transform"
	"github.com/gin-gonic/gin"

	httpclient "github.com/UpmeshLTD/urus-aio/infrastructure/client/http"
)

type iAddress interface {
	CreateBuyerAddress(ctx *gin.Context)
	UpdateBuyerAddress(ctx *gin.Context)
	DeleteBuyerAddress(ctx *gin.Context)
	GetBuyerAddressByID(ctx *gin.Context)
	GetBuyerAddresses(ctx *gin.Context)

	SaveDraftCheckoutInfo(ctx *gin.Context)
	GetDraftCheckoutInfoByBuyer(c *gin.Context)
	GetDraftCheckoutInfoBySeller(c *gin.Context)
}

// CreateBuyerAddress godoc
// @Summary CreateBuyerAddress
// @Description Create a new buyer address
// @Tags Address
// @Accept json
// @Produce json
// @Param request body dto.CreateBuyerAddressRequest true "request"
// @Success 200 {object} dto.BuyerAddressResponse
// @Security BearerToken
// @Router /buyer-addresses [POST]
func (s *serviceImpl) CreateBuyerAddress(c *gin.Context) {
	req := &dto.CreateBuyerAddressRequest{}
	if err := gin2.BindData(c, &req, gin2.BindType_JSON); err != nil {
		gin2.ResponseBadRequest(c, err)
		return
	}

	res, err := s.addressDomain.CreateBuyerAddress(getCtx(c), transform.CreateBuyerAddressDTOToEntity(c, req))
	if err != nil {
		gin2.ResponseInternalServerError(c, err)
		return
	}
	gin2.ResponseSuccess(c, transform.BuyerAddressEntityToDTO(res))
}

// UpdateBuyerAddress godoc
// @Summary UpdateBuyerAddress
// @Description Update a buyer address
// @Tags Address
// @Accept json
// @Produce json
// @Param id path int true "Buyer address ID"
// @Param request body dto.UpdateBuyerAddressRequest true "Update buyer address"
// @Success 204
// @Security BearerToken
// @Router /buyer-addresses/{id} [PUT]
func (s *serviceImpl) UpdateBuyerAddress(c *gin.Context) {
	req := &dto.UpdateBuyerAddressRequest{}
	if err := gin2.BindData(c, &req, gin2.BindType_JSON, gin2.BindType_URI); err != nil {
		gin2.ResponseBadRequest(c, err)
		return
	}

	res, err := s.addressDomain.UpdateBuyerAddress(getCtx(c), transform.UpdateBuyerAddressDTOToEntity(c, req))
	if err != nil {
		gin2.ResponseInternalServerError(c, err)
		return
	}

	gin2.ResponseSuccess(c, transform.BuyerAddressEntityToDTO(res))
}

// DeleteBuyerAddress godoc
// @Summary DeleteBuyerAddress
// @Description Delete a buyer address
// @Tags Address
// @Accept json
// @Produce json
// @Param id path int true "Buyer address ID"
// @Success 204
// @Security BearerToken
// @Router /buyer-addresses/{id} [DELETE]
func (s *serviceImpl) DeleteBuyerAddress(c *gin.Context) {
	req := &dto.DeleteBuyerAddressRequest{}
	if err := gin2.BindData(c, req, gin2.BindType_URI); err != nil {
		gin2.ResponseBadRequest(c, err)
		return
	}

	if err := s.addressDomain.DeleteBuyerAddress(getCtx(c), transform.DeleteBuyerAddressDTOToEntity(c, req)); err != nil {
		gin2.ResponseInternalServerError(c, err)
		return
	}
	gin2.ResponseNoContent(c)
}

// GetBuyerAddressByID godoc
// @Summary Get buyer address by ID
// @Description Get buyer address by ID
// @Tags Address
// @Accept json
// @Produce json
// @Param id path int true "Buyer address ID"
// @Success 200 {object} dto.BuyerAddressResponse
// @Security BearerToken
// @Router /buyer-addresses/{id} [GET]
func (s *serviceImpl) GetBuyerAddressByID(c *gin.Context) {
	var req = &dto.GetBuyerAddressByIDRequest{}
	if err := gin2.BindData(c, &req, gin2.BindType_URI); err != nil {
		gin2.ResponseBadRequest(c, err)
		return
	}

	res, err := s.addressDomain.GetBuyerAddressByID(getCtx(c), transform.GetBuyerAddressByIDDTOToEntity(c, req))
	if err != nil {
		gin2.ResponseInternalServerError(c, err)
		return
	}

	gin2.ResponseSuccess(c, transform.BuyerAddressEntityToDTO(res))
}

// GetBuyerAddresses godoc
// @Summary Get buyer addresses
// @Description Get buyer addresses
// @Tags Address
// @Accept */*
// @Produce json
// @Success 200 {object} dto.BuyerAddressResponse
// @Security BearerToken
// @Router /buyer-addresses [GET]
func (s *serviceImpl) GetBuyerAddresses(c *gin.Context) {
	results, err := s.addressDomain.GetBuyerAddressByBuyerID(getCtx(c), middleware.GetBuyerId(c))
	if err != nil {
		gin2.ResponseInternalServerError(c, err)
		return
	}
	gin2.ResponseSuccess(c, transform.BuyerAddressEntitiesToBuyerAddressArrayResponseDTO(results))
}

// SaveDraftCheckoutInfo godoc
// @Summary Save draft checkout info
// @Description Save draft checkout info
// @Tags Address
// @Accept json
// @Produce json
// @Success 200 {object} dto.DraftCheckoutInfoResponse
// @Security BearerToken
// @Router /draft-checkout-info [POST]
func (s *serviceImpl) SaveDraftCheckoutInfo(c *gin.Context) {
	var req = &dto.SaveDraftCheckoutInfoRequest{}
	if err := gin2.BindData(c, &req, gin2.BindType_JSON); err != nil {
		gin2.ResponseBadRequest(c, err)
		return
	}

	res, err := s.addressDomain.SaveDraftCheckoutInfo(getCtx(c), transform.SaveDraftCheckoutInfoDTOToParams(c, req))
	if err != nil {
		gin2.ResponseInternalServerError(c, err)
		return
	}

	gin2.ResponseSuccess(c, transform.DraftCheckoutInfoEntityToDTO(res))
}

// GetDraftCheckoutInfoByBuyer godoc
// @Summary Get draft checkout info by buyer
// @Description Get draft checkout info by buyer
// @Tags Address
// @Param cartId path string true "Cart ID"
// @Accept */*
// @Produce json
// @Success 200 {object} dto.DraftCheckoutInfoResponse
// @Security BearerToken
// @Router /draft-checkout-info/{cartId} [GET]
func (s *serviceImpl) GetDraftCheckoutInfoByBuyer(c *gin.Context) {
	var req = &dto.GetDraftCheckoutInfoByBuyerRequest{}
	if err := gin2.BindData(c, &req, gin2.BindType_URI); err != nil {
		gin2.ResponseBadRequest(c, err)
		return
	}

	buyerId := middleware.GetBuyerId(c)

	// Get entity
	res, err := s.addressDomain.GetDraftCheckoutInfo(getCtx(c), &entities.DraftCheckoutInfo{
		CartID:  req.CartID,
		BuyerID: &buyerId,
	})
	if err != nil {
		gin2.ResponseInternalServerError(c, err)
		return
	}

	gin2.ResponseSuccess(c, transform.DraftCheckoutInfoEntityToDTO(res))
}

// GetDraftCheckoutInfoBySeller godoc
// @Summary Get draft checkout info by seller
// @Description Get draft checkout info by seller
// @Tags Address
// @Param cartId path string true "Cart ID"
// @Accept json
// @Produce json
// @Success 200 {object} dto.DraftCheckoutInfoResponse
// @Security BearerToken
// @Router /draft-checkout-info/{cartId}/by-seller [GET]
func (s *serviceImpl) GetDraftCheckoutInfoBySeller(c *gin.Context) {
	var req = &dto.GetDraftCheckoutInfoBySellerRequest{}
	if err := gin2.BindData(c, &req, gin2.BindType_URI, gin2.BindType_JSON); err != nil {
		gin2.ResponseBadRequest(c, err)
		return
	}

	workspaceId := middleware.GetWorkspaceId(c)

	// Get entity
	res, err := s.addressDomain.GetDraftCheckoutInfo(getCtx(c), &entities.DraftCheckoutInfo{
		CartID:  req.CartID,
		BuyerID: req.BuyerID,
	})
	if err != nil {
		gin2.ResponseInternalServerError(c, err)
		return
	}

	// Check owner for seller
	// If the record not have WorkspaceId then we need to verify the param workspaceId
	if res.WorkspaceID == nil || *res.WorkspaceID == "" {
		token := c.Request.Header.Get("Authorization")
		cartDetail, err := s.getCartByID(getCtx(c), token, req.CartID)
		if err != nil {
			gin2.ResponseInternalServerError(c, err)
			return
		}
		if cartDetail.SellerID != workspaceId {
			gin2.ResponseInternalServerError(c, gorm.ErrRecordNotFound)
			return
		} else {
			// Update WorkspaceID if verify succeed
			s.addressDomain.SaveDraftCheckoutInfo(getCtx(c), &entities.SaveDraftCheckoutInfoParams{
				CartID:      req.CartID,
				BuyerID:     *req.BuyerID,
				WorkspaceID: &workspaceId,
			})
		}
	} else if workspaceId != *res.WorkspaceID {
		gin2.ResponseInternalServerError(c, gorm.ErrRecordNotFound)
		return
	}

	gin2.ResponseSuccess(c, transform.DraftCheckoutInfoEntityToDTO(res))
}

func (s *serviceImpl) getCartByID(ctx context.Context, token string, id string) (*dto.CartDetail, error) {
	url := fmt.Sprintf("%s/%s", s.cfg.GetCartServiceConfig().GetCartDetail, id)
	headers := map[string]interface{}{
		"Authorization": token,
	}
	res, err := httpclient.Do(ctx, http.MethodGet, url, nil, headers)
	if err != nil {
		return nil, err
	}
	cartDetail := &dto.CartDetail{}
	if err = json.Unmarshal(res, cartDetail); err != nil {
		return nil, err
	}
	return cartDetail, nil
}
