package service

import (
	"github.com/UpmeshLTD/urus-aio/infrastructure/db/entities"
	gin2 "github.com/UpmeshLTD/urus-aio/infrastructure/server/transport/http/gin"
	"github.com/UpmeshLTD/urus-aio/infrastructure/server/transport/http/gin/middleware"
	"github.com/UpmeshLTD/urus-aio/shipment/server/http/dto"
	"github.com/UpmeshLTD/urus-aio/shipment/service/transform"
	"github.com/gin-gonic/gin"
)

type iCourier interface {
	GetCouriers(c *gin.Context)
	GetCourier(c *gin.Context)
	CreateCourier(c *gin.Context)
	UpdateCourier(c *gin.Context)
	DeleteCourier(c *gin.Context)
	GetLinkAccountURL(c *gin.Context)
}

// GetCouriers godoc
// @Summary Get list of all couriers.
// @Description Get list of all couriers.
// @Tags Courier
// @Accept */*
// @Produce json
// @Success 200 {object} dto.GetCouriersResponse
// @Security BearerToken
// @Router /couriers [GET]
func (s *serviceImpl) GetCouriers(c *gin.Context) {
	req := &dto.GetCouriersRequest{}
	if err := gin2.BindData(c, req, gin2.BindType_Query); err != nil {
		gin2.ResponseBadRequest(c, err)
		return
	}
	result, err := s.courierDomain.GetCouriers(getCtx(c), transform.GetCouriersRequestDTOToEntity(req))
	if err != nil {
		gin2.ResponseInternalServerError(c, err)
		return
	}
	gin2.ResponseSuccess(c, transform.GetCouriersResponseEntityToDTO(result))
}

// GetCourier godoc
// @Summary Get Courier by id.
// @Description Get Courier by id.
// @Tags Courier
// @Accept */*
// @Param id path int true "Courier ID"
// @Success 200 {object} dto.GetCourierResponse
// @Security BearerToken
// @Router /couriers/{id} [GET]
func (s *serviceImpl) GetCourier(c *gin.Context) {
	req := &dto.GetCourierRequest{}
	if err := gin2.BindData(c, req, gin2.BindType_URI); err != nil {
		gin2.ResponseBadRequest(c, err)
		return
	}
	result, err := s.courierDomain.GetCourier(getCtx(c), transform.GetCourierRequestDTOToEntity(req))
	if err != nil {
		gin2.ResponseInternalServerError(c, err)
		return
	}
	gin2.ResponseSuccess(c, transform.GetCourierResponseEntityToDTO(result))
}

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

	res, err := s.courierDomain.CreateCourier(getCtx(c), transform.CreateCourierRequestDTOToEntity(req))

	if err != nil || res == nil {
		gin2.ResponseInternalServerError(c, err)
		return
	}

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

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

	if err := s.courierDomain.UpdateCourier(getCtx(c), entities.IDFromInt64(req.ID), transform.UpdateCourierRequestDTOToEntity(req)); err != nil {
		gin2.ResponseInternalServerError(c, err)
		return
	}
	gin2.ResponseNoContent(c)
}

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

	if err := s.courierDomain.DeleteCourier(getCtx(c), entities.IDFromInt64(req.ID)); err != nil {
		gin2.ResponseInternalServerError(c, err)
		return
	}
	gin2.ResponseNoContent(c)
}

// GetLinkAccountURL godoc
// @Summary Get link account URL.
// @Description Get link account URL.
// @Tags Courier
// @Accept */*
// @Produce json
// @Param userworkspace header string true "User workspace")
// @Param id path int true "Courier ID"
// @Success 200 {object} dto.GetLinkAccountUrlResponse
// @Security BearerToken
// @Router /couriers/{id}/link-account [POST]
func (s *serviceImpl) GetLinkAccountURL(c *gin.Context) {
	req := &dto.GetLinkAccountUrlRequest{}
	if err := gin2.BindData(c, req, gin2.BindType_URI); err != nil {
		gin2.ResponseBadRequest(c, err)
		return
	}

	courier, err := s.courierDomain.GetCourierByID(getCtx(c), entities.ID{ID: req.CourierID})
	if err != nil {
		gin2.ResponseBadRequest(c, err)
	}

	ces, err := s.externalShipmentFactory.Get(courier.Code)
	if err != nil {
		gin2.ResponseInternalServerError(c, err)
		return
	}

	workspaceID := middleware.GetWorkspaceId(c)

	// Create seller courier if not found
	sc, _ := s.courierDomain.GetSellerCourier(getCtx(c), workspaceID, req.CourierID)
	if sc == nil {
		encryptedInfo, salt, err := ces.EncryptCourierAuthenticationInfo(dto.CourierAuthenticationInfo{}, workspaceID)
		if err != nil {
			gin2.ResponseInternalServerError(c, err)
			return
		}
		sc, err = s.courierDomain.CreateSellerCourier(getCtx(c), transform.CreateSellerCourierRequestDTOToEntity(&dto.CreateSellerCourierRequest{
			CourierID: req.CourierID,
		}, middleware.GetWorkspaceId(c), encryptedInfo, salt, false))
		if err != nil {
			gin2.ResponseInternalServerError(c, err)
			return
		}
	}

	linkAccountUrl, err := ces.GenerateLinkAccountURL(getCtx(c), sc)
	if err != nil {
		gin2.ResponseInternalServerError(c, err)
		return
	}

	gin2.ResponseSuccess(c, dto.GetLinkAccountUrlResponse{
		SellerCourierID: sc.ID.ToInt64(),
		LinkAccountUrl:  linkAccountUrl,
	})
}
