package common

import (
	"encoding/json"

	"github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc"
	"github.com/fiskerinc/cloud-services/pkg/logger"
)

func DepotRouteTRexToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_DepotPayload {

	switch input.Handler {
	case "init":
		return MessageToGRPC(input)
	}
	return &kafka_grpc.GRPC_DepotPayload{
		Handler: input.Handler,
	}
}

func DepotRouteHMIToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_DepotPayload {

	switch input.Handler {
	case "init":
		return HMISessionToGRPC(input)
	}
	return &kafka_grpc.GRPC_DepotPayload{
		Handler: input.Handler,
	}
}

func DepotRouteMobileToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_DepotPayload {
	return &kafka_grpc.GRPC_DepotPayload{
		Handler: input.Handler,
	}
}

func MessageToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_DepotPayload {
	grpcPayload := &kafka_grpc.GRPC_DepotPayload{
		Handler: input.Handler,
	}
	var data map[string]string
	err := json.Unmarshal(input.Data, &data)
	if err != nil || data == nil {
		return grpcPayload
	}

	initPayload := &kafka_grpc.InitPayload{
		Data: data,
	}

	grpcPayload.Data = &kafka_grpc.GRPC_DepotPayload_InitPayload{
		InitPayload: initPayload,
	}
	return grpcPayload
}

// #####################################################################################################
// ################################## Attendant ########################################################
// #####################################################################################################
func AttendantRouteTRexGRPC(input MessageRawJSON) *kafka_grpc.GRPC_AttendantPayload {

	switch input.Handler {
	case "car_state":
		return CarStateToGRPC(input)
	case "car_update_status", "car_update_download", "car_update_install":
		return CarUpdateToGRPC(input)
	case "get_filekeys":
		return CarFileKeysRequestToGRPC(input)
	case "ecc_keys":
		return CarUpdateRequestToGRPC(input)
	case "dtcs":
		return DTCEntryToGRPC(input)
	case "sms_delivery_status_manifest":
		return MessageStatusToGRPC(input)
	}
	return &kafka_grpc.GRPC_AttendantPayload{
		Handler: input.Handler,
	}
}

func AttendantRouteHMIGRPC(input MessageRawJSON) *kafka_grpc.GRPC_AttendantPayload {

	switch input.Handler {
	case "update_approve":
		return UpdateApprove(input)
	case "car_update_status", "car_update_download", "car_update_install":
		return CarUpdateToGRPC(input)
	case "get_filekeys":
		return CarFileKeysRequestToGRPC(input)
	}
	return &kafka_grpc.GRPC_AttendantPayload{
		Handler: input.Handler,
	}
}

func AttendantRouteMobileGRPC(input MessageRawJSON) *kafka_grpc.GRPC_AttendantPayload {

	switch input.Handler {
	case "update_approve":
		if input.Data == nil {
			break
		}
		return UpdateApprove(input)
	case "updates_get":
		if input.Data == nil {
			break
		}
		var m map[string]string
		err := json.Unmarshal(input.Data, &m)
		if err != nil || m == nil {
			break
		}
		return &kafka_grpc.GRPC_AttendantPayload{
			Handler: input.Handler,
			Data: &kafka_grpc.GRPC_AttendantPayload_UpdateGet{
				UpdateGet: &kafka_grpc.VehicleData{
					Vin: m["vin"],
				},
			},
		}
	}
	return &kafka_grpc.GRPC_AttendantPayload{
		Handler: input.Handler,
	}
}

func UpdateApprove(input MessageRawJSON) *kafka_grpc.GRPC_AttendantPayload {
	var m map[string]int
	err := json.Unmarshal(input.Data, &m)
	if err != nil || m == nil {
		return &kafka_grpc.GRPC_AttendantPayload{
			Handler: input.Handler,
		}
	}
	return &kafka_grpc.GRPC_AttendantPayload{
		Handler: input.Handler,
		Data: &kafka_grpc.GRPC_AttendantPayload_UpdateApprove{
			UpdateApprove: &kafka_grpc.UpdateData{
				Id: int64(m["id"]),
			},
		},
	}
}

// tmobile.MessageStatus imp
func MessageStatusToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_AttendantPayload {
	var m map[string]string
	err := json.Unmarshal(input.Data, &m)
	if err != nil || m == nil {
		return &kafka_grpc.GRPC_AttendantPayload{
			Handler: input.Handler,
		}
	}
	val, ok := kafka_grpc.EmumStatus_value[m["Status"]]
	if !ok {
		return &kafka_grpc.GRPC_AttendantPayload{
			Handler: input.Handler,
		}
	}
	return &kafka_grpc.GRPC_AttendantPayload{
		Handler: input.Handler,
		Data: &kafka_grpc.GRPC_AttendantPayload_MessageStatus{
			MessageStatus: &kafka_grpc.MessageStatus{
				MessageId: m["MessageID"],
				Status:    kafka_grpc.EmumStatus(val),
			},
		},
	}
}

func DTCEntryToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_AttendantPayload {
	grpcPayload := &kafka_grpc.GRPC_AttendantPayload{
		Handler: input.Handler,
	}
	var dtc CommonDTCEntry
	err := json.Unmarshal(input.Data, &dtc)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_AttendantPayload_DtcEntry{
		DtcEntry: &kafka_grpc.DTCEntry{
			Vin:            dtc.VIN,
			Ecu:            dtc.ECU,
			Dtc:            dtc.DTC,
			Timestamp:      dtc.CreatedAt.UnixMilli(),
			CreatedAt:      dtc.CreatedAt.UnixMilli(),
			Speed:          uint32(dtc.Speed),
			Mileage:        dtc.Mileage,
			Volt:           uint32(dtc.Voltage),
			SnapshotBase64: dtc.SnapshotBase64,
			Status:         uint32(dtc.Status),
		},
	}
	return grpcPayload
}

func AttendantRouteServiceGRPC(input MessageRawJSON) *kafka_grpc.GRPC_AttendantPayload {

	switch input.Handler {
	case "send_manifest":
		return UpdateManifestToGRPC(input)
	case "order_updated":
		return OrderToGRPC(input)
	}

	return &kafka_grpc.GRPC_AttendantPayload{
		Handler: input.Handler,
	}

}

func CarUpdateRequestToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_AttendantPayload {
	grpcPayload := &kafka_grpc.GRPC_AttendantPayload{
		Handler: input.Handler,
	}
	var carState CarUpdateRequest
	err := json.Unmarshal(input.Data, &carState)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_AttendantPayload_CarUpdateRequest{
		CarUpdateRequest: &kafka_grpc.CarUpdateRequest{
			CarUpdateId: carState.CarUpdateID,
		},
	}

	return grpcPayload
}

func CarStateToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_AttendantPayload {
	grpcPayload := &kafka_grpc.GRPC_AttendantPayload{
		Handler: input.Handler,
	}
	var carState CarStateUpdate
	err := json.Unmarshal(input.Data, &carState)
	if err != nil {
		return grpcPayload
	}

	var ecuUpdate map[string]*kafka_grpc.CarECU
	for key, ecu := range carState.ECUs {
		if ecuUpdate == nil {
			ecuUpdate = make(map[string]*kafka_grpc.CarECU)
		}

		obj := &kafka_grpc.CarECU{
			Vin:               ecu.VIN,
			Ecu:               ecu.ECU,
			SwVersion:         ecu.Version,
			SerialNumber:      ecu.SerialNumber,
			HwVersion:         ecu.HWVersion,
			BootLoaderVersion: ecu.BootLoaderVersion,
			Fingerprint:       ecu.Fingerprint,
			CodeDataString:    ecu.Config,
			Vendor:            ecu.Vendor,
			SupplierSwVersion: ecu.SupplierSWVersion,
			EpochUsec:         ecu.Epoch_usec,
			AssyNumber:        ecu.ASSYNumber,
		}

		if ecu.CreatedAt != nil {
			t := ecu.CreatedAt.UnixMilli()
			obj.CreatedAt = &t
		}
		if ecu.UpdatedAt != nil {
			t := ecu.UpdatedAt.UnixMilli()
			obj.UpdatedAt = &t
		}

		ecuUpdate[key] = obj
	}

	carUpdateStatus := &kafka_grpc.CarUpdateStatus{
		Ecus: ecuUpdate,
	}

	grpcPayload.Data = &kafka_grpc.GRPC_AttendantPayload_CarUpdateStatus{
		CarUpdateStatus: carUpdateStatus,
	}

	return grpcPayload
}

func CarUpdateToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_AttendantPayload {
	grpcPayload := &kafka_grpc.GRPC_AttendantPayload{
		Handler: input.Handler,
	}
	var carProg CarUpdateProgress
	err := json.Unmarshal(input.Data, &carProg)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_AttendantPayload_CarUpdateProgress{
		CarUpdateProgress: &kafka_grpc.CarUpdateProgress{
			FileCurrent:    carProg.FileCurrent,
			FileTotal:      carProg.FileTotal,
			PkgCurrent:     carProg.PackageCurrent,
			PkgTotal:       carProg.PackageTotal,
			InstalledFiles: int64(carProg.InstalledFiles),
			TotalFiles:     int64(carProg.TotalFiles),
			CarUpdateID:    carProg.CarUpdateID,
			Ecu:            carProg.ECU,
			Status:         carProg.Status,
			Info:           carProg.Info,
			ErrCode:        int64(carProg.ErrorCode),
		},
	}
	return grpcPayload
}

func CarFileKeysRequestToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_AttendantPayload {
	grpcPayload := &kafka_grpc.GRPC_AttendantPayload{
		Handler: input.Handler,
	}
	var fileReq FileKeysRequest
	err := json.Unmarshal(input.Data, &fileReq)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_AttendantPayload_FileKeyReq{
		FileKeyReq: &kafka_grpc.FileKeysRequest{
			FileIDs: fileReq.FileIDs,
		},
	}

	return grpcPayload
}

func ECCKeysToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_AttendantPayload {
	grpcPayload := &kafka_grpc.GRPC_AttendantPayload{
		Handler: input.Handler,
	}
	var eccKeys []ECCKeys
	err := json.Unmarshal(input.Data, &eccKeys)
	if err != nil || len(eccKeys) == 0 {
		return grpcPayload
	}

	var grpcECCKeys []*kafka_grpc.ECCKey

	for _, eccKey := range eccKeys {
		grpcECCKey := toKafkaECCKey(&eccKey)

		grpcECCKeys = append(grpcECCKeys, grpcECCKey)
	}

	grpcPayload.Data = &kafka_grpc.GRPC_AttendantPayload_Keys{
		Keys: &kafka_grpc.ECCKeys{
			EccKeys: grpcECCKeys,
		},
	}
	return grpcPayload
}

func HMISessionToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_DepotPayload {
	grpcPayload := &kafka_grpc.GRPC_DepotPayload{
		Handler: input.Handler,
	}
	var sessionData HMISessionData
	err := json.Unmarshal(input.Data, &sessionData)
	if err != nil {
		return grpcPayload
	}

	hmiSession := &kafka_grpc.HMISessionData{
		SessionId: sessionData.SessionID,
		Vin:       sessionData.VIN,
		Salt:      sessionData.Salt,
	}

	grpcPayload.Data = &kafka_grpc.GRPC_DepotPayload_HmiSession{
		HmiSession: hmiSession,
	}

	return grpcPayload
}

// #####################################################################################################
// ################################## Valet ########################################################
// #####################################################################################################
func ValetRouteTRexPayloadGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {

	switch input.Handler {
	case "departure_schedule":
		return DepartureScheduleToGRPC(input)
	case "charge_settings":
		return ChargeSettingToGRPC(input)
	case "charging_command":
		return ChargingCMDToGRPC(input)
	}
	return &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
}

func ChargingCMDToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var m map[string]string
	err := json.Unmarshal(input.Data, &m)
	if err != nil || m == nil {
		return grpcPayload
	}
	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_ChCMD{
		ChCMD: &kafka_grpc.ChargingCommand{
			Action: m["action"],
		},
	}
	return grpcPayload
}

func DepartureScheduleToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var schedule DepartureSchedule
	err := json.Unmarshal(input.Data, &schedule)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_DepartureSchedule{
		DepartureSchedule: &kafka_grpc.DepartureSchedule{
			NextDayDeparture: schedule.NextDayDeparture,
		},
	}

	if len(schedule.DepartureDays) > 0 {
		var departureDays []*kafka_grpc.DepartureDay
		for _, d := range schedule.DepartureDays {
			departureDays = append(departureDays, &kafka_grpc.DepartureDay{
				DayOfWeek: d.DayOfWeek,
				Time:      d.Time,
			})
		}
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_DepartureSchedule).DepartureSchedule.DepartureDays = departureDays
	}

	return grpcPayload
}

func ChargeSettingToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var settings ChargeSettings
	err := json.Unmarshal(input.Data, &settings)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_ChargeSetting{
		ChargeSetting: &kafka_grpc.ChargeSettings{
			ChargeLimit: int32(settings.ChargeLimit),
			MaxCurrent:  int32(settings.MaxCurrent),
		},
	}
	if settings.OffPeakCharging != nil {
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_ChargeSetting).ChargeSetting.OffPeakCcharging = &kafka_grpc.OffPeakCharging{
			Start: settings.OffPeakCharging.Start.UnixMilli(),
			End:   settings.OffPeakCharging.End.UnixMilli(),
		}
	}

	if settings.MinCharge != nil {
		minCharge := int32(*settings.MinCharge)
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_ChargeSetting).ChargeSetting.MinCharge = &minCharge
	}

	return grpcPayload
}

func ValetRouteHMIPayloadGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {

	switch input.Handler {
	case "ble_key":
		return BLEKeysToGRPC(input)
	case "profiles":
		break
	case "settings_update":
		return HMISettingsUpdateToGRPC(input)
	case "map_history":
		return HMIMapHistoryToGRPC(input)
	case "user_pois":
		return HMIPOIsMessageToGRPC(input)
	case "consent":
		return UserConsentFromHMIToGRPC(input)
	case "profile_delete":
		return JSONHMIDeleteProfileToGRPC(input)
	}
	return &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
}

func BLEKeysToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var m map[string]string
	err := json.Unmarshal(input.Data, &m)
	if err != nil || m == nil {
		return grpcPayload
	}
	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_BLEKey{
		BLEKey: &kafka_grpc.BLEKeyRequest{
			DriverId: m["driver_id"],
			BleKey:   m["ble_key"],
		},
	}
	return grpcPayload
}

func HMISettingsUpdateToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var update HMISettingsUpdate
	err := json.Unmarshal(input.Data, &update)
	if err != nil {
		return grpcPayload
	}
	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_HmiSettingsUpdate{
		HmiSettingsUpdate: &kafka_grpc.HMISettingsUpdate{
			DriverId: update.DriverID,
		},
	}
	if len(update.Settings) > 0 {
		var settings []*kafka_grpc.CarSetting
		for _, s := range update.Settings {
			carSetting := &kafka_grpc.CarSetting{
				Vin:      s.VIN,
				DriverId: s.DriverID,
				Name:     s.Name,
				Type:     s.Type,
				Value:    s.Value,
			}
			if s.CreatedAt != nil {
				millis := s.CreatedAt.UnixMilli()
				carSetting.Created = &millis
			}
			if s.UpdatedAt != nil {
				millis := s.UpdatedAt.UnixMilli()
				carSetting.Updated = &millis
			}

			settings = append(settings, carSetting)
		}
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_HmiSettingsUpdate).HmiSettingsUpdate.Settings = settings
	}

	return grpcPayload
}

func HMIMapHistoryToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var history HMIMapHistory
	err := json.Unmarshal(input.Data, &history)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_HmiMapHistory{
		HmiMapHistory: &kafka_grpc.HMIMapHistory{
			DriverId: history.DriverID,
		},
	}

	if len(history.Searches) > 0 {
		var searches []*kafka_grpc.MapHistory
		for _, s := range history.Searches {
			search := &kafka_grpc.MapHistory{
				Name:        s.Name,
				Description: s.Description,
			}
			if s.Location != nil {
				search.Location = &kafka_grpc.MapCoordinates{
					Latitude:  s.Location.Latitude,
					Longitude: s.Location.Longitude,
				}
			}
			searches = append(searches, search)
		}
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_HmiMapHistory).HmiMapHistory.Searches = searches
	}

	return grpcPayload
}

func HMIPOIsMessageToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var message HMIPOIsMessage
	err := json.Unmarshal(input.Data, &message)
	if err != nil {
		return grpcPayload
	}
	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_HmiPOIsMessage{
		HmiPOIsMessage: &kafka_grpc.HMIPOIsMessage{
			DriverId: message.DriverID,
		},
	}

	if len(message.UserPOIs) > 0 {
		var userPOIs []*kafka_grpc.PointOfInterest
		for _, poi := range message.UserPOIs {
			userPOI := &kafka_grpc.PointOfInterest{
				Name: poi.Name,
			}
			if poi.Location != (POILocation{}) {
				userPOI.Location = &kafka_grpc.POILocation{
					Latitude:  poi.Location.Latitude,
					Longitude: poi.Location.Longitude,
				}
			}
			userPOIs = append(userPOIs, userPOI)
		}
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_HmiPOIsMessage).HmiPOIsMessage.UserPOIs = userPOIs
	}

	return grpcPayload
}

func UserConsentFromHMIToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var consent []UserConsentFromHMI
	err := json.Unmarshal(input.Data, &consent)
	if err != nil {
		return grpcPayload
	}

	if len(consent) > 0 {
		var arr []*kafka_grpc.UserConsent
		for _, c := range consent {
			arr = append(arr, &kafka_grpc.UserConsent{
				Name:     c.Name,
				Accept:   c.Accept,
				DriverId: c.DriverID,
			})
		}
		grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_UserConsentFromHMI{
			UserConsentFromHMI: &kafka_grpc.UserConsentFromHMI{
				UserConsent: arr,
			},
		}
	}

	return grpcPayload
}

func JSONHMIDeleteProfileToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var profile JSONHMIDeleteProfile
	err := json.Unmarshal(input.Data, &profile)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_HmiDeleteProfile{
		HmiDeleteProfile: &kafka_grpc.JSONHMIDeleteProfile{
			DriverId: profile.DriverID,
		},
	}

	return grpcPayload
}

func ValetRouteMobilePayloadGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {

	switch input.Handler {
	case "charge_settings_get", "departure_schedule_get":
		return EmptyMessageFromMobileToGRPC(input)
	case "remote_command":
		return RemoteCMDToGRPC(input)
	case "car_locations", "map_history_get", "profiles", "store_inventory", "user_pois_get":
		break // no payload for these handlers
	case "digital_twin":
		return DigitalTwinRequestToGRPC(input)
	case "map_destination":
		return MapDestinationRequestToGRPC(input)
	case "map_route":
		return MapRouteRequestToGRPC(input)
	case "map_history_add":
		return MapHistoryToGRPC(input)
	case "settings_update":
		return MobileSettingsUpdateToGRPC(input)
	case "charge_settings":
		return MobileChargeSettingUpdateToGRPC(input)
	case "store_purchase":
		return StorePurchasesToGRPC(input)
	case "user_poi_create":
		return PointOfInterestToGRPC(input)
	case "user_poi_edit":
		return MobilePOIEditMessageToGRPC(input)
	case "user_poi_delete":
		return MobilePOIDeleteMessageToGRPC(input)
	case "departure_schedule":
		return MobileDepartureScheduleToGRPC(input)
	case "wake_car":
		return WakeCarToGRPC(input)
	case "manual_issue":
		return AddIssueRequestToGRPC(input)
	}
	return &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
}

func EmptyMessageFromMobileToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var request EmptyMessageFromMobile
	err := json.Unmarshal(input.Data, &request)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_EmptyMsg{
		EmptyMsg: &kafka_grpc.VIN{
			Vin: request.VIN,
		},
	}

	return grpcPayload
}

func AddIssueRequestToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var request AddIssueRequest
	err := json.Unmarshal(input.Data, &request)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_AddIssueReq{
		AddIssueReq: &kafka_grpc.AddIssueRequest{
			Images: request.Images,
		},
	}

	issue := &kafka_grpc.Issue{
		Vin:         request.VIN,
		Description: request.Description,
		DriverId:    request.DriverID,
		Id:          int32(request.ID),
		Title:       request.Title,
		Timestamp:   request.Timestamp.UnixMilli(),
	}

	if len(request.IssueImages) > 0 {
		var images []*kafka_grpc.IssueImage
		for _, img := range request.IssueImages {
			image := &kafka_grpc.IssueImage{
				Id:      int32(img.ID),
				Image:   img.Image,
				IssueId: int32(img.IssueID),
			}
			images = append(images, image)
		}
		issue.Images = images
	}
	grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_AddIssueReq).AddIssueReq.Issue = issue
	return grpcPayload
}

func WakeCarToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var wakeCar EmptyMessageFromMobile
	err := json.Unmarshal(input.Data, &wakeCar)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_Wakecar{
		Wakecar: &kafka_grpc.VIN{
			Vin: wakeCar.VIN,
		},
	}

	return grpcPayload
}

func MobileDepartureScheduleToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var schedule MobileDepartureSchedule
	err := json.Unmarshal(input.Data, &schedule)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_MobileDepartureSchedule{
		MobileDepartureSchedule: &kafka_grpc.MobileDepartureSchedule{
			Vin: schedule.VIN,
		},
	}

	if schedule.DepartureSchedule.NextDayDeparture != nil || len(schedule.DepartureSchedule.DepartureDays) > 0 {
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_MobileDepartureSchedule).MobileDepartureSchedule.DepartureSchedule = &kafka_grpc.DepartureSchedule{
			NextDayDeparture: schedule.DepartureSchedule.NextDayDeparture,
		}

		if len(schedule.DepartureSchedule.DepartureDays) > 0 {
			var departureDays []*kafka_grpc.DepartureDay
			for _, day := range schedule.DepartureSchedule.DepartureDays {
				departureDay := &kafka_grpc.DepartureDay{
					DayOfWeek: day.DayOfWeek,
					Time:      day.Time,
				}
				departureDays = append(departureDays, departureDay)
			}
			grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_MobileDepartureSchedule).MobileDepartureSchedule.DepartureSchedule.DepartureDays = departureDays
		}
	}

	return grpcPayload
}

func MobilePOIDeleteMessageToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var message MobilePOIDeleteMessage
	err := json.Unmarshal(input.Data, &message)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_PoiDelete{
		PoiDelete: &kafka_grpc.MobilePOIDeleteMessage{
			Name: message.Name,
		},
	}

	return grpcPayload
}

func MobilePOIEditMessageToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var message MobilePOIEditMessage
	err := json.Unmarshal(input.Data, &message)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_PoiEdit{
		PoiEdit: &kafka_grpc.MobilePOIEditMessage{
			OldName: message.OldName,
		},
	}

	if message.UserPOI != (PointOfInterest{}) {
		userPoi := &kafka_grpc.PointOfInterest{
			Name: message.UserPOI.Name,
		}
		if message.UserPOI.Location != (POILocation{}) {
			userPoi.Location = &kafka_grpc.POILocation{
				Latitude:  message.UserPOI.Location.Latitude,
				Longitude: message.UserPOI.Location.Longitude,
			}
		}
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_PoiEdit).PoiEdit.UserPoi = userPoi
	}

	return grpcPayload
}

func PointOfInterestToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var poi PointOfInterest
	err := json.Unmarshal(input.Data, &poi)
	if err != nil {
		return grpcPayload
	}
	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_PoiCreate{
		PoiCreate: &kafka_grpc.PointOfInterest{
			Name: poi.Name,
		},
	}

	if poi.Location != (POILocation{}) {
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_PoiCreate).PoiCreate.Location = &kafka_grpc.POILocation{
			Latitude:  poi.Location.Latitude,
			Longitude: poi.Location.Longitude,
		}
	}

	return grpcPayload
}

func StorePurchasesToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var purchases StorePurchases
	err := json.Unmarshal(input.Data, &purchases)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_StorePurchases{
		StorePurchases: &kafka_grpc.StorePurchases{
			Vin: purchases.VIN,
		},
	}

	if len(purchases.Purchases) > 0 {
		var storePurchases []*kafka_grpc.StorePurchaseItem
		for _, p := range purchases.Purchases {
			purchase := &kafka_grpc.StorePurchaseItem{
				Id: p.ID.String(),
			}
			storePurchases = append(storePurchases, purchase)
		}
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_StorePurchases).StorePurchases.Purchases = storePurchases
	}

	return grpcPayload
}
func MobileChargeSettingUpdateToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var setting MobileChargeSetting
	err := json.Unmarshal(input.Data, &setting)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_MobileChargeSetting{
		MobileChargeSetting: &kafka_grpc.MobileChargeSetting{
			Vin: setting.VIN,
		},
	}

	if setting.ChargeSettings != (ChargeSettings{}) {
		chargeSettings := &kafka_grpc.ChargeSettings{
			ChargeLimit: int32(setting.ChargeSettings.ChargeLimit),
			MaxCurrent:  int32(setting.ChargeSettings.MaxCurrent),
		}
		if setting.ChargeSettings.OffPeakCharging != nil {
			chargeSettings.OffPeakCcharging = &kafka_grpc.OffPeakCharging{
				Start: setting.ChargeSettings.OffPeakCharging.Start.UnixMilli(),
				End:   setting.ChargeSettings.OffPeakCharging.End.UnixMilli(),
			}
		}

		if setting.ChargeSettings.MinCharge != nil {
			minCharge := int32(*setting.ChargeSettings.MinCharge)
			chargeSettings.MinCharge = &minCharge
		}

		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_MobileChargeSetting).MobileChargeSetting.ChargeSettings = chargeSettings
	}

	return grpcPayload
}

func MobileSettingsUpdateToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var update MobileSettingsUpdate
	err := json.Unmarshal(input.Data, &update)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_SettingsUpdate{
		SettingsUpdate: &kafka_grpc.MobileSettingsUpdate{
			Vin: update.VIN,
		},
	}

	if len(update.Settings) > 0 {
		var settings []*kafka_grpc.CarSetting
		for _, s := range update.Settings {
			setting := &kafka_grpc.CarSetting{
				Vin:      s.VIN,
				DriverId: s.DriverID,
				Name:     s.Name,
				Value:    s.Value,
				Type:     s.Type,
			}
			if s.CreatedAt != nil {
				millis := s.CreatedAt.UnixMilli()
				setting.Created = &millis
			}
			if s.UpdatedAt != nil {
				millis := s.UpdatedAt.UnixMilli()
				setting.Updated = &millis
			}
			settings = append(settings, setting)
		}
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_SettingsUpdate).SettingsUpdate.Settings = settings
	}

	return grpcPayload
}

func MapHistoryToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var history MapHistory
	err := json.Unmarshal(input.Data, &history)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_MapHistory{
		MapHistory: &kafka_grpc.MapHistory{
			Name:        history.Name,
			Description: history.Description,
		},
	}

	if history.Location != nil {
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_MapHistory).MapHistory.Location = &kafka_grpc.MapCoordinates{
			Latitude:  history.Location.Latitude,
			Longitude: history.Location.Longitude,
		}
	}

	return grpcPayload
}

func MapRouteRequestToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var request MapRouteRequest
	err := json.Unmarshal(input.Data, &request)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_MapRouteReq{
		MapRouteReq: &kafka_grpc.MapRouteRequest{
			Vin: request.VIN,
		},
	}

	if len(request.Route) > 0 {
		var route []*kafka_grpc.MapCoordinates
		for _, r := range request.Route {
			route = append(route, &kafka_grpc.MapCoordinates{
				Latitude:  r.Latitude,
				Longitude: r.Longitude,
			})
		}
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_MapRouteReq).MapRouteReq.Route = route
	}

	if len(request.Waypoints) > 0 {
		var waypoints []*kafka_grpc.MapWaypoint
		for _, p := range request.Waypoints {
			waypoint := &kafka_grpc.MapWaypoint{
				Type:  p.Type,
				Title: p.Title,
				Coordinates: &kafka_grpc.MapCoordinates{
					Latitude:  p.Latitude,
					Longitude: p.Longitude,
				},
			}
			waypoints = append(waypoints, waypoint)
		}
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_MapRouteReq).MapRouteReq.Waypoints = waypoints
	}

	return grpcPayload
}

func MapDestinationRequestToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var request MapDestinationRequest
	err := json.Unmarshal(input.Data, &request)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_MapDestReq{
		MapDestReq: &kafka_grpc.MapDestinationRequest{
			Vin:  request.VIN,
			Name: request.Name,
		},
	}

	if request.Address != nil {
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_MapDestReq).MapDestReq.Address = &kafka_grpc.TomTomAddress{
			StreetName:             request.Address.StreetName,
			StreetNumber:           request.Address.StreetNumber,
			LocalName:              request.Address.LocalName,
			PostalCode:             request.Address.PostalCode,
			CountrySubdivisionName: request.Address.CountrySubdivisionName,
			CountryCodeIso3:        request.Address.CountryCodeIso3,
		}
	}

	if request.Coordinates != (MapCoordinates{}) {
		grpcPayload.Data.(*kafka_grpc.GRPC_ValetPayload_MapDestReq).MapDestReq.Coordinates = &kafka_grpc.MapCoordinates{
			Latitude:  request.Coordinates.Latitude,
			Longitude: request.Coordinates.Longitude,
		}
	}

	return grpcPayload
}

func DigitalTwinRequestToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var request DigitalTwinRequest
	err := json.Unmarshal(input.Data, &request)
	if err != nil {
		return grpcPayload
	}
	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_DigitalTwinReq{
		DigitalTwinReq: &kafka_grpc.VIN{
			Vin: request.VIN,
		},
	}

	return grpcPayload
}

func ValetRouteServicePayloadGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {

	switch input.Handler {
	case "remote_command":
		return RemoteCMDToGRPC(input)
	}
	return &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}

}

func RemoteCMDToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_ValetPayload {
	grpcPayload := &kafka_grpc.GRPC_ValetPayload{
		Handler: input.Handler,
	}
	var command kafka_grpc.RemoteCommand
	err := json.Unmarshal(input.Data, &command)
	if err != nil {
		return grpcPayload
	}

	grpcPayload.Data = &kafka_grpc.GRPC_ValetPayload_RemoteCmd{
		RemoteCmd: &command,
	}

	return grpcPayload
}

func UpdateManifestToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_AttendantPayload {
	grpcPayload := &kafka_grpc.GRPC_AttendantPayload{
		Handler: input.Handler,
	}

	var commonManifest UpdateManifest
	err := json.Unmarshal(input.Data, &commonManifest)
	if err != nil {
		return grpcPayload
	}
	data := &kafka_grpc.UpdateManifest{}

	data.Id = commonManifest.ID
	data.Name = commonManifest.Name
	data.Version = commonManifest.Version
	data.Description = commonManifest.Description
	data.ReleaseNotes = commonManifest.ReleaseNotes
	data.EcuList = commonManifest.ECUList
	data.Fingerprint = commonManifest.Fingerprint
	data.CarUpdateId = commonManifest.CarUpdateID
	data.Rollback = commonManifest.RollbackEnabled
	data.Type = commonManifest.Type
	data.Vod = commonManifest.VOD
	data.ManifestType = int32(commonManifest.ManifestType)
	data.Env = commonManifest.Env
	data.UpdateDuration = int32(commonManifest.UpdateDuration)
	data.MaxAttempts = int32(commonManifest.MaxAttempts)
	data.Active = commonManifest.Active
	data.Country = commonManifest.Country
	data.BodyType = commonManifest.BodyType
	data.Restraint = commonManifest.Restraint
	data.Powertrain = commonManifest.PowerTrain
	data.Model = commonManifest.Model
	data.Trim = commonManifest.Trim
	data.Year = int32(commonManifest.Year)
	data.Sums = commonManifest.SUMS

	if commonManifest.CreatedAt != nil {
		created := commonManifest.CreatedAt.UnixMilli()
		data.Created = &created
	}
	if commonManifest.UpdatedAt != nil {
		updated := commonManifest.UpdatedAt.UnixMilli()
		data.Updated = &updated
	}

	// Convert ECUs
	updateManifestECU := make([]*kafka_grpc.UpdateManifestECU, 0, len(commonManifest.ECUs))
	updateManifestECU = UpdateManifestECUToGRPC(commonManifest.ECUs, updateManifestECU)
	data.EcuUpdates = updateManifestECU
	data.EcuUpdates = updateManifestECU
	grpcPayload.Data = &kafka_grpc.GRPC_AttendantPayload_UpdateManifest{
		UpdateManifest: data,
	}
	return grpcPayload
}

func UpdateManifestECUToGRPC(ecus []*UpdateManifestECU, output []*kafka_grpc.UpdateManifestECU) []*kafka_grpc.UpdateManifestECU {
	for _, commonECU := range ecus {
		u := &kafka_grpc.UpdateManifestECU{
			Id:                commonECU.ID,
			ManifestId:        commonECU.UpdateManifestID,
			Name:              commonECU.ECU,
			Version:           commonECU.Version,
			CurrentVersion:    commonECU.CurrentVersion,
			HwVersion:         commonECU.HWVersion,
			HwVersions:        commonECU.HWVersions,
			ConfigurationMask: commonECU.ConfigurationMask,
			Configuration:     commonECU.Configuration,
			SelfDownload:      commonECU.SelfDownload,
			Mode:              commonECU.Mode,
			InstallPriority:   int32(commonECU.InstallPriority),
		}
		if commonECU.CreatedAt != nil {
			created := commonECU.CreatedAt.UnixMilli()
			u.Created = &created
		}
		if commonECU.UpdatedAt != nil {
			updated := commonECU.UpdatedAt.UnixMilli()
			u.Updated = &updated
		}

		u.EccKeys = toKafkaECCKey(commonECU.ECCKeys)

		if len(commonECU.Files) > 0 {
			fileInBytes := toBinaryArray(commonECU.Files)
			if len(fileInBytes) > 0 {
				var files []*kafka_grpc.UpdateManifestFile
				err := json.Unmarshal(fileInBytes, &files)
				if err != nil {
					logger.Warn().Msgf("unable to parse Files of %s", string(fileInBytes))
				}
				u.Files = files
			}
		}
		if len(commonECU.Rollback) > 0 {
			u.Rollback = make([]*kafka_grpc.UpdateManifestECU, 0, len(u.Rollback))
			u.Rollback = UpdateManifestECUToGRPC(commonECU.Rollback, u.Rollback)
		}
		output = append(output, u)
	}
	return output
}

func toKafkaECCKey(req *ECCKeys) *kafka_grpc.ECCKey {

	if req == nil {
		return nil
	}
	e := &kafka_grpc.ECCKey{
		Ecu: req.ECU,
		Env: req.Env,
	}
	if req.CreatedAt != nil {
		t := req.CreatedAt.UnixMilli()
		e.Created = &t
	}

	if req.UpdatedAt != nil {
		t := req.UpdatedAt.UnixMilli()
		e.Updated = &t
	}

	if req.PubKey1 != nil {
		e.PubKeyLevel_1 = &kafka_grpc.BineryHex{
			Data: req.PubKey1.Bytes(),
		}
	}
	if req.PubKey2 != nil {
		e.PubKeyLevel_2 = &kafka_grpc.BineryHex{
			Data: req.PubKey2.Bytes(),
		}
	}
	if req.PubKey3 != nil {
		e.PubKeyLevel_3 = &kafka_grpc.BineryHex{
			Data: req.PubKey3.Bytes(),
		}
	}
	if req.PrivKey1 != nil {
		e.Level_1 = &kafka_grpc.BineryHex{
			Data: req.PrivKey1.Bytes(),
		}
	}
	if req.PrivKey2 != nil {
		e.Level_2 = &kafka_grpc.BineryHex{
			Data: req.PrivKey2.Bytes(),
		}
	}
	if req.PrivKey3 != nil {
		e.Level_3 = &kafka_grpc.BineryHex{
			Data: req.PrivKey3.Bytes(),
		}
	}
	return e

}
