package handlers_test

import (
	"encoding/base64"
	"fmt"
	"net/http"
	"testing"

	"otaupdate/handlers"
	"otaupdate/services"

	"github.com/fiskerinc/cloud-services/pkg/common"
	dbm "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks"
	"github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc"
	htc "github.com/fiskerinc/cloud-services/pkg/httpclient/tester"
	"github.com/fiskerinc/cloud-services/pkg/kafka"
	km "github.com/fiskerinc/cloud-services/pkg/kafka/mock"
	"github.com/fiskerinc/cloud-services/pkg/mongo"
	"github.com/fiskerinc/cloud-services/pkg/redis"
	rm "github.com/fiskerinc/cloud-services/pkg/redis/tester"
	th "github.com/fiskerinc/cloud-services/pkg/testhelper"
	"github.com/fiskerinc/cloud-services/pkg/testrunner"
	uhelpers "github.com/fiskerinc/cloud-services/pkg/usecase_helpers"
	"google.golang.org/protobuf/proto"
)

func TestFleetUpdateAdd(t *testing.T) {
	client, err := services.GetMongoClient()
	if err != nil {
		t.Error(err)
		return
	}
	redis.MockRedisConnection()
	vin1 := "1G1FP87S3GN100062"
	vin2 := "TESTVIN1234567891"
	mockDB := dbm.MockCarUpdates{}
	mockKafka := km.KafkaMock{}
	mockRedis := rm.MockRedis{}
	mockMongo := mongo.NewFleetsCollection(
		&mongo.MockCollection{
			AggregateObject: []mongo.Fleet{
				{
					Name: "Grande",
					Vehicles: []string{
						vin1,
						vin2,
					},
				},
			},
		},
	)
	client.SetFleets(mockMongo)
	services.GetDB().SetCarUpdates(&mockDB)
	services.SetKafkaProducer(&mockKafka)
	services.SetRedisClientPool(rm.NewMockClientPool(&mockRedis))
	otaUpdateKey1 := common.Service.Key(vin1)
	otaUpdateKey2 := common.Service.Key(vin2)
	attendentTopic := kafka.AttendantServiceGRPCKafka
	updateMsg := &kafka_grpc.GRPC_AttendantPayload_UpdateManifest{
		UpdateManifest: &kafka_grpc.UpdateManifest{
			CarUpdateId: 1,
		},
	}
	kafkaMSG := kafka_grpc.GRPC_AttendantPayload{
		Handler: "send_manifest",
		Data:    updateMsg,
	}

	binaryPayload, _ := proto.Marshal(&kafkaMSG)
	sEnc := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(binaryPayload))
	standardManifest := common.UpdateManifest{
		ID:         100,
		Name:       "TEST",
		Version:    "10000",
		Type:       "standard",
		Country:    "US",
		PowerTrain: "MD23",
		Restraint:  "None",
		Model:      "Ocean",
		Trim:       "Sport",
		Year:       2022,
		BodyType:   "truck",
	}
	forcedManifest := standardManifest
	forcedManifest.Type = "forced"

	tests := []testrunner.TestCase{
		{
			Name: "Bad car ids",
			HttpTestCase: &htc.HttpTestCase{
				Request: th.MakeTestRequest(http.MethodPost, "http://example.com/fleetupdate", uhelpers.JSONFleetUpdatesRequest{
					UpdateManifestID: 1,
				}),
				ExpectedStatus:   http.StatusBadRequest,
				ExpectedResponse: `{"message":"FleetNames required","error":"Bad Request"}`,
			},
		},
		{
			Name: "No data",
			HttpTestCase: &htc.HttpTestCase{
				Request:          th.MakeTestRequest(http.MethodPost, "http://example.com/fleetupdate", nil),
				ExpectedStatus:   http.StatusBadRequest,
				ExpectedResponse: `{"message":"UpdateManifestID required. FleetNames required","error":"Bad Request"}`,
			},
		},
		{
			Name: "Bad package ids",
			HttpTestCase: &htc.HttpTestCase{
				Request: th.MakeTestRequest(http.MethodPost, "http://example.com/fleetupdate", uhelpers.JSONFleetUpdatesRequest{
					FleetNames: []string{"Slick Grande"},
				}),
				ExpectedStatus:   http.StatusBadRequest,
				ExpectedResponse: `{"message":"UpdateManifestID required. FleetNames[0] fleet ","error":"Bad Request"}`,
			},
		},
		{
			Name: "Good data standard manifest id",
			HttpTestCase: &htc.HttpTestCase{
				Request: th.MakeTestRequest(http.MethodPost, "http://example.com/fleetupdate", uhelpers.JSONFleetUpdatesRequest{
					UpdateManifestID: 1,
					FleetNames:       []string{"Slick"},
				}),
				ExpectedStatus:   http.StatusOK,
				ExpectedResponse: `{"data":["Slick"]}`,
			},
			DBTestCase: &dbm.DBTestCase{
				SetupMockResponse: func() {
					services.GetDB().SetUpdateManifests(&dbm.MockUpdateManifests{
						LoadResponse: &standardManifest,
					})
				},
			},
			RedisTestCase: &rm.RedisTestCase{},
			KafkaTestCase: &km.KafkaTestCase{
				ExpectedProduceMessages: map[string]map[string]interface{}{
					attendentTopic: {
						otaUpdateKey1: sEnc,
						otaUpdateKey2: sEnc,
					},
				},
			},
		},
		{
			Name: "Error",
			HttpTestCase: &htc.HttpTestCase{
				Request: th.MakeTestRequest(http.MethodPost, "http://example.com/fleetupdate", uhelpers.JSONFleetUpdatesRequest{
					UpdateManifestID: 1,
					FleetNames:       []string{"Slick"},
				}),
				ExpectedStatus:   http.StatusServiceUnavailable,
				ExpectedResponse: `{"message":"something went wrong","error":"Service Unavailable"}`,
			},
			DBTestCase: &dbm.DBTestCase{
				MockError: fmt.Errorf("something went wrong"),
			},
		},
	}

	for _, test := range tests {
		mockRedis.Reset()
		mockKafka.Reset()

		if test.DBTestCase != nil {
			test.DBTestCase.SetupDB(&mockDB)
		}
		if test.RedisTestCase != nil {
			test.RedisTestCase.SetupRedis(&mockRedis)
		}
		if test.KafkaTestCase != nil {
			test.KafkaTestCase.Setup(&mockKafka)
		}

		if test.HttpTestCase != nil {
			w := test.HttpTestCase.Test(handlers.HandleFleetUpdatesAdd)
			test.HttpTestCase.ValidateHttp(t, test.Name, w)
		}

		if test.DBTestCase != nil {
			test.DBTestCase.Validate(t, test.Name, &mockDB)
		}
		if test.RedisTestCase != nil {
			test.RedisTestCase.Validate(t, test.Name, &mockRedis)
		}
		if test.KafkaTestCase != nil {
			test.KafkaTestCase.Validate(t, test.Name, &mockKafka)
		}
	}
}
