package service

import (
	"errors"
	"github.com/UpmeshLTD/urus-aio/infrastructure/db/entities"
	"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/infrastructure/utils"
	pdfpb "github.com/UpmeshLTD/urus-aio/pdf/server/grpc/pb"
	ishipment "github.com/UpmeshLTD/urus-aio/shipment/external/shipment"
	"github.com/UpmeshLTD/urus-aio/shipment/server/http/dto"
	ginBase "github.com/gin-gonic/gin"
	"log"
	"net/http"
)

// GenerateShipmentLabels godoc
// @Summary Generate shipment labels
// @Description Generate shipment labels
// @Tags Shipment
// @Accept */*
// @Param userworkspace header string true "User workspace")
// @Param request body dto.GenerateShipmentLabelsRequest true "request"
// @Success 200 {object} dto.GenerateShipmentLabelsResponse
// @Security BearerToken
// @Router /shipments/labels/pdf [POST]
func (s *serviceImpl) GenerateShipmentLabels(c *ginBase.Context) {
	workspaceID := middleware.GetWorkspaceId(c)
	req := &dto.GenerateShipmentLabelsRequest{}
	if err := c.BindJSON(&req); err != nil {
		gin.ResponseErrorWithCode(c, http.StatusBadRequest, err)
		return
	}

	// Get shipments
	rows, err := s.shipmentDomain.GetByIDs(c, req.ShipmentIDs, workspaceID)
	if err != nil {
		gin.ResponseErrorWithCode(c, http.StatusBadRequest, err)
		return
	}

	total := len(rows)
	if total == 0 {
		gin.ResponseErrorWithCode(c, http.StatusBadRequest, errors.New("shipments was empty"))
		return
	}

	pdfTitle := "Shipping Labels"
	if total == 1 {
		pdfTitle = *rows[0].CourierTrackingID
	}

	if req.Language == "" {
		req.Language = middleware.GetAcceptLanguage(c)
	} else {
		req.Language = utils.FixAcceptLanguage(req.Language)
	}

	shipmentLabels := make([]*pdfpb.ShipmentLabel, 0)
	failedShipmentDTOs := make([]*dto.GenerateShipmentLabelsResponseDataFailedShipment, 0)
	externalShipmentFactoryMap := map[string]ishipment.IShipment{}

	// sort by input ids
	shipmentMap := map[string]*entities.Shipment{}
	for _, s := range rows {
		shipmentMap[s.ID.String()] = s
	}

	shipments := make([]*entities.Shipment, 0)
	for _, id := range req.ShipmentIDs {
		if v, ok := shipmentMap[id]; ok {
			shipments = append(shipments, v)
		} else {
			log.Printf("[GenerateShipmentLabels] shipment not found - ID: %s\n", id)
			failedShipmentDTOs = append(failedShipmentDTOs, &dto.GenerateShipmentLabelsResponseDataFailedShipment{
				ID:    id,
				Error: "shipment not found",
			})
		}
	}

	for _, shipment := range shipments {
		if shipment.Courier.Code == "" {
			log.Printf("[GenerateShipmentLabels] courier code was empty - shipment: %s\n", shipment.ID.String())
			failedShipmentDTOs = append(failedShipmentDTOs, &dto.GenerateShipmentLabelsResponseDataFailedShipment{
				ID:    shipment.ID.String(),
				Error: "courier code was empty",
			})
			continue
		}

		if _, ok := externalShipmentFactoryMap[shipment.Courier.Code]; !ok {
			es, err := s.externalShipmentFactory.Get(shipment.Courier.Code)
			if err != nil {
				log.Printf("[GenerateShipmentLabels] courier not support to generate pdf file - shipment: %s - courier code: %s\n", shipment.ID.String(), shipment.Courier.Name)
				failedShipmentDTOs = append(failedShipmentDTOs, &dto.GenerateShipmentLabelsResponseDataFailedShipment{
					ID:    shipment.ID.String(),
					Error: err.Error(),
				})
				continue
			}
			externalShipmentFactoryMap[shipment.Courier.Code] = es
		}

		shipmentLabel, err := externalShipmentFactoryMap[shipment.Courier.Code].TransformShipmentEntityToShipmentLabel(c, shipment, req.Language)
		if err != nil {
			log.Printf("[GenerateShipmentLabels] failed to transform shipment to protobuf - shipment: %s - error: `%s`\n", shipment.ID.String(), err.Error())
			failedShipmentDTOs = append(failedShipmentDTOs, &dto.GenerateShipmentLabelsResponseDataFailedShipment{
				ID:    shipment.ID.String(),
				Error: err.Error(),
			})
			continue
		}

		shipmentLabels = append(shipmentLabels, shipmentLabel)
	}

	log.Printf("[GenerateShipmentLabels] calling to GRPC: %d/%d shipment labels\n", len(shipmentLabels), total)
	generateResponse, err := s.pdfClient.Generate(c, &pdfpb.GenerateRequest{
		Title:          pdfTitle,
		Language:       req.Language,
		ShipmentLabels: shipmentLabels,
	})

	if err != nil {
		log.Printf("[GenerateShipmentLabels] failed to generate pdf file: `%s`\n", err.Error())
		gin.ResponseSuccess(c, &dto.GenerateShipmentLabelsResponse{
			Data: &dto.GenerateShipmentLabelsResponseData{
				Error:           err.Error(),
				FailedShipments: failedShipmentDTOs,
			},
		})
		return
	}

	if generateResponse.Error != "" {
		log.Printf("[GenerateShipmentLabels] failed to generate pdf file: `%s`\n", generateResponse.Error)
	} else {
		log.Printf("[GenerateShipmentLabels] pdf generated successully: %s - %s - %s\n", pdfTitle, req.Language, generateResponse.URL)
	}

	for _, failed := range generateResponse.FailedShipments {
		failedShipmentDTOs = append(failedShipmentDTOs, &dto.GenerateShipmentLabelsResponseDataFailedShipment{
			ID:    failed.ID,
			Error: failed.Error,
		})
	}

	gin.ResponseSuccess(c, &dto.GenerateShipmentLabelsResponse{
		Data: &dto.GenerateShipmentLabelsResponseData{
			URL:             generateResponse.URL,
			Error:           generateResponse.Error,
			FailedShipments: failedShipmentDTOs,
		},
	})
}
