package handlers_test

import (
	"fmt"
	"testing"

	"github.com/fiskerinc/cloud-services/services/attendant/handlers"
	"github.com/fiskerinc/cloud-services/services/attendant/services"

	"github.com/fiskerinc/cloud-services/pkg/common"
	"github.com/fiskerinc/cloud-services/pkg/redis"
	"github.com/fiskerinc/cloud-services/pkg/redis/tester"
	"github.com/fiskerinc/cloud-services/pkg/testhelper"
)

func TestCarUpdateProgressFunctional(t *testing.T) {
	t.Skip()
	testVIN := "WBSEH93466B798124"
	conn := tester.NewRedisMock()
	db := services.GetDB()
	manifest, carUpdateID, err := setupCarUpdateProgressFunc(db, testVIN)
	if err != nil {
		panic(err)
	}
	defer func() {
		if carUpdateID > 0 {
			db.GetCarUpdates().Delete(&common.CarUpdate{ID: carUpdateID})
			conn.Delete(redis.CarUpdateStatusTBOXHashKey(carUpdateID), redis.CarUpdateStatusHMIHashKey(carUpdateID))
		}
		if manifest != nil && manifest.ID > 0 {
			db.GetUpdateManifests().Delete(manifest)
		}
	}()

	ka := services.NewKeepAwakeService()
	ka.SetService(&services.MockKeepAwakeImplementation{})

	type testCase struct {
		Name                  string
		Device                common.Device
		Payload               string
		ExpectedMsg           string
		ExpectInstalled       int
		ExpectInstallTotal    int
		ExpectDBStatus        string
		ExpectDownloadCurrent uint64
		ExpectDownloadTotal   uint64
		ExpectErrorCode       int
	}

	tests := []testCase{
		{
			Name:   "manifest_received",
			Device: common.TRex,
			Payload: fmt.Sprintf(`{
				"car_update_id": %d,
				"msg": "manifest_received"
			}`, carUpdateID),
			ExpectedMsg:    "manifest_received",
			ExpectDBStatus: "manifest_received",
		},
		{
			Name:   "install_approval_await",
			Device: common.TRex,
			Payload: fmt.Sprintf(`{
				"car_update_id": %d,
				"msg": "install_approval_await"
			}`, carUpdateID),
			ExpectedMsg:    "install_approval_await",
			ExpectDBStatus: "install_approval_await",
		},
		{
			Name:   "other error",
			Device: common.TRex,
			Payload: fmt.Sprintf(`{
				"car_update_id": %d,
				"ecu": "TEST",
				"installed": 3,
				"total_files": 10,
				"msg": "other error",
				"err": -100
			}`, carUpdateID),
			ExpectedMsg:        "other error",
			ExpectDBStatus:     "other error",
			ExpectInstalled:    3,
			ExpectInstallTotal: 10,
			ExpectErrorCode:    -100,
		},
		{
			Name:   "download_start",
			Device: common.TRex,
			Payload: fmt.Sprintf(`{
				"car_update_id": %d,
				"ecu": "TEST",
				"file_current": 0,
				"file_total": 100,
				"package_current": 0,
				"package_total": 100,
				"msg": "download_start",
				"err": 0
			}`, carUpdateID),
			ExpectedMsg:           "downloading",
			ExpectDBStatus:        "package_download_start",
			ExpectDownloadCurrent: 0,
			ExpectDownloadTotal:   100,
		},
		{
			Name:   "downloading",
			Device: common.TRex,
			Payload: fmt.Sprintf(`{
				"car_update_id": %d,
				"ecu": "TEST",
				"file_current": 0,
				"file_total": 100,
				"package_current": 30,
				"package_total": 100,
				"msg": "downloading",
				"err": 0
			}`, carUpdateID),
			ExpectedMsg:           "downloading",
			ExpectDBStatus:        "package_download_start",
			ExpectDownloadCurrent: 30,
			ExpectDownloadTotal:   100,
		},
		{
			Name:   "download_complete",
			Device: common.TRex,
			Payload: fmt.Sprintf(`{
				"car_update_id": %d,
				"ecu": "TEST",
				"file_current": 100,
				"file_total": 100,
				"package_current": 900,
				"package_total": 1000,
				"msg": "download_complete",
				"err": 0
			}`, carUpdateID),
			ExpectedMsg:           "downloading",
			ExpectDBStatus:        "package_download_start",
			ExpectDownloadCurrent: 900,
			ExpectDownloadTotal:   1000,
		},
		{
			Name:   "package_download_complete",
			Device: common.TRex,
			Payload: fmt.Sprintf(`{
				"car_update_id": %d,
				"ecu": "TEST",
				"file_current": 100,
				"file_total": 100,
				"package_current": 1000,
				"package_total": 1000,
				"msg": "download_complete",
				"err": 0
			}`, carUpdateID),
			ExpectedMsg:           "package_download_complete",
			ExpectDBStatus:        "package_download_complete",
			ExpectDownloadCurrent: 1000,
			ExpectDownloadTotal:   1000,
		},
		{
			Name:   "download_error",
			Device: common.TRex,
			Payload: fmt.Sprintf(`{
				"car_update_id": %d,
				"ecu": "TEST",
				"file_current": 0,
				"file_total": 100,
				"package_current": 0,
				"package_total": 1000,
				"msg": "download_error",
				"err": 0
			}`, carUpdateID),
			ExpectedMsg:           "download_error",
			ExpectDBStatus:        "download_error",
			ExpectDownloadCurrent: 0,
			ExpectDownloadTotal:   1000,
		},
		{
			Name:   "install_start",
			Device: common.TRex,
			Payload: fmt.Sprintf(`{
				"car_update_id": %d,
				"ecu": "TEST",
				"installed": 0,
				"total_files": 10,
				"msg": "install_start",
				"err": 0
			}`, carUpdateID),
			ExpectedMsg:        "installing",
			ExpectDBStatus:     "package_install_start",
			ExpectInstalled:    0,
			ExpectInstallTotal: 10,
		},
		{
			Name:   "installing",
			Device: common.TRex,
			Payload: fmt.Sprintf(`{
				"car_update_id": %d,
				"ecu": "TEST",
				"installed": 2,
				"total_files": 10,
				"msg": "installing",
				"err": 0
			}`, carUpdateID),
			ExpectedMsg:        "installing",
			ExpectDBStatus:     "package_install_start",
			ExpectInstalled:    2,
			ExpectInstallTotal: 10,
		},
		{
			Name:   "install_complete",
			Device: common.TRex,
			Payload: fmt.Sprintf(`{
				"car_update_id": %d,
				"ecu": "TEST",
				"installed": 9,
				"total_files": 10,
				"msg": "install_complete",
				"err": 0
			}`, carUpdateID),
			ExpectedMsg:        "installing",
			ExpectDBStatus:     "package_install_start",
			ExpectInstalled:    9,
			ExpectInstallTotal: 10,
		},
		{
			Name:   "package_install_complete",
			Device: common.TRex,
			Payload: fmt.Sprintf(`{
				"car_update_id": %d,
				"ecu": "TEST",
				"installed": 10,
				"total_files": 10,
				"msg": "install_complete",
				"err": 0
			}`, carUpdateID),
			ExpectedMsg:        "package_install_complete",
			ExpectDBStatus:     "package_install_complete",
			ExpectInstalled:    10,
			ExpectInstallTotal: 10,
		},
		{
			Name:   "install_error",
			Device: common.TRex,
			Payload: fmt.Sprintf(`{
				"car_update_id": %d,
				"ecu": "TEST",
				"installed": 3,
				"total_files": 10,
				"msg": "install_error",
				"err": 0
			}`, carUpdateID),
			ExpectedMsg:        "install_error",
			ExpectDBStatus:     "install_error",
			ExpectInstalled:    3,
			ExpectInstallTotal: 10,
		},
		{
			Name:   "package_download_complete",
			Device: common.HMI,
			Payload: fmt.Sprintf(`{
				"car_update_id":%d,
				"ecu":"ICC",
				"file_current":null,
				"file_total":null,
				"package_current":920639485,
				"package_total":920639485,
				"installed":null,
				"total_files":null,
				"msg":"package_download_complete",
				"err":null
				}`, carUpdateID),
			ExpectedMsg:           "package_download_complete",
			ExpectDBStatus:        "package_download_complete",
			ExpectInstalled:       3,
			ExpectInstallTotal:    10,
			ExpectDownloadCurrent: 920639485,
			ExpectDownloadTotal:   920639485,
		},
	}

	keys := make([]string, 1)
	statuses := make([]interface{}, 1)

	for _, test := range tests {
		err := handlers.CarUpdateProgressStatus(db, ka, test.Device, testVIN, []byte(test.Payload))
		if err != nil {
			t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s output error", test.Device, test.Name), nil, err)
		}

		status := common.CarUpdateProgress{}
		keys[0] = redis.CarUpdateStatusHashKey(carUpdateID)
		statuses[0] = &status

		err = conn.GetObjectsMulti(keys, statuses)
		if err != nil {
			t.Errorf(testhelper.TestErrorTemplate, "GetObjectsMulti", nil, err)
			return
		}
		if status.Status != test.ExpectedMsg {
			t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s Status", test.Device, test.Name), test.ExpectedMsg, status.Status)
		}
		if status.InstalledFiles != test.ExpectInstalled {
			t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s InstalledFiles", test.Device, test.Name), test.ExpectInstalled, status.InstalledFiles)
		}
		if status.TotalFiles != test.ExpectInstallTotal {
			t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s TotalFiles", test.Device, test.Name), test.ExpectInstallTotal, status.TotalFiles)
		}
		if status.PackageCurrent != test.ExpectDownloadCurrent {
			t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s PackageCurrent", test.Device, test.Name), test.ExpectDownloadCurrent, status.PackageCurrent)
		}
		if status.PackageTotal != test.ExpectDownloadTotal {
			t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s PackageTotal", test.Device, test.Name), test.ExpectDownloadTotal, status.PackageTotal)
		}
		if status.ErrorCode != test.ExpectErrorCode {
			t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s ErrorCode", test.Device, test.Name), test.ExpectErrorCode, status.ErrorCode)
		}

		cu, err := db.GetCarUpdates().SelectByID(carUpdateID)
		if err != nil {
			t.Errorf(testhelper.TestErrorTemplate, "Get from DB", nil, err)
			return
		}
		if cu.Status != test.ExpectDBStatus {
			t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s DB Status", test.Device, test.Name), test.ExpectDBStatus, cu.Status)
		}
		if cu.ErrorCode != test.ExpectErrorCode {
			t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("[%v] %s DB ErrorCode", test.Device, test.Name), test.ExpectErrorCode, cu.ErrorCode)
		}
	}
}

func setupCarUpdateProgressFunc(db *services.DB, vin string) (*common.UpdateManifest, int64, error) {
	_, err := db.GetCars().SelectOrInsert(&common.Car{
		VIN:   vin,
		Model: "Ocean",
		Year:  2022,
		Trim:  "Sport",
	})
	if err != nil {
		return nil, 0, err
	}

	manifest := common.UpdateManifest{
		Name:         "TestCarUpdateProgressFunctional",
		Version:      "1000",
		Description:  "For TestCarUpdateProgressFunctional",
		ReleaseNotes: "http://releasenotes.com",
		Country:      "US",
		PowerTrain:   "MD23",
		Restraint:    "None",
		Model:        "Ocean",
		Trim:         "Sport",
		Year:         2022,
		BodyType:     "truck",
	}
	_, err = db.GetUpdateManifests().Insert(&manifest)
	if err != nil {
		return nil, 0, err
	}
	_, err = db.GetUpdateManifests().ECUInsert(&common.UpdateManifestECU{
		UpdateManifestID: manifest.ID,
		ECU:              "ICC",
		Version:          "ICCVERSION",
	})
	if err != nil {
		return nil, 0, err
	}
	_, err = db.GetUpdateManifests().ECUInsert(&common.UpdateManifestECU{
		UpdateManifestID: manifest.ID,
		ECU:              "ADAS",
		Version:          "ADASVERSION",
	})
	if err != nil {
		return nil, 0, err
	}

	carupdate := common.CarUpdate{
		VIN:              vin,
		UpdateManifestID: manifest.ID,
	}
	_, err = db.GetCarUpdates().Insert(&carupdate)
	if err != nil {
		return nil, 0, err
	}

	return &manifest, carupdate.ID, nil
}
