package api_test

import (
	"encoding/json"
	"net/http"
	"testing"

	"github.com/AlchemyTelcoSolutions/callisto-so-bff/cmd/app/config"

	"github.com/AlchemyTelcoSolutions/callisto-so-bff/api/v1"
	"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"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

func TestServerSaleOrders(t *testing.T) {
	t.Parallel()

	tests := []baseHandlerTestCase{
		{
			name:       "post_sale_order_success",
			statusCode: http.StatusOK,
			method:     http.MethodPost,
			route:      buildUrl("/callisto/v1/sale-orders", map[string]string{}),
			mockFunc: func(c *baseHandlerTestCase) {
				c.saleOrderService.EXPECT().
					CreateOrder(mock.Anything).
					Return(
						&model.SaleOrderResponse{
							Code: http.StatusOK,
							Response: &model.CallistoApiResponse{
								Success: true,
								Result:  []interface{}{"is_success"},
							},
						}).
					Once()
			},
			customTestFunc: func(t *testing.T, response []byte, c *baseHandlerTestCase) {
				var resp api.PostSaleOrdersRestResponse
				_ = json.Unmarshal(response, &resp)
				assert.Equal(t, true, resp.Success)
			},
			proxySvc: func(t *testing.T) proxy.ProxyService {
				return nil
			},
		},
		{
			name:       "post_sale_order_fail",
			statusCode: http.StatusInternalServerError,
			method:     http.MethodPost,
			route:      buildUrl("/callisto/v1/sale-orders", map[string]string{}),
			mockFunc: func(c *baseHandlerTestCase) {
				c.saleOrderService.EXPECT().
					CreateOrder(mock.Anything).
					Return(
						&model.SaleOrderResponse{
							Code: http.StatusInternalServerError,
							Response: &model.CallistoApiResponse{
								Success: false,
								Error:   map[string]interface{}{"message": "fail"},
							},
						}).
					Once()
			},
			customTestFunc: func(t *testing.T, response []byte, c *baseHandlerTestCase) {
				var resp api.PostSaleOrdersRestResponse
				_ = json.Unmarshal(response, &resp)
				assert.Equal(t, false, resp.Success)
			},
			proxySvc: func(t *testing.T) proxy.ProxyService {
				return nil
			},
		},
		{
			name:       "put_create_order_from_auction_fail",
			statusCode: http.StatusInternalServerError,
			method:     http.MethodPut,
			route:      buildUrl("/callisto/v1/bids/123456/winner", map[string]string{}),
			mockFunc: func(c *baseHandlerTestCase) {
				c.saleOrderService.EXPECT().
					CreateOrderFromAuction(mock.Anything, "123456").
					Return(
						&model.SaleOrderResponse{
							Code: http.StatusInternalServerError,
							Response: &model.CallistoApiResponse{
								Success: false,
								Error:   map[string]interface{}{"message": "fail"},
							},
						}).
					Once()
			},
			customTestFunc: func(t *testing.T, response []byte, c *baseHandlerTestCase) {
				var resp api.PostSaleOrdersRestResponse
				_ = json.Unmarshal(response, &resp)
				assert.Equal(t, false, resp.Success)
			},
			proxySvc: func(t *testing.T) proxy.ProxyService {
				return nil
			},
		},
		{
			name:       "put_create_order_from_auction_success",
			statusCode: http.StatusOK,
			method:     http.MethodPut,
			route:      buildUrl("/callisto/v1/bids/12345/winner", map[string]string{}),
			mockFunc: func(c *baseHandlerTestCase) {
				c.saleOrderService.EXPECT().
					CreateOrderFromAuction(mock.Anything, "12345").
					Return(
						&model.SaleOrderResponse{
							Code: http.StatusOK,
							Response: &model.CallistoApiResponse{
								Success: true,
								Result:  []interface{}{"is_success"},
							},
						}).
					Once()
			},
			customTestFunc: func(t *testing.T, response []byte, c *baseHandlerTestCase) {
				var resp api.PostSaleOrdersRestResponse
				_ = json.Unmarshal(response, &resp)
				assert.Equal(t, true, resp.Success)
			},
			proxySvc: func(t *testing.T) proxy.ProxyService {
				return nil
			},
		},
		{
			name:       "post_create_order_proxy_call",
			statusCode: http.StatusOK,
			method:     http.MethodPost,
			route:      buildUrl("/callisto/v1/bids/12345/winner", map[string]string{}),
			mockFunc:   func(c *baseHandlerTestCase) {},
			customTestFunc: func(t *testing.T, response []byte, c *baseHandlerTestCase) {
				var resp api.PostSaleOrdersRestResponse
				_ = json.Unmarshal(response, &resp)
				assert.Equal(t, true, resp.Success)
			},
			proxySvc: func(t *testing.T) proxy.ProxyService {
				mockProxy := proxy_mock.NewProxyService(t)
				mockProxy.On("ForwardProxy", mock.Anything, "/callisto/v1/bids/12345/winner").
					Return(&model.ProxyResponse{
						BodyBytes:  []byte(`{"success":true,"result":[{"location_id":14,"order_number":80005468,"items":[]}]}`),
						StatusCode: 200,
					},
					)
				return mockProxy
			},
		},
	}

	for _, test := range tests {
		t.Run(test.name, test.genericHandlerTestFunc())
	}
}

func TestServerGetSOSummary(t *testing.T) {
	t.Parallel()

	tests := []baseHandlerTestCase{
		{
			name:       "get_so_summary_success",
			statusCode: http.StatusOK,
			method:     http.MethodGet,
			route:      buildUrl("/callisto/v1/sale-orders/summary", map[string]string{}),
			config: &config.Config{
				HTTP: config.HTTPServerConfig{
					ApiPrefix: "/callisto",
					Proxy: config.Proxy{
						IsEnabled: true,
					}},
				CallistoSO: config.CallistoSOConfig{
					Enabled: true,
					Config: config.CallistoSORPCConfig{
						GetSOSummary: config.FeatureFlag{
							IsEnabled: true,
						},
					},
				},
			},
			mockFunc: func(c *baseHandlerTestCase) {
				c.saleOrderService.EXPECT().
					GetSOSummary(mock.Anything).
					Return(
						&model.Response[model.SaleOrderSummaryResponse]{
							Success: true,
							Code:    http.StatusOK,
							Result: &model.SaleOrderSummaryResponse{
								Status: model.SOSummaryStatusResponse{
									Details: []model.SOSummaryDetailResponse{},
									Groups:  model.SOSummaryGroupResponse{},
								},
							},
						}).
					Once()
			},
			proxySvc: func(t *testing.T) proxy.ProxyService {
				return nil
			},
		},
		{
			name:       "get_so_summary_failed",
			statusCode: http.StatusInternalServerError,
			method:     http.MethodGet,
			route:      buildUrl("/callisto/v1/sale-orders/summary", map[string]string{}),
			config: &config.Config{
				HTTP: config.HTTPServerConfig{
					ApiPrefix: "/callisto",
					Proxy: config.Proxy{
						IsEnabled: true,
					}},
				CallistoSO: config.CallistoSOConfig{
					Enabled: true,
					Config: config.CallistoSORPCConfig{
						GetSOSummary: config.FeatureFlag{
							IsEnabled: true,
						},
					},
				},
			},
			mockFunc: func(c *baseHandlerTestCase) {
				c.saleOrderService.EXPECT().
					GetSOSummary(mock.Anything).
					Return(
						&model.Response[model.SaleOrderSummaryResponse]{
							Success: false,
							Code:    http.StatusInternalServerError,
							Result:  nil,
						}).
					Once()
			},
			proxySvc: func(t *testing.T) proxy.ProxyService {
				return nil
			},
		},
		{
			name:       "get_so_summary_success_forward_to_proxy",
			statusCode: http.StatusOK,
			method:     http.MethodGet,
			route:      buildUrl("/callisto/v1/sale-orders/summary", map[string]string{}),
			config: &config.Config{
				HTTP: config.HTTPServerConfig{
					ApiPrefix: "/callisto",
					Proxy: config.Proxy{
						IsEnabled: true,
					}},
				CallistoSO: config.CallistoSOConfig{
					Enabled: true,
					Config: config.CallistoSORPCConfig{
						GetSOSummary: config.FeatureFlag{
							IsEnabled: false,
						},
					},
				},
			},
			mockFunc: func(c *baseHandlerTestCase) {

			},
			proxySvc: func(t *testing.T) proxy.ProxyService {
				mockProxy := proxy_mock.NewProxyService(t)
				mockProxy.On("ForwardProxy", mock.Anything, "/callisto/v1/sale-orders/summary").
					Return(&model.ProxyResponse{
						BodyBytes:  []byte(`{"result":{"status":{"details":[{"status_id":3,"status":"Invoicing","count":1},{"status_id":9,"status":"Cancelled","count":2},{"status_id":5,"status":"Payment Confirmation","count":2}],"groups":{"requires_action":0,"processing":3,"complete":0,"cancelled":2}}},"success":true}`),
						StatusCode: 200,
					},
					)
				return mockProxy
			},
		},
	}

	for _, test := range tests {
		t.Run(test.name, test.genericHandlerTestFunc())
	}
}

func TestServerExportASNData(t *testing.T) {
	t.Parallel()

	tests := []baseHandlerTestCase{
		{
			name:       "export_sale_order_asn_success",
			statusCode: http.StatusOK,
			method:     http.MethodGet,
			route: buildUrl("/callisto/v1/sale-orders/123456/asn/export", map[string]string{
				"format": "csv",
			}),
			config: nil,
			mockFunc: func(c *baseHandlerTestCase) {
				c.saleOrderService.EXPECT().
					ExportASNToFile(mock.Anything, "123456", "csv").
					Return(
						&model.SaleOrderExportASNResponse{
							Code:    http.StatusOK,
							Success: true,
							File: &model.File{
								Content:  []byte("sku,product_name,serial,imei"),
								Name:     "ASN-123456",
								MIMEType: "text/csv",
							},
						}).
					Once()
			},
			customTestFunc: func(t *testing.T, response []byte, c *baseHandlerTestCase) {
				assert.Equal(t, []byte("sku,product_name,serial,imei"), response)
			},
			proxySvc: func(t *testing.T) proxy.ProxyService {
				return nil
			},
		},
		{
			name:       "export_sale_order_asn_fail",
			statusCode: http.StatusUnsupportedMediaType,
			method:     http.MethodGet,
			route: buildUrl("/callisto/v1/sale-orders/123456/asn/export", map[string]string{
				"format": "json",
			}),
			config: nil,
			mockFunc: func(c *baseHandlerTestCase) {
				c.saleOrderService.EXPECT().
					ExportASNToFile(mock.Anything, "123456", "json").
					Return(
						&model.SaleOrderExportASNResponse{
							Code:    http.StatusUnsupportedMediaType,
							Success: false,
							File:    nil,
							Error:   map[string]interface{}{"message": "file format is not supported"},
						}).
					Once()
			},
			customTestFunc: func(t *testing.T, response []byte, c *baseHandlerTestCase) {
				var resp map[string]interface{}
				_ = json.Unmarshal(response, &resp)
				assert.Equal(t, "file format is not supported", resp["message"])
			},
			proxySvc: func(t *testing.T) proxy.ProxyService {
				return nil
			},
		},
		{
			name:       "export_sale_order_asn_success_with_no_name_set",
			statusCode: http.StatusOK,
			method:     http.MethodGet,
			route: buildUrl("/callisto/v1/sale-orders/123456/asn/export", map[string]string{
				"format": "csv",
			}),
			config: nil,
			mockFunc: func(c *baseHandlerTestCase) {
				c.saleOrderService.EXPECT().
					ExportASNToFile(mock.Anything, "123456", "csv").
					Return(
						&model.SaleOrderExportASNResponse{
							Code:    http.StatusOK,
							Success: true,
							File: &model.File{
								Content:  []byte("sku,product_name,serial,imei"),
								Name:     "",
								MIMEType: "text/csv",
							},
						}).
					Once()
			},
			customTestFunc: func(t *testing.T, response []byte, c *baseHandlerTestCase) {
				assert.Equal(t, []byte("sku,product_name,serial,imei"), response)
			},
			proxySvc: func(t *testing.T) proxy.ProxyService {
				return nil
			},
		},
	}

	for _, test := range tests {
		t.Run(test.name, test.genericHandlerTestFunc())
	}
}
