package sale_order

import (
	"bytes"
	"context"
	"errors"
	"fmt"

	"io"
	"net/http"
	"net/http/httptest"
	"testing"

	SOProto "github.com/AlchemyTelcoSolutions/proto/gen/go/callisto/so/v1"
	"github.com/AlchemyTelcoSolutions/xutils-go/xlogger"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"

	"github.com/AlchemyTelcoSolutions/callisto-so-bff/cmd/app/config"
	"github.com/AlchemyTelcoSolutions/callisto-so-bff/internal/auth"
	auth_mock "github.com/AlchemyTelcoSolutions/callisto-so-bff/internal/auth/mocks"
	"github.com/AlchemyTelcoSolutions/callisto-so-bff/internal/domain/model"
	"github.com/AlchemyTelcoSolutions/callisto-so-bff/internal/proxy"
	proxy_mock "github.com/AlchemyTelcoSolutions/callisto-so-bff/internal/proxy/mocks"
	sale_order_mock "github.com/AlchemyTelcoSolutions/callisto-so-bff/internal/sale_order/mocks"
)

func TestCreateOrdersServiceSaleOrder(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name                   string
		assert                 func(*testing.T, *model.SaleOrderResponse)
		proxyService           func(*testing.T, *http.Request) proxy.ProxyService
		iscallistoSOGRPCClient bool
		callistoSOGRPCClient   SOServiceClient
		funcToCall             func(*Service, *http.Request) *model.SaleOrderResponse
		configEndpoint         string
		request                *http.Request
	}{
		{
			name: "error calling callisto api server so from auction",
			assert: func(t *testing.T, resp *model.SaleOrderResponse) {
				assert.Equal(t, false, resp.Response.Success)
				assert.Equal(t, http.StatusInternalServerError, resp.Code)
			},
			proxyService: func(t *testing.T, r *http.Request) proxy.ProxyService {
				proxySvc := proxy_mock.NewProxyService(t)
				proxySvc.On("SendRequestToClient", r, mock.Anything, map[string]string{}).Return(nil, fmt.Errorf("server fail"))
				return proxySvc
			},
			configEndpoint: "v1/bids/12345/winner",
			funcToCall: func(s *Service, r *http.Request) *model.SaleOrderResponse {
				return s.CreateOrderFromAuction(r, "12345")
			},
			request: getTestRequestFromAuction(),
		},
		{
			name: "error calling callisto api server so from cart",
			assert: func(t *testing.T, resp *model.SaleOrderResponse) {
				assert.Equal(t, false, resp.Response.Success)
				assert.Equal(t, http.StatusInternalServerError, resp.Code)
			},
			proxyService: func(t *testing.T, r *http.Request) proxy.ProxyService {
				proxySvc := proxy_mock.NewProxyService(t)
				proxySvc.On("SendRequestToClient", r, mock.Anything, map[string]string{}).Return(nil, fmt.Errorf("server fail"))
				return proxySvc
			},
			configEndpoint: "v1/bids/12345/winner",
			funcToCall: func(s *Service, r *http.Request) *model.SaleOrderResponse {
				return s.CreateOrder(r)
			},
			request: getTestRequestFromCart(),
		},
		{
			name: "error response from callisto api server",
			assert: func(t *testing.T, resp *model.SaleOrderResponse) {
				assert.Equal(t, false, resp.Response.Success)
				assert.Equal(t, "some api error", resp.Response.Error["message"])
				assert.Equal(t, http.StatusConflict, resp.Code)
			},
			proxyService: func(t *testing.T, r *http.Request) proxy.ProxyService {
				proxySvc := proxy_mock.NewProxyService(t)
				response := &model.ProxyResponse{
					BodyBytes:  []byte(`some api error`),
					StatusCode: http.StatusConflict,
				}
				proxySvc.On("SendRequestToClient", r, mock.Anything, map[string]string{}).Return(response, nil)
				return proxySvc
			},
			configEndpoint: "v1/sale-orders",
			funcToCall: func(s *Service, r *http.Request) *model.SaleOrderResponse {
				return s.CreateOrder(r)
			},
			request: getTestRequestFromCart(),
		},
		{
			name: "success API NIL GRPC Client",
			assert: func(t *testing.T, resp *model.SaleOrderResponse) {
				assert.Equal(t, true, resp.Response.Success)
			},
			proxyService: func(t *testing.T, r *http.Request) proxy.ProxyService {
				proxySvc := proxy_mock.NewProxyService(t)
				response := &model.ProxyResponse{
					BodyBytes:  []byte(`{"success":true,"result":[{"location_id":14,"order_number":80005468,"items":[]}]}`),
					StatusCode: 200,
				}
				proxySvc.On("SendRequestToClient", r, mock.Anything, map[string]string{}).Return(response, nil)
				return proxySvc
			},
			iscallistoSOGRPCClient: false,
			callistoSOGRPCClient:   getGrpcClientCreateResponse(t, false, true, getTestRequestFromCart().Context()),
			configEndpoint:         "v1/create",
			funcToCall: func(s *Service, r *http.Request) *model.SaleOrderResponse {
				return s.CreateOrder(r)
			},
			request: getTestRequestFromCart(),
		},
		{
			name: "success API fail marshal body",
			assert: func(t *testing.T, resp *model.SaleOrderResponse) {
				assert.Equal(t, false, resp.Response.Success)
			},
			proxyService: func(t *testing.T, r *http.Request) proxy.ProxyService {
				proxySvc := proxy_mock.NewProxyService(t)
				response := &model.ProxyResponse{
					BodyBytes:  []byte(`{"success":true,"result":[{"location_id":14,"order_number":80005468,"items":[]}]},`),
					StatusCode: 200,
				}
				proxySvc.On("SendRequestToClient", r, mock.Anything, map[string]string{}).Return(response, nil)
				return proxySvc
			},
			iscallistoSOGRPCClient: false,
			callistoSOGRPCClient:   getGrpcClientCreateResponse(t, false, true, getTestRequestFromCart().Context()),
			configEndpoint:         "v1/create",
			funcToCall: func(s *Service, r *http.Request) *model.SaleOrderResponse {
				return s.CreateOrder(r)
			},
			request: getTestRequestFromCart(),
		},
		{
			name: "success API Fail Proto",
			assert: func(t *testing.T, resp *model.SaleOrderResponse) {
				assert.Equal(t, true, resp.Response.Success)
			},
			proxyService: func(t *testing.T, r *http.Request) proxy.ProxyService {
				proxySvc := proxy_mock.NewProxyService(t)
				response := &model.ProxyResponse{
					BodyBytes:  []byte(getBFFAPIStringResponse(true)),
					StatusCode: 200,
				}
				proxySvc.On("SendRequestToClient", r, mock.Anything, map[string]string{}).Return(response, nil)
				return proxySvc
			},
			iscallistoSOGRPCClient: true,
			callistoSOGRPCClient:   getGrpcClientCreateResponse(t, false, false, getTestRequestFromCart().Context()),
			configEndpoint:         "v1/create",
			funcToCall: func(s *Service, r *http.Request) *model.SaleOrderResponse {
				return s.CreateOrder(r)
			},
			request: getTestRequestFromCart(),
		},
		{
			name: "success API Fail Transformer",
			assert: func(t *testing.T, resp *model.SaleOrderResponse) {
				assert.Equal(t, true, resp.Response.Success)
			},
			proxyService: func(t *testing.T, r *http.Request) proxy.ProxyService {
				proxySvc := proxy_mock.NewProxyService(t)
				response := &model.ProxyResponse{
					BodyBytes:  []byte(getBFFAPIStringResponse(false)),
					StatusCode: 200,
				}
				proxySvc.On("SendRequestToClient", r, mock.Anything, map[string]string{}).Return(response, nil)
				return proxySvc
			},
			iscallistoSOGRPCClient: true,
			callistoSOGRPCClient:   getGrpcClientCreateResponse(t, false, true, getTestRequestFromCart().Context()),
			configEndpoint:         "v1/create",
			funcToCall: func(s *Service, r *http.Request) *model.SaleOrderResponse {
				return s.CreateOrder(r)
			},
			request: getTestRequestFromCart(),
		},
		{
			name: "success API Success Proto",
			assert: func(t *testing.T, resp *model.SaleOrderResponse) {
				assert.Equal(t, true, resp.Response.Success)
			},
			proxyService: func(t *testing.T, r *http.Request) proxy.ProxyService {
				proxySvc := proxy_mock.NewProxyService(t)
				response := &model.ProxyResponse{
					BodyBytes:  []byte(getBFFAPIStringResponse(true)),
					StatusCode: 200,
				}
				proxySvc.On("SendRequestToClient", r, mock.Anything, map[string]string{}).Return(response, nil)
				return proxySvc
			},
			iscallistoSOGRPCClient: true,
			callistoSOGRPCClient:   getGrpcClientCreateResponse(t, true, false, getTestRequestFromCart().Context()),
			configEndpoint:         "v1/create",
			funcToCall: func(s *Service, r *http.Request) *model.SaleOrderResponse {
				return s.CreateOrder(r)
			},
			request: getTestRequestFromCart(),
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			config := &config.Config{}
			config.CallistoAPI.Host = "0.0.0.0"
			config.CallistoAPI.SaleOrder.Create = tt.configEndpoint
			config.CallistoSO.Enabled = tt.iscallistoSOGRPCClient
			proxyService := tt.proxyService(t, tt.request)
			s := NewSaleOrderService().
				SetProxyService(proxyService).
				SetLogger(xlogger.NoOpLogger{}).
				SetConfigs(config).
				SetCallistoSOGRPC(tt.callistoSOGRPCClient)
			resp := tt.funcToCall(s, tt.request)
			tt.assert(t, resp)
		})
	}
}

// getTestRequestFromAuction returns an example http request JSON payload, to create an SO from an auction
func getTestRequestFromAuction() *http.Request {
	jsonString := `{
		"sent":1,
		"shipping_address_id": 1
	}`
	body := io.NopCloser(bytes.NewReader([]byte(jsonString)))
	httpRequest := httptest.NewRequest(http.MethodPut, "http://localhost.com?with_query_string=1&and_second=2", body)
	return httpRequest
}

// getTestRequestFromCart returns an example http request JSOn payload, for the cart
func getTestRequestFromCart() *http.Request {
	jsonString := `{
		"inventories": [
			{
				"id": 8754,
				"qty": 1,
				"price": "72.98",
				"grade_id": 4
			}
		],
		"customer_po": "My Example PO Ref",
		"shipping_address_id": 4143,
		"currency_id": "3",
		"incoterm_id": 1,
		"company_id": 2183
    }`
	body := io.NopCloser(bytes.NewReader([]byte(jsonString)))
	httpRequest := httptest.NewRequest(http.MethodPost, "http://localhost.com?with_query_string=1&and_second=2", body)
	return httpRequest
}

func getGrpcClientCreateResponse(t *testing.T, success, isNil bool, ctx context.Context) SOServiceClient {
	if isNil {
		return nil
	}
	mockClient := sale_order_mock.NewSOServiceClient(t)
	result := &SOProto.CreateSaleOrderResponse{}
	err := fmt.Errorf("error in grpc call")
	if success {
		result.Orders = []*SOProto.CreateSaleOrderResponse_SaleOrdersResponse{
			{
				OrderRef: "orderRef1",
			},
		}
		err = nil
	}
	mockClient.Mock.On("CreateSaleOrder", ctx, mock.Anything).Return(result, err)
	return mockClient
}

func getBFFAPIStringResponse(successBFF bool) string {
	bffData := bffFail()
	if successBFF {
		bffData = bffSuccess()
	}
	return fmt.Sprintf(`{
		%s,
		"success": true,
		"result": [
			{
				"location_id": 14,
				"order_number": 80013851,
				"items": []
			}
		]
	}`, bffData)
}

func bffSuccess() string {
	return `"_bff": {
		"buyerId": "2183",
		"buyerPo": "tumamamemimamuchooo6",
		"currency": "CURRENCY_JPY",
		"totalValue": 72.98,
		"incoterms": "INCOTERM_EXW",
		"source": "cart",
		"shippingAddress": {
			"contactName": "Alfonso Rodriguez",
			"contactNumber": "55306773",
			"address1": "Queens Building",
			"address2": "Hastings Road",
			"city": "London",
			"state": "Leicester",
			"country": "United Kingdom"
		},
		"billingAddress": {
			"contactName": "Alfonso Rodriguez",
			"contactNumber": "55306773",
			"address1": "20 Queens Building Wenlock Road",
			"address2": "12",
			"city": "London",
			"country": "United Kingdom"
		},
		"orders": [
			{
				"locationRef": "14",
				"legacyId": "80013851",
				"buyerId": "2183",
				"sellerId": "1",
				"approvalsNeeded": 1,
				"orderLines": [
					{
						"sku": "PH-AP-IP7000-FAC-128-JB-A1778-D",
						"conditionRef": "4",
						"quantity": 1,
						"baseCurrency": "CURRENCY_USD",
						"baseFloor": 76.92,
						"baseAsp": 88,
						"baseSale": 86.24,
						"baseRate": 0.8462,
						"quoteCurrency": "CURRENCY_JPY",
						"quoteAsp": 74.47,
						"quoteSale": 72.98,
						"quoteDiscount": -1.49,
						"quoteNet": 72.98
					}
				]
			}
		]
	}`
}

func bffFail() string {
	return `"_bff": {
		"buyerIds": "2183s",
		"buyerPos": "tumamamemimamuchooo6s",
		"currencys": "CURRENCY_JPYs",
		"totalValues": "ss",
		"incotermss": "INCOTERM_EXWs",
		"sources": "carts",
		"shippingAddresss": {
			"contactNames": "Alfonso Rodriguezs",
			"contactNumbesr": "sssssssss",
			"address1s": "Queens Buildings",
			"address2s": "Hastings Roads",
			"citys": "Londonsssss",
			"state": "Leicesters",
			"countrys": "United Kingdoms"
		},
		"billingAddresssssss": {
			"contactNames": "Alfonso Rodriguez",
			"contactNumbersss": "55306773",
			"address1s": "20 Queens Building Wenlock Road",
			"address2s": "12",
			"citysss": "London",
			"countryssss": "United Kingdom"
		},
		"orderssdass": [
			{
				"locationRefs": "14",
				"legacyIds": "80013851",
				"buyerIds": "2183",
				"sellerIds": "1",
				"approvalsNeededssss": 1,
				"orderLinessss": [
					{
						"sksuss": "PH-AP-IP7000-FAC-128-JB-A1778-D",
						"conditionRefs": "4",
						"quantitys": 1,
						"baseCurrensdascy": "CURRENCY_USD",
						"baseFloosdasr": 76.92,
						"baseAsps": 88,
						"baseSales": 86.24,
						"baseRates": 0.8462,
						"quoteCurrsasency": "CURRENCY_JPY",
						"quoteAsps": 74.47,
						"quoteSasdasdle": 72.98,
						"quoteDiscounasdast": -1.49,
						"asdasdquoteNet": 72.98
					}
				]
			}
		]
	}`
}

func TestService_GetSOSummary(t *testing.T) {
	mockProxy := proxy_mock.NewProxyService(t)
	mockClient := sale_order_mock.NewSOServiceClient(t)
	mockAuthSvc := auth_mock.NewAuthService(t)

	httpRequest := httptest.NewRequest(http.MethodGet, "http://localhost.com/v1/sale-orders/summary", nil)

	result := model.SaleOrderSummaryResponse{
		Status: model.SOSummaryStatusResponse{
			Details: []model.SOSummaryDetailResponse{
				{
					StatusId: uint64(SOProto.Status_STATUS_APPROVAL),
					Count:    1,
				},
			},
			Groups: model.SOSummaryGroupResponse{
				RequiresAction: 1,
			},
		},
	}

	type args struct {
		req    *http.Request
		params *model.SOSummaryRequest
	}
	tests := []struct {
		name string
		args args
		want *model.Response[model.SaleOrderSummaryResponse]
		fn   func()
	}{
		{
			name: "ShouldError_FailedGetAuthMe",
			args: args{
				req:    httpRequest,
				params: &model.SOSummaryRequest{},
			},
			want: defaultErrorResponse[model.SaleOrderSummaryResponse](nil, "unauthorized", http.StatusUnauthorized),
			fn: func() {
				mockAuthSvc.Mock.On("AuthMeByRequest", mock.Anything).Return(nil, errors.New("failed")).Once()
			},
		},
		{
			name: "ShouldError_FailedGetSummaryToSOClient",
			args: args{
				req:    httpRequest,
				params: &model.SOSummaryRequest{},
			},
			want: defaultErrorResponse[model.SaleOrderSummaryResponse](nil, "failed get summary to callisto-so", http.StatusInternalServerError),
			fn: func() {
				mockAuthSvc.Mock.On("AuthMeByRequest", mock.Anything).Return(&model.AuthMeResponse{CompanyId: 123}, nil).Once()
				mockClient.Mock.On("GetSummary", mock.Anything, &SOProto.GetSummaryRequest{BuyerCompanyId: uint64(123)}).Return(nil, errors.New("failed call to callisto-so client")).Once()
			},
		},
		{
			name: "Success",
			args: args{
				req:    httpRequest,
				params: &model.SOSummaryRequest{},
			},
			want: &model.Response[model.SaleOrderSummaryResponse]{
				Code:    http.StatusOK,
				Success: true,
				Result:  &result,
			},
			fn: func() {
				mockAuthSvc.Mock.On("AuthMeByRequest", mock.Anything).Return(&model.AuthMeResponse{CompanyId: 123}, nil).Once()
				mockClient.Mock.On("GetSummary", mock.Anything, &SOProto.GetSummaryRequest{BuyerCompanyId: uint64(123)}).Return(&SOProto.GetSummaryResponse{
					Details: []*SOProto.GetSummaryResponse_Detail{
						{
							StatusId: uint64(SOProto.Status_STATUS_APPROVAL),
							Count:    1,
						},
					},
					Groups: &SOProto.GetSummaryResponse_Group{RequiresAction: 1},
				}, nil).Once()
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tt.fn()
			s := NewSaleOrderService().
				SetProxyService(mockProxy).
				SetLogger(xlogger.NoOpLogger{}).
				SetConfigs(&config.Config{
					CallistoAPI: config.CallistoAPIConfig{
						Auth: config.AuthAPI{
							Me: "/v1/auth/me",
						}},
				}).
				SetAuthService(mockAuthSvc).
				SetCallistoSOGRPC(mockClient)
			assert.Equalf(t, tt.want, s.GetSOSummary(tt.args.req), "GetSOSummary(%v, %v)", tt.args.req, tt.args.params)
		})
	}
}

func TestServiceSaleOrderExportASNData(t *testing.T) {
	t.Parallel()

	validOrderOwnerID := uint64(123)

	tests := []struct {
		name                   string
		assert                 func(*testing.T, *model.SaleOrderExportASNResponse)
		proxyService           func(*testing.T, *http.Request) proxy.ProxyService
		iscallistoSOGRPCClient bool
		callistoSOGRPCClient   func(t *testing.T, ctx context.Context) SOServiceClient
		authAPI                func(t *testing.T, ctx context.Context) auth.AuthService
		funcToCall             func(*Service, *http.Request) *model.SaleOrderExportASNResponse
		configEndpoint         string
		request                *http.Request
	}{
		{
			name: "order not affiliated",
			assert: func(t *testing.T, resp *model.SaleOrderExportASNResponse) {
				assert.Equal(t, false, resp.Success)
				assert.Equal(t, http.StatusForbidden, resp.Code)
				assert.Equal(t, "forbidden", resp.Error["message"])
			},
			proxyService: func(t *testing.T, r *http.Request) proxy.ProxyService {
				return nil
			},
			iscallistoSOGRPCClient: true,
			configEndpoint:         "/v1/sale-orders/12345/asn/export",
			funcToCall: func(s *Service, r *http.Request) *model.SaleOrderExportASNResponse {
				return s.ExportASNToFile(r, "12345", "csv")
			},
			callistoSOGRPCClient: func(t *testing.T, ctx context.Context) SOServiceClient {
				mockClient := sale_order_mock.NewSOServiceClient(t)
				mockClient.Mock.On("CheckOrderAffiliation", ctx, mock.Anything).Return(&SOProto.CheckOrderAffiliationResponse{BuyerAffiliated: false}, nil)
				return mockClient
			},
			authAPI: func(t *testing.T, ctx context.Context) auth.AuthService {
				mockClient := auth_mock.NewAuthService(t)
				mockClient.Mock.On("AuthMeByRequest", mock.Anything).Return(&model.AuthMeResponse{CompanyId: validOrderOwnerID}, nil)
				return mockClient
			},
			request: getTestRequestForExportASN("csv"),
		},
		{
			name: "error failed to get shipments",
			assert: func(t *testing.T, resp *model.SaleOrderExportASNResponse) {
				assert.Equal(t, false, resp.Success)
				assert.Equal(t, http.StatusInternalServerError, resp.Code)
				assert.Equal(t, "failed to get shipments data", resp.Error["message"])
			},
			proxyService: func(t *testing.T, r *http.Request) proxy.ProxyService {
				return nil
			},
			iscallistoSOGRPCClient: true,
			configEndpoint:         "/v1/sale-orders/12345/asn/export",
			funcToCall: func(s *Service, r *http.Request) *model.SaleOrderExportASNResponse {
				return s.ExportASNToFile(r, "12345", "csv")
			},
			callistoSOGRPCClient: func(t *testing.T, ctx context.Context) SOServiceClient {
				mockClient := sale_order_mock.NewSOServiceClient(t)
				mockClient.Mock.On("CheckOrderAffiliation", ctx, mock.Anything).Return(&SOProto.CheckOrderAffiliationResponse{BuyerAffiliated: true}, nil)
				mockClient.Mock.On("GetShipments", ctx, mock.Anything).Return(&SOProto.GetShipmentsResponse{Shipments: []*SOProto.Shipment{}}, errors.New("some error happen"))
				return mockClient
			},
			authAPI: func(t *testing.T, ctx context.Context) auth.AuthService {
				mockClient := auth_mock.NewAuthService(t)
				mockClient.Mock.On("AuthMeByRequest", mock.Anything).Return(&model.AuthMeResponse{CompanyId: validOrderOwnerID}, nil)
				return mockClient
			},
			request: getTestRequestForExportASN("csv"),
		},
		{
			name: "error failed to get shipments empty shipment",
			assert: func(t *testing.T, resp *model.SaleOrderExportASNResponse) {
				assert.Equal(t, false, resp.Success)
				assert.Equal(t, http.StatusNotFound, resp.Code)
				assert.Equal(t, ErrorNoShipmentFound, resp.Error["message"])
			},
			proxyService: func(t *testing.T, r *http.Request) proxy.ProxyService {
				return nil
			},
			iscallistoSOGRPCClient: true,
			configEndpoint:         "/v1/sale-orders/12345/asn/export",
			funcToCall: func(s *Service, r *http.Request) *model.SaleOrderExportASNResponse {
				return s.ExportASNToFile(r, "12345", "csv")
			},
			callistoSOGRPCClient: func(t *testing.T, ctx context.Context) SOServiceClient {
				mockClient := sale_order_mock.NewSOServiceClient(t)
				mockClient.Mock.On("CheckOrderAffiliation", ctx, mock.Anything).Return(&SOProto.CheckOrderAffiliationResponse{BuyerAffiliated: true}, nil)
				mockClient.Mock.On("GetShipments", ctx, mock.Anything).Return(&SOProto.GetShipmentsResponse{Shipments: []*SOProto.Shipment{}}, nil)
				return mockClient
			},
			authAPI: func(t *testing.T, ctx context.Context) auth.AuthService {
				mockClient := auth_mock.NewAuthService(t)
				mockClient.Mock.On("AuthMeByRequest", mock.Anything).Return(&model.AuthMeResponse{CompanyId: validOrderOwnerID}, nil)
				return mockClient
			},
			request: getTestRequestForExportASN("csv"),
		},
		{
			name: "error failed to get ASN for one of shipments",
			assert: func(t *testing.T, resp *model.SaleOrderExportASNResponse) {
				assert.Equal(t, false, resp.Success)
				assert.Equal(t, http.StatusInternalServerError, resp.Code)
				assert.Equal(t, "failed to get ASN data", resp.Error["message"])
			},
			proxyService: func(t *testing.T, r *http.Request) proxy.ProxyService {
				return nil
			},
			iscallistoSOGRPCClient: true,
			configEndpoint:         "/v1/sale-orders/12345/asn/export",
			funcToCall: func(s *Service, r *http.Request) *model.SaleOrderExportASNResponse {
				return s.ExportASNToFile(r, "12345", "csv")
			},
			callistoSOGRPCClient: func(t *testing.T, ctx context.Context) SOServiceClient {
				mockClient := sale_order_mock.NewSOServiceClient(t)
				mockClient.Mock.On("CheckOrderAffiliation", ctx, mock.Anything).Return(&SOProto.CheckOrderAffiliationResponse{BuyerAffiliated: true}, nil)
				mockClient.Mock.On("GetShipments", ctx, mock.Anything).Return(&SOProto.GetShipmentsResponse{Shipments: []*SOProto.Shipment{
					{
						ShipmentId: 123,
					},
				}}, nil)
				mockClient.Mock.On("GetAsnData", ctx, mock.Anything).Return(&SOProto.GetAsnDataResponse{}, errors.New("some error happen"))
				return mockClient
			},
			authAPI: func(t *testing.T, ctx context.Context) auth.AuthService {
				mockClient := auth_mock.NewAuthService(t)
				mockClient.Mock.On("AuthMeByRequest", mock.Anything).Return(&model.AuthMeResponse{CompanyId: validOrderOwnerID}, nil)
				return mockClient
			},
			request: getTestRequestForExportASN("csv"),
		},
		{
			name: "error no ASN for one of shipments",
			assert: func(t *testing.T, resp *model.SaleOrderExportASNResponse) {
				assert.Equal(t, false, resp.Success)
				assert.Equal(t, http.StatusNotFound, resp.Code)
				assert.Equal(t, ErrorNoASNFound, resp.Error["message"])
			},
			proxyService: func(t *testing.T, r *http.Request) proxy.ProxyService {
				return nil
			},
			iscallistoSOGRPCClient: true,
			configEndpoint:         "/v1/sale-orders/12345/asn/export",
			funcToCall: func(s *Service, r *http.Request) *model.SaleOrderExportASNResponse {
				return s.ExportASNToFile(r, "12345", "csv")
			},
			callistoSOGRPCClient: func(t *testing.T, ctx context.Context) SOServiceClient {
				mockClient := sale_order_mock.NewSOServiceClient(t)
				mockClient.Mock.On("CheckOrderAffiliation", ctx, mock.Anything).Return(&SOProto.CheckOrderAffiliationResponse{BuyerAffiliated: true}, nil)
				mockClient.Mock.On("GetShipments", ctx, mock.Anything).Return(&SOProto.GetShipmentsResponse{Shipments: []*SOProto.Shipment{
					{
						ShipmentId: 123,
					},
					{
						ShipmentId: 234,
					},
				}}, nil)
				mockClient.Mock.On("GetAsnData", ctx, mock.Anything).Return(&SOProto.GetAsnDataResponse{}, nil)
				return mockClient
			},
			authAPI: func(t *testing.T, ctx context.Context) auth.AuthService {
				mockClient := auth_mock.NewAuthService(t)
				mockClient.Mock.On("AuthMeByRequest", mock.Anything).Return(&model.AuthMeResponse{CompanyId: validOrderOwnerID}, nil)
				return mockClient
			},
			request: getTestRequestForExportASN("csv"),
		},
		{
			name: "error unsupported file format",
			assert: func(t *testing.T, resp *model.SaleOrderExportASNResponse) {
				assert.Equal(t, false, resp.Success)
				assert.Equal(t, http.StatusUnsupportedMediaType, resp.Code)
				assert.Equal(t, "file format is not supported", resp.Error["message"])
			},
			proxyService: func(t *testing.T, r *http.Request) proxy.ProxyService {
				return nil
			},
			iscallistoSOGRPCClient: true,
			configEndpoint:         "/v1/sale-orders/12345/asn/export",
			funcToCall: func(s *Service, r *http.Request) *model.SaleOrderExportASNResponse {
				return s.ExportASNToFile(r, "12345", "PDF")
			},
			callistoSOGRPCClient: func(t *testing.T, ctx context.Context) SOServiceClient {
				mockClient := sale_order_mock.NewSOServiceClient(t)
				return mockClient
			},
			authAPI: func(t *testing.T, ctx context.Context) auth.AuthService {
				mockClient := auth_mock.NewAuthService(t)
				return mockClient
			},
			request: getTestRequestForExportASN("PDF"),
		},
		{
			name: "successfully generate ASN data into file",
			assert: func(t *testing.T, resp *model.SaleOrderExportASNResponse) {
				assert.Equal(t, true, resp.Success)
				assert.Equal(t, 0, resp.Code)
				assert.Equal(t, `sku,product_name,serial,imei
TB-AP-PADP1N-FAC-256-SL-A1674-Z,iPad Pro 9.7 Inch 256GB WiFi & Cellular - Grade C,DMPRGB3ZGXQ8,3.55652E+14
`, string(resp.File.Content))
			},
			proxyService: func(t *testing.T, r *http.Request) proxy.ProxyService {
				return nil
			},
			iscallistoSOGRPCClient: true,
			configEndpoint:         "/v1/sale-orders/12345/asn/export",
			funcToCall: func(s *Service, r *http.Request) *model.SaleOrderExportASNResponse {
				return s.ExportASNToFile(r, "12345", "csv")
			},
			callistoSOGRPCClient: func(t *testing.T, ctx context.Context) SOServiceClient {
				mockClient := sale_order_mock.NewSOServiceClient(t)
				mockClient.Mock.On("CheckOrderAffiliation", ctx, mock.Anything).Return(&SOProto.CheckOrderAffiliationResponse{BuyerAffiliated: true}, nil)
				mockClient.Mock.On("GetShipments", ctx, mock.Anything).Return(&SOProto.GetShipmentsResponse{Shipments: []*SOProto.Shipment{
					{
						ShipmentId: 123,
					},
				}}, nil)
				mockClient.Mock.On("GetAsnData", ctx, mock.Anything).Return(&SOProto.GetAsnDataResponse{
					AsnItems: []*SOProto.AsnItem{
						{
							Sku:              "TB-AP-PADP1N-FAC-256-SL-A1674-Z",
							Serial:           "DMPRGB3ZGXQ8",
							Imei:             "3.55652E+14",
							NonSerial:        false,
							ProductName:      "iPad Pro 9.7 Inch 256GB WiFi & Cellular - Grade C",
							ShippingCartonId: "",
						},
					},
				}, nil)
				return mockClient
			},
			authAPI: func(t *testing.T, ctx context.Context) auth.AuthService {
				mockClient := auth_mock.NewAuthService(t)
				mockClient.Mock.On("AuthMeByRequest", mock.Anything).Return(&model.AuthMeResponse{CompanyId: validOrderOwnerID}, nil)
				return mockClient
			},
			request: getTestRequestForExportASN("csv"),
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			config := &config.Config{}
			config.CallistoSO.Enabled = tt.iscallistoSOGRPCClient
			proxyService := tt.proxyService(t, tt.request)
			s := NewSaleOrderService().
				SetProxyService(proxyService).
				SetLogger(xlogger.NoOpLogger{}).
				SetConfigs(config).
				SetCallistoSOGRPC(tt.callistoSOGRPCClient(t, context.Background())).
				SetAuthService(tt.authAPI(t, context.Background()))
			resp := tt.funcToCall(s, tt.request)
			tt.assert(t, resp)
		})
	}
}

func getTestRequestForExportASN(fileFormat string) *http.Request {
	endpoint := fmt.Sprintf("http://localhost.com?format=%s", fileFormat)
	httpRequest := httptest.NewRequest(http.MethodGet, endpoint, nil)
	return httpRequest
}
