/*
 * Copyright 2020-2024 Open Networking Foundation (ONF) and the ONF Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Package mib provides the utilities for managing the onu mib
package mib

import (
	"context"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"sync"

	"github.com/looplab/fsm"

	"time"

	"github.com/google/gopacket"
	"github.com/opencord/omci-lib-go/v2"
	me "github.com/opencord/omci-lib-go/v2/generated"
	"github.com/opencord/voltha-lib-go/v7/pkg/db/kvstore"
	"github.com/opencord/voltha-lib-go/v7/pkg/log"
	cmn "github.com/opencord/voltha-openonu-adapter-go/internal/pkg/common"
	devdb "github.com/opencord/voltha-openonu-adapter-go/internal/pkg/devdb"
	otst "github.com/opencord/voltha-openonu-adapter-go/internal/pkg/omcitst"
	"github.com/opencord/voltha-protos/v5/go/inter_adapter"
)

type sLastTxMeParameter struct {
	pLastTxMeInstance *me.ManagedEntity
	lastTxMessageType omci.MessageType
	repeatCount       uint8
}

const (
	retryDelay          = 30 * time.Second
	maxRetries          = 3
	initialRetryAttempt = 1
)

var supportedClassIds = []me.ClassID{
	me.CardholderClassID,                              // 5
	me.CircuitPackClassID,                             // 6
	me.SoftwareImageClassID,                           // 7
	me.PhysicalPathTerminationPointEthernetUniClassID, // 11
	me.PhysicalPathTerminationPointPotsUniClassID,     // 53
	me.OltGClassID,                                    // 131
	me.OnuPowerSheddingClassID,                        // 133
	me.IpHostConfigDataClassID,                        // 134
	me.OnuGClassID,                                    // 256
	me.Onu2GClassID,                                   // 257
	me.TContClassID,                                   // 262
	me.AniGClassID,                                    // 263
	me.UniGClassID,                                    // 264
	me.PriorityQueueClassID,                           // 277
	me.TrafficSchedulerClassID,                        // 278
	me.VirtualEthernetInterfacePointClassID,           // 329
	me.EnhancedSecurityControlClassID,                 // 332
	me.OnuDynamicPowerManagementControlClassID,        // 336
	// 347 // definitions for ME "IPv6 host config data" are currently missing in omci-lib-go!
}

var omccVersionSupportsExtendedOmciFormat = map[uint8]bool{
	0x80: false,
	0x81: false,
	0x82: false,
	0x83: false,
	0x84: false,
	0x85: false,
	0x86: false,
	0xA0: false,
	0xA1: false,
	0xA2: false,
	0xA3: false,
	0x96: true,
	0xB0: true,
	0xB1: true,
	0xB2: true,
	0xB3: true,
	0xB4: true,
}

var fsmMsg cmn.TestMessageType

func (oo *OnuDeviceEntry) enterStartingState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start processing MibSync-msgs in State": e.FSM.Current(), "device-id": oo.deviceID})
	oo.pOnuDB = devdb.NewOnuDeviceDB(log.WithSpanFromContext(context.TODO(), ctx), oo.deviceID)
	go oo.processMibSyncMessages(ctx)
}

func (oo *OnuDeviceEntry) enterResettingMibState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start MibTemplate processing in State": e.FSM.Current(), "device-id": oo.deviceID})

	if (!oo.IsNewOnu() && !oo.baseDeviceHandler.IsReconciling()) || //use case: re-auditing failed
		oo.baseDeviceHandler.IsSkipOnuConfigReconciling() { //use case: reconciling without omci-config failed
		oo.baseDeviceHandler.PrepareReconcilingWithActiveAdapter(ctx)
		oo.devState = cmn.DeviceStatusInit
	}
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"send mibReset in State": e.FSM.Current(), "device-id": oo.deviceID})
	oo.mutexLastTxParamStruct.Lock()
	_ = oo.PDevOmciCC.SendMibReset(log.WithSpanFromContext(context.TODO(), ctx), oo.baseDeviceHandler.GetOmciTimeout(), true)
	//TODO: needs to handle timeouts
	//even though lastTxParameters are currently not used for checking the ResetResponse message we have to ensure
	//  that the lastTxMessageType is correctly set to avoid misinterpreting other responses
	oo.lastTxParamStruct.lastTxMessageType = omci.MibResetRequestType
	oo.lastTxParamStruct.repeatCount = 0
	oo.mutexLastTxParamStruct.Unlock()
}

func (oo *OnuDeviceEntry) enterGettingVendorAndSerialState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start getting VendorId and SerialNumber in State": e.FSM.Current(), "device-id": oo.deviceID})
	requestedAttributes := me.AttributeValueMap{me.OnuG_VendorId: "", me.OnuG_SerialNumber: 0}
	oo.mutexLastTxParamStruct.Lock()
	meInstance, err := oo.PDevOmciCC.SendGetMe(log.WithSpanFromContext(context.TODO(), ctx), me.OnuGClassID, cmn.OnugMeID, requestedAttributes,
		oo.baseDeviceHandler.GetOmciTimeout(), true, oo.PMibUploadFsm.CommChan, false)
	//accept also nil as (error) return value for writing to LastTx
	//  - this avoids misinterpretation of new received OMCI messages
	if err != nil {
		oo.mutexLastTxParamStruct.Unlock()
		logger.Errorw(ctx, "ONU-G get failed, aborting MibSync FSM", log.Fields{"device-id": oo.deviceID})
		pMibUlFsm := oo.PMibUploadFsm
		if pMibUlFsm != nil {
			//nolint:unparam
			go func(a_pAFsm *cmn.AdapterFsm) {
				_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
			}(pMibUlFsm)
		}
		return
	}
	oo.lastTxParamStruct.lastTxMessageType = omci.GetRequestType
	oo.lastTxParamStruct.pLastTxMeInstance = meInstance
	oo.mutexLastTxParamStruct.Unlock()
}

func (oo *OnuDeviceEntry) enterGettingVersionState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start getting Version in State": e.FSM.Current(), "device-id": oo.deviceID})
	requestedAttributes := me.AttributeValueMap{me.OnuG_Version: ""}
	oo.mutexLastTxParamStruct.Lock()
	meInstance, err := oo.PDevOmciCC.SendGetMe(log.WithSpanFromContext(context.TODO(), ctx), me.OnuGClassID, cmn.OnugMeID,
		requestedAttributes, oo.baseDeviceHandler.GetOmciTimeout(), true, oo.PMibUploadFsm.CommChan, false)
	//accept also nil as (error) return value for writing to LastTx
	//  - this avoids misinterpretation of new received OMCI messages
	if err != nil {
		oo.mutexLastTxParamStruct.Unlock()
		logger.Errorw(ctx, "ONU-G get failed, aborting MibSync FSM", log.Fields{"device-id": oo.deviceID})
		pMibUlFsm := oo.PMibUploadFsm
		if pMibUlFsm != nil {
			//nolint:unparam
			go func(a_pAFsm *cmn.AdapterFsm) {
				_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
			}(pMibUlFsm)
		}
		return
	}
	oo.lastTxParamStruct.lastTxMessageType = omci.GetRequestType
	oo.lastTxParamStruct.pLastTxMeInstance = meInstance
	oo.mutexLastTxParamStruct.Unlock()
}

func (oo *OnuDeviceEntry) enterGettingEquipIDAndOmccVersState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start getting EquipmentId and OMCC version in State": e.FSM.Current(), "device-id": oo.deviceID})
	requestedAttributes := me.AttributeValueMap{me.Onu2G_EquipmentId: "", me.Onu2G_OpticalNetworkUnitManagementAndControlChannelOmccVersion: 0}
	oo.mutexLastTxParamStruct.Lock()
	meInstance, err := oo.PDevOmciCC.SendGetMe(log.WithSpanFromContext(context.TODO(), ctx), me.Onu2GClassID, cmn.Onu2gMeID, requestedAttributes,
		oo.baseDeviceHandler.GetOmciTimeout(), true, oo.PMibUploadFsm.CommChan, false)
	//accept also nil as (error) return value for writing to LastTx
	//  - this avoids misinterpretation of new received OMCI messages
	if err != nil {
		oo.mutexLastTxParamStruct.Unlock()
		logger.Errorw(ctx, "ONU2-G get failed, aborting MibSync FSM!", log.Fields{"device-id": oo.deviceID})
		pMibUlFsm := oo.PMibUploadFsm
		if pMibUlFsm != nil {
			//nolint:unparam
			go func(a_pAFsm *cmn.AdapterFsm) {
				_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
			}(pMibUlFsm)
		}
		return
	}
	oo.lastTxParamStruct.lastTxMessageType = omci.GetRequestType
	oo.lastTxParamStruct.pLastTxMeInstance = meInstance
	oo.mutexLastTxParamStruct.Unlock()
}

func (oo *OnuDeviceEntry) enterTestingExtOmciSupportState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start testing extended OMCI msg in State": e.FSM.Current(), "device-id": oo.deviceID})
	omciVerify := otst.NewOmciTestRequest(log.WithSpanFromContext(context.TODO(), ctx),
		oo.deviceID, oo.PDevOmciCC, true, true, true)
	verifyExec := make(chan bool)
	omciVerify.PerformOmciTest(log.WithSpanFromContext(context.TODO(), ctx), verifyExec)

	// If verification of test message in extended OMCI format fails, reset ONU capability to OMCI baseline format
	select {
	case <-time.After(((cmn.CDefaultRetries+1)*otst.CTestRequestOmciTimeout + 1) * time.Second):
		logger.Warnw(ctx, "testing extended OMCI msg format timed out - reset to baseline format", log.Fields{"device-id": oo.deviceID})
		oo.MutexPersOnuConfig.Lock()
		oo.SOnuPersistentData.PersIsExtOmciSupported = false
		oo.MutexPersOnuConfig.Unlock()
	case success := <-verifyExec:
		if success {
			logger.Debugw(ctx, "testing extended OMCI msg format succeeded", log.Fields{"device-id": oo.deviceID})
		} else {
			logger.Warnw(ctx, "testing extended OMCI msg format failed - reset to baseline format", log.Fields{"device-id": oo.deviceID, "result": success})
			oo.MutexPersOnuConfig.Lock()
			oo.SOnuPersistentData.PersIsExtOmciSupported = false
			oo.MutexPersOnuConfig.Unlock()
		}
	}
	pMibUlFsm := oo.PMibUploadFsm
	if pMibUlFsm != nil {
		//nolint:unparam
		go func(a_pAFsm *cmn.AdapterFsm) {
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvGetFirstSwVersion)
		}(pMibUlFsm)
	}
}

func (oo *OnuDeviceEntry) enterGettingFirstSwVersionState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start getting IsActive and Version of first SW-image in State": e.FSM.Current(), "device-id": oo.deviceID})
	requestedAttributes := me.AttributeValueMap{me.SoftwareImage_IsCommitted: 0, me.SoftwareImage_IsActive: 0, me.SoftwareImage_Version: ""}
	oo.mutexLastTxParamStruct.Lock()
	meInstance, err := oo.PDevOmciCC.SendGetMe(log.WithSpanFromContext(context.TODO(), ctx), me.SoftwareImageClassID, cmn.FirstSwImageMeID, requestedAttributes,
		oo.baseDeviceHandler.GetOmciTimeout(), true, oo.PMibUploadFsm.CommChan, oo.GetPersIsExtOmciSupported())
	//accept also nil as (error) return value for writing to LastTx
	//  - this avoids misinterpretation of new received OMCI messages
	if err != nil {
		oo.mutexLastTxParamStruct.Unlock()
		logger.Errorw(ctx, "SoftwareImage get failed, aborting MibSync FSM", log.Fields{"device-id": oo.deviceID})
		pMibUlFsm := oo.PMibUploadFsm
		if pMibUlFsm != nil {
			//nolint:unparam
			go func(a_pAFsm *cmn.AdapterFsm) {
				_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
			}(pMibUlFsm)
		}
		return
	}
	oo.lastTxParamStruct.lastTxMessageType = omci.GetRequestType
	oo.lastTxParamStruct.pLastTxMeInstance = meInstance
	oo.mutexLastTxParamStruct.Unlock()
}

func (oo *OnuDeviceEntry) enterGettingSecondSwVersionState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start getting IsActive and Version of second SW-image in State": e.FSM.Current(), "device-id": oo.deviceID})
	requestedAttributes := me.AttributeValueMap{me.SoftwareImage_IsCommitted: 0, me.SoftwareImage_IsActive: 0, me.SoftwareImage_Version: ""}
	oo.mutexLastTxParamStruct.Lock()
	meInstance, err := oo.PDevOmciCC.SendGetMe(log.WithSpanFromContext(context.TODO(), ctx), me.SoftwareImageClassID, cmn.SecondSwImageMeID, requestedAttributes,
		oo.baseDeviceHandler.GetOmciTimeout(), true, oo.PMibUploadFsm.CommChan, oo.GetPersIsExtOmciSupported())
	//accept also nil as (error) return value for writing to LastTx
	//  - this avoids misinterpretation of new received OMCI messages
	if err != nil {
		oo.mutexLastTxParamStruct.Unlock()
		logger.Errorw(ctx, "SoftwareImage get failed, aborting MibSync FSM", log.Fields{"device-id": oo.deviceID})
		pMibUlFsm := oo.PMibUploadFsm
		if pMibUlFsm != nil {
			//nolint:unparam
			go func(a_pAFsm *cmn.AdapterFsm) {
				_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
			}(pMibUlFsm)
		}
		return
	}
	oo.lastTxParamStruct.lastTxMessageType = omci.GetRequestType
	oo.lastTxParamStruct.pLastTxMeInstance = meInstance
	oo.mutexLastTxParamStruct.Unlock()
}

func (oo *OnuDeviceEntry) enterGettingMacAddressState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start getting MacAddress in State": e.FSM.Current(), "device-id": oo.deviceID})
	requestedAttributes := me.AttributeValueMap{me.IpHostConfigData_MacAddress: ""}
	oo.mutexLastTxParamStruct.Lock()
	meInstance, err := oo.PDevOmciCC.SendGetMe(log.WithSpanFromContext(context.TODO(), ctx), me.IpHostConfigDataClassID, cmn.IPHostConfigDataMeID, requestedAttributes,
		oo.baseDeviceHandler.GetOmciTimeout(), true, oo.PMibUploadFsm.CommChan, oo.GetPersIsExtOmciSupported())
	//accept also nil as (error) return value for writing to LastTx
	//  - this avoids misinterpretation of new received OMCI messages
	if err != nil {
		oo.mutexLastTxParamStruct.Unlock()
		logger.Errorw(ctx, "IpHostConfigData get failed, aborting MibSync FSM", log.Fields{"device-id": oo.deviceID})
		pMibUlFsm := oo.PMibUploadFsm
		if pMibUlFsm != nil {
			//nolint:unparam
			go func(a_pAFsm *cmn.AdapterFsm) {
				_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
			}(pMibUlFsm)
		}
		return
	}
	oo.lastTxParamStruct.lastTxMessageType = omci.GetRequestType
	oo.lastTxParamStruct.pLastTxMeInstance = meInstance
	oo.mutexLastTxParamStruct.Unlock()
}

//nolint:unparam
func (oo *OnuDeviceEntry) enterGettingMibTemplateState(ctx context.Context, e *fsm.Event) {

	oo.mutexOnuSwImageIndications.RLock()
	if oo.onuSwImageIndications.ActiveEntityEntry.Valid {
		oo.MutexPersOnuConfig.Lock()
		oo.SOnuPersistentData.PersActiveSwVersion = oo.onuSwImageIndications.ActiveEntityEntry.Version
		oo.MutexPersOnuConfig.Unlock()
		oo.mutexOnuSwImageIndications.RUnlock()
	} else {
		oo.mutexOnuSwImageIndications.RUnlock()
		logger.Errorw(ctx, "get-mib-template: no active SW version found, working with empty SW version, which might be untrustworthy",
			log.Fields{"device-id": oo.deviceID})
	}

	//create the MIB Template path
	oo.mibTemplatePath = oo.buildMibTemplatePath()

	if cmnMEDBVal, exist := oo.pOpenOnuAc.FetchEntryFromMibDatabaseMap(ctx, oo.mibTemplatePath); exist {
		logger.Infow(ctx, "A Common MIB DB Instance exist for this type of ONT", log.Fields{"device-id": oo.deviceID, "mibTemplatePath": oo.mibTemplatePath})
		oo.pOpenOnuAc.LockMutexMibTemplateGenerated()
		oo.pOnuDB.CommonMeDb = cmnMEDBVal

		//VOL-5406:If the Instance is present but the MIB templates are cleaned up . Check and create one.
		Value, err := oo.mibTemplateKVStore.Get(log.WithSpanFromContext(context.TODO(), ctx), oo.mibTemplatePath)
		if err == nil {
			if Value == nil {

				error := oo.createAndPersistMibTemplate(ctx)
				if error != nil {
					logger.Errorw(ctx, "MibSync - MibTemplate - Failed to create and persist the mib template", log.Fields{"error": err, "device-id": oo.deviceID})
				} else {
					logger.Infow(ctx, "MIB Template created and stored ", log.Fields{"device-id": oo.deviceID, "mibTemplatePath": oo.mibTemplatePath})
				}
			}
		} else {
			logger.Errorw(ctx, "MibSync - MibTemplate - Failed to create and persist the mib template", log.Fields{"error": err, "device-id": oo.deviceID})
		}

		if cmnMEDBVal.MIBUploadStatus == devdb.Completed {
			oo.pOnuDB.CommonMeDb.MeDbLock.Lock()
			oo.updateOnuSpecificEntries(ctx)
			oo.pOnuDB.CommonMeDb.MeDbLock.Unlock()
			fsmMsg = cmn.LoadMibTemplateOk
		} else {
			logger.Errorw(ctx, "A previous MIB Upload for this type of ONT  has failed, request for a MIB UPload", log.Fields{"device-id": oo.deviceID})
			oo.pOpenOnuAc.ResetEntryFromMibDatabaseMap(ctx, oo.mibTemplatePath)
			oo.pOnuDB.CommonMeDb.MeDbLock.Lock()
			fsmMsg = cmn.LoadMibTemplateFailed
		}
		oo.pOpenOnuAc.UnlockMutexMibTemplateGenerated()

	} else {
		Value, err := oo.mibTemplateKVStore.Get(log.WithSpanFromContext(context.TODO(), ctx), oo.mibTemplatePath)
		if err == nil && Value != nil {
			logger.Infow(ctx, "No Common MIB DB instance  exist , creating from Template", log.Fields{"device-id": oo.deviceID, "mibTemplatePath": oo.mibTemplatePath})
			oo.processMibTemplate(ctx, Value)
			fsmMsg = cmn.LoadMibTemplateOk
		} else {
			logger.Infow(ctx, "Neither  Common MIB  DB  Instance nor MIB template exist for this type of ONT", log.Fields{"device-id": oo.deviceID, "mibTemplatePath": oo.mibTemplatePath})
			oo.pOpenOnuAc.LockMutexMibTemplateGenerated()
			cmnMEDBValue, _ := oo.pOpenOnuAc.CreateEntryAtMibDatabaseMap(ctx, oo.mibTemplatePath)
			oo.pOnuDB.CommonMeDb = cmnMEDBValue
			oo.pOnuDB.CommonMeDb.MeDbLock.Lock()
			fsmMsg = cmn.LoadMibTemplateFailed

		}

	}

	mibSyncMsg := cmn.Message{
		Type: cmn.TestMsg,
		Data: cmn.TestMessage{
			TestMessageVal: fsmMsg,
		},
	}
	oo.PMibUploadFsm.CommChan <- mibSyncMsg
}

func (oo *OnuDeviceEntry) enterUploadingState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"send MibUpload in State": e.FSM.Current(), "device-id": oo.deviceID})
	_ = oo.PDevOmciCC.SendMibUpload(log.WithSpanFromContext(context.TODO(), ctx),
		oo.baseDeviceHandler.GetOmciTimeout(), true, oo.GetPersIsExtOmciSupported())
	//even though lastTxParameters are currently not used for checking the ResetResponse message we have to ensure
	//  that the lastTxMessageType is correctly set to avoid misinterpreting other responses
	oo.mutexLastTxParamStruct.Lock()
	oo.lastTxParamStruct.lastTxMessageType = omci.MibUploadRequestType
	oo.mutexLastTxParamStruct.Unlock()
}

func (oo *OnuDeviceEntry) enterUploadDoneState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"send notification to core in State": e.FSM.Current(), "device-id": oo.deviceID})
	oo.transferSystemEvent(ctx, cmn.MibDatabaseSync)
	go func() {
		_ = oo.PMibUploadFsm.PFsm.Event(UlEvSuccess)
	}()
}

func (oo *OnuDeviceEntry) enterInSyncState(ctx context.Context, e *fsm.Event) {
	oo.MutexPersOnuConfig.Lock()
	oo.SOnuPersistentData.PersMibLastDbSync = uint32(time.Now().Unix())
	oo.MutexPersOnuConfig.Unlock()
	if oo.mibAuditInterval > 0 {
		logger.Debugw(ctx, "MibSync FSM", log.Fields{"trigger next Audit in State": e.FSM.Current(), "oo.mibAuditInterval": oo.mibAuditInterval, "device-id": oo.deviceID})
		go func() {
			time.Sleep(oo.mibAuditInterval)
			if err := oo.PMibUploadFsm.PFsm.Event(UlEvAuditMib); err != nil {
				logger.Debugw(ctx, "MibSyncFsm: Can't go to state auditing", log.Fields{"device-id": oo.deviceID, "err": err})
			}
		}()
	}
}

func (oo *OnuDeviceEntry) enterVerifyingAndStoringTPsState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start verifying and storing TPs in State": e.FSM.Current(), "device-id": oo.deviceID})

	if oo.getAllStoredTpInstFromParentAdapter(ctx) {
		logger.Debugw(ctx, "MibSync FSM", log.Fields{"reconciling - verifying TPs successful": e.FSM.Current(), "device-id": oo.deviceID})
		go func() {
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvSuccess)
		}()
	} else {
		logger.Debugw(ctx, "MibSync FSM", log.Fields{"reconciling - verifying TPs not successful": e.FSM.Current(), "device-id": oo.deviceID})
		oo.baseDeviceHandler.SetReconcilingReasonUpdate(true)
		go func() {
			if err := oo.baseDeviceHandler.StorePersistentData(ctx); err != nil {
				logger.Warnw(ctx, "reconciling - store persistent data error - continue for now as there will be additional write attempts",
					log.Fields{"device-id": oo.deviceID, "err": err})
			}
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvMismatch)
		}()
	}
}

func (oo *OnuDeviceEntry) enterExaminingMdsState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start GetMds processing in State": e.FSM.Current(), "device-id": oo.deviceID})
	oo.pOpenOnuAc.RLockMutexDeviceHandlersMap()
	dh, _ := oo.pOpenOnuAc.GetDeviceHandler(oo.deviceID)
	oo.pOpenOnuAc.RUnlockMutexDeviceHandlersMap()
	if dh.GetSkipOnuConfigEnabled() {
		go func() {
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvSuccess)
		}()
	} else {
		oo.requestMdsValue(ctx)
	}

}

func (oo *OnuDeviceEntry) enterResynchronizingState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start MibResync processing in State": e.FSM.Current(), "device-id": oo.deviceID})
	logger.Debug(ctx, "function not implemented yet")
	// TODOs:
	// VOL-3805 - Provide exclusive OMCI channel for one FSM
	// VOL-3785 - New event notifications and corresponding performance counters for openonu-adapter-go
	// VOL-3792 - Support periodical audit via mib resync
	// VOL-3793 - ONU-reconcile handling after adapter restart based on mib resync
}

func (oo *OnuDeviceEntry) enterExaminingMdsSuccessState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM",
		log.Fields{"Start processing on examining MDS success in State": e.FSM.Current(), "device-id": oo.deviceID})

	if oo.getMibFromTemplate(ctx) {
		oo.baseDeviceHandler.StartReconciling(ctx, true)
		oo.baseDeviceHandler.AddAllUniPorts(ctx)
		_ = oo.baseDeviceHandler.ReasonUpdate(ctx, cmn.DrInitialMibDownloaded, oo.baseDeviceHandler.IsReconcilingReasonUpdate())
		oo.baseDeviceHandler.SetReadyForOmciConfig(true)

		if !oo.baseDeviceHandler.GetCollectorIsRunning() {
			var waitForOmciProcess sync.WaitGroup
			waitForOmciProcess.Add(1)
			// Start PM collector routine
			go oo.baseDeviceHandler.StartCollector(ctx, &waitForOmciProcess)
			waitForOmciProcess.Wait()
		}
		if !oo.baseDeviceHandler.GetAlarmManagerIsRunning(ctx) {
			go oo.baseDeviceHandler.StartAlarmManager(ctx)
		}

		for _, uniPort := range *oo.baseDeviceHandler.GetUniEntityMap() {
			// only if this port was enabled for use by the operator at startup
			if (1<<uniPort.UniID)&oo.baseDeviceHandler.GetUniPortMask() == (1 << uniPort.UniID) {
				if !oo.baseDeviceHandler.GetFlowMonitoringIsRunning(uniPort.UniID) {
					go oo.baseDeviceHandler.PerOnuFlowHandlerRoutine(uniPort.UniID)
				}
			}
		}
		oo.MutexPersOnuConfig.RLock()
		if oo.SOnuPersistentData.PersUniDisableDone {
			oo.MutexPersOnuConfig.RUnlock()
			oo.baseDeviceHandler.DisableUniPortStateUpdate(ctx)
			_ = oo.baseDeviceHandler.ReasonUpdate(ctx, cmn.DrOmciAdminLock, oo.baseDeviceHandler.IsReconcilingReasonUpdate())
		} else {
			oo.MutexPersOnuConfig.RUnlock()
			oo.baseDeviceHandler.EnableUniPortStateUpdate(ctx)
		}

		// no need to reconcile additional data for MibDownloadFsm, LockStateFsm, or UnlockStateFsm

		// During reboot, if the adapter restarted while configuring TP or flows, we need to continue with flow configurations now.
		// Set the mibUpload FSM as success and proceed with flow configs
		oo.MutexPersOnuConfig.RLock()
		if oo.SOnuPersistentData.PersRebootInProgress {
			oo.MutexPersOnuConfig.RUnlock()
			logger.Debugw(ctx, "Set mib upload as success before proceeding with flow configuration", log.Fields{"device-id": oo.deviceID})
			go func() {
				_ = oo.PMibUploadFsm.PFsm.Event(UlEvSuccess)
				if oo.baseDeviceHandler.CheckForDeviceTechProf(ctx) {
					oo.baseDeviceHandler.DeviceFlowConfigOnReboot(ctx)
				}
			}()
		} else {
			oo.MutexPersOnuConfig.RUnlock()
			if oo.baseDeviceHandler.ReconcileDeviceTechProf(ctx) {
				// start go routine with select() on reconciling flow channel before
				// starting flow reconciling process to prevent loss of any signal
				syncChannel := make(chan struct{})
				go func(aSyncChannel chan struct{}) {
					// In multi-ONU/multi-flow environment stopping reconcilement has to be delayed until
					// we get a signal that the processing of the last step to rebuild the adapter internal
					// flow data is finished.
					expiry := oo.baseDeviceHandler.GetReconcileExpiryVlanConfigAbort()
					oo.setReconcilingFlows(true)
					aSyncChannel <- struct{}{}
					select {
					case success := <-oo.chReconcilingFlowsFinished:
						if success {
							logger.Debugw(ctx, "reconciling flows has been finished in time",
								log.Fields{"device-id": oo.deviceID})
							_ = oo.PMibUploadFsm.PFsm.Event(UlEvSuccess)

						} else {
							logger.Debugw(ctx, "wait for reconciling flows aborted",
								log.Fields{"device-id": oo.deviceID})
						}
					case <-time.After(expiry):
						logger.Errorw(ctx, "timeout waiting for reconciling flows to be finished!",
							log.Fields{"device-id": oo.deviceID, "expiry": expiry})
						_ = oo.PMibUploadFsm.PFsm.Event(UlEvMismatch)
					}
					oo.setReconcilingFlows(false)
				}(syncChannel)
				// block further processing until the above Go routine has really started
				// and is ready to receive values from chReconcilingFlowsFinished
				<-syncChannel
				oo.baseDeviceHandler.ReconcileDeviceFlowConfig(ctx)
			}
		}
	} else {
		logger.Debugw(ctx, "MibSync FSM",
			log.Fields{"Getting MIB from template not successful": e.FSM.Current(), "device-id": oo.deviceID})
		go func() {
			//switch to reconciling with OMCI config
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvMismatch)
		}()
	}
}

func (oo *OnuDeviceEntry) enterAuditingState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start MibAudit processing in State": e.FSM.Current(), "device-id": oo.deviceID})
	if oo.baseDeviceHandler.CheckAuditStartCondition(ctx, cmn.CUploadFsm) {
		oo.requestMdsValue(ctx)
	} else {
		logger.Debugw(ctx, "MibSync FSM", log.Fields{"Configuration is ongoing or missing - skip auditing!": e.FSM.Current(), "device-id": oo.deviceID})
		go func() {
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvSuccess)
		}()
	}
}

func (oo *OnuDeviceEntry) enterReAuditingState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start retest MdsValue processing in State": e.FSM.Current(), "device-id": oo.deviceID})
	if oo.baseDeviceHandler.CheckAuditStartCondition(ctx, cmn.CUploadFsm) {
		oo.requestMdsValue(ctx)
	} else {
		logger.Debugw(ctx, "MibSync FSM", log.Fields{"Configuration is ongoing or missing - skip re-auditing!": e.FSM.Current(), "device-id": oo.deviceID})
		go func() {
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvSuccess)
		}()
	}
}

func (oo *OnuDeviceEntry) enterOutOfSyncState(ctx context.Context, e *fsm.Event) {
	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start  MibReconcile processing in State": e.FSM.Current(), "device-id": oo.deviceID})
	logger.Debug(ctx, "function not implemented yet")
}

func (oo *OnuDeviceEntry) processMibSyncMessages(ctx context.Context) {
	logger.Debugw(ctx, "MibSync Msg", log.Fields{"Start routine to process OMCI-messages for device-id": oo.deviceID})
	oo.mutexMibSyncMsgProcessorRunning.Lock()
	oo.mibSyncMsgProcessorRunning = true
	oo.mutexMibSyncMsgProcessorRunning.Unlock()
loop:
	for {
		// case <-ctx.Done():
		// 	logger.Info("MibSync Msg", log.Fields{"Message handling canceled via context for device-id": onuDeviceEntry.deviceID})
		// 	break loop
		message, ok := <-oo.PMibUploadFsm.CommChan
		if !ok {
			logger.Info(ctx, "MibSync Msg", log.Fields{"Message couldn't be read from channel for device-id": oo.deviceID})
			oo.mutexMibSyncMsgProcessorRunning.Lock()
			oo.mibSyncMsgProcessorRunning = false
			oo.mutexMibSyncMsgProcessorRunning.Unlock()
			break loop
		}
		logger.Debugw(ctx, "MibSync Msg", log.Fields{"Received message on ONU MibSyncChan for device-id": oo.deviceID})

		switch message.Type {
		case cmn.TestMsg:
			msg, _ := message.Data.(cmn.TestMessage)
			if msg.TestMessageVal == cmn.AbortMessageProcessing {
				logger.Debugw(ctx, "MibSync Msg abort ProcessMsg", log.Fields{"for device-id": oo.deviceID})
				oo.mutexMibSyncMsgProcessorRunning.Lock()
				oo.mibSyncMsgProcessorRunning = false
				oo.mutexMibSyncMsgProcessorRunning.Unlock()
				break loop
			}
			oo.handleTestMsg(ctx, msg)
		case cmn.OMCI:
			msg, _ := message.Data.(cmn.OmciMessage)
			oo.handleOmciMessage(ctx, msg)
		default:
			logger.Warn(ctx, "MibSync Msg", log.Fields{"Unknown message type received for device-id": oo.deviceID, "message.Type": message.Type})
		}
	}
	logger.Info(ctx, "MibSync Msg", log.Fields{"Stopped handling of MibSyncChan for device-id": oo.deviceID})
	// TODO: only this action?
	_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
}

func (oo *OnuDeviceEntry) handleTestMsg(ctx context.Context, msg cmn.TestMessage) {

	logger.Debugw(ctx, "MibSync Msg", log.Fields{"TestMessage received for device-id": oo.deviceID, "msg.TestMessageVal": msg.TestMessageVal})

	switch msg.TestMessageVal {
	case cmn.LoadMibTemplateFailed:
		_ = oo.PMibUploadFsm.PFsm.Event(UlEvUploadMib)
		logger.Debugw(ctx, "MibSync Msg", log.Fields{"state": string(oo.PMibUploadFsm.PFsm.Current())})
	case cmn.LoadMibTemplateOk:
		_ = oo.PMibUploadFsm.PFsm.Event(UlEvSuccess)
		logger.Debugw(ctx, "MibSync Msg", log.Fields{"state": string(oo.PMibUploadFsm.PFsm.Current())})
	default:
		logger.Warn(ctx, "MibSync Msg", log.Fields{"Unknown message type received for device-id": oo.deviceID, "msg.TestMessageVal": msg.TestMessageVal})
	}
}

func (oo *OnuDeviceEntry) handleOmciMibResetResponseMessage(ctx context.Context, msg cmn.OmciMessage) {
	if oo.PMibUploadFsm.PFsm.Is(UlStResettingMib) {
		msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeMibResetResponse)
		if msgLayer != nil {
			msgObj, msgOk := msgLayer.(*omci.MibResetResponse)
			if msgOk {
				logger.Infow(ctx, "MibResetResponse Data", log.Fields{"data-fields": msgObj})
				if msgObj.Result == me.Success {
					oo.MutexPersOnuConfig.Lock()
					oo.SOnuPersistentData.PersMibDataSyncAdpt = cmn.MdsDefaultMib
					oo.MutexPersOnuConfig.Unlock()
					oo.PDevOmciCC.ResetConfFailMEs()
					// trigger retrieval of VendorId and SerialNumber
					_ = oo.PMibUploadFsm.PFsm.Event(UlEvGetVendorAndSerial)
					return
				}
				logger.Errorw(ctx, "Omci MibResetResponse Error", log.Fields{"device-id": oo.deviceID, "Error": msgObj.Result})
			} else {
				logger.Errorw(ctx, "Omci Msg layer could not be assigned", log.Fields{"device-id": oo.deviceID})
			}
		} else {
			logger.Errorw(ctx, "Omci Msg layer could not be detected", log.Fields{"device-id": oo.deviceID})
		}
	} else {
		//in case the last request was MdsGetRequest this issue may appear if the ONU was online before and has received the MIB reset
		//  with Sequence number 0x8000 as last request before - so it may still respond to that
		//  then we may force the ONU to react on the MdsGetRequest with a new message that uses an increased Sequence number
		oo.mutexLastTxParamStruct.Lock()
		if oo.lastTxParamStruct.lastTxMessageType == omci.GetRequestType && oo.lastTxParamStruct.repeatCount == 0 {
			logger.Debugw(ctx, "MibSync FSM - repeat MdsGetRequest (updated SequenceNumber)", log.Fields{"device-id": oo.deviceID})
			requestedAttributes := me.AttributeValueMap{me.OnuData_MibDataSync: ""}
			_, err := oo.PDevOmciCC.SendGetMe(log.WithSpanFromContext(context.TODO(), ctx),
				me.OnuDataClassID, cmn.OnuDataMeID, requestedAttributes, oo.baseDeviceHandler.GetOmciTimeout(), true, oo.PMibUploadFsm.CommChan, false)
			if err != nil {
				oo.mutexLastTxParamStruct.Unlock()
				logger.Errorw(ctx, "ONUData get failed, aborting MibSync", log.Fields{"device-id": oo.deviceID})
				_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
				return
			}
			//TODO: needs extra handling of timeouts
			oo.lastTxParamStruct.repeatCount = 1
			oo.mutexLastTxParamStruct.Unlock()
			return
		}
		oo.mutexLastTxParamStruct.Unlock()
		logger.Errorw(ctx, "unexpected MibResetResponse - ignoring", log.Fields{"device-id": oo.deviceID})
		//perhaps some still lingering message from some prior activity, let's wait for the real response
		return
	}
	logger.Info(ctx, "MibSync Msg", log.Fields{"Stopped handling of MibSyncChan for device-id": oo.deviceID})
	_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
}

func (oo *OnuDeviceEntry) handleOmciMibUploadResponseMessage(ctx context.Context, msg cmn.OmciMessage) {
	msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeMibUploadResponse)
	if msgLayer == nil {
		logger.Errorw(ctx, "Omci Msg layer could not be detected", log.Fields{"device-id": oo.deviceID})
		return
	}
	msgObj, msgOk := msgLayer.(*omci.MibUploadResponse)
	if !msgOk {
		logger.Errorw(ctx, "Omci Msg layer could not be assigned", log.Fields{"device-id": oo.deviceID})
		return
	}
	logger.Debugw(ctx, "MibUploadResponse Data for:", log.Fields{"device-id": oo.deviceID, "data-fields": msgObj})
	/* to be verified / reworked !!! */
	oo.PDevOmciCC.UploadNoOfCmds = msgObj.NumberOfCommands
	if oo.PDevOmciCC.UploadSequNo < oo.PDevOmciCC.UploadNoOfCmds {
		_ = oo.PDevOmciCC.SendMibUploadNext(log.WithSpanFromContext(context.TODO(), ctx),
			oo.baseDeviceHandler.GetOmciTimeout(), true, oo.GetPersIsExtOmciSupported())
		//even though lastTxParameters are currently not used for checking the ResetResponse message we have to ensure
		//  that the lastTxMessageType is correctly set to avoid misinterpreting other responses
		oo.mutexLastTxParamStruct.Lock()
		oo.lastTxParamStruct.lastTxMessageType = omci.MibUploadNextRequestType
		oo.mutexLastTxParamStruct.Unlock()
	} else {
		logger.Errorw(ctx, "Invalid number of commands received for:", log.Fields{"device-id": oo.deviceID, "UploadNoOfCmds": oo.PDevOmciCC.UploadNoOfCmds})
		//TODO right action?
		_ = oo.PMibUploadFsm.PFsm.Event(UlEvTimeout)
	}
}

func (oo *OnuDeviceEntry) handleOmciMibUploadNextResponseMessage(ctx context.Context, msg cmn.OmciMessage) {
	msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeMibUploadNextResponse)
	if msgLayer != nil {
		msgObj, msgOk := msgLayer.(*omci.MibUploadNextResponse)
		if !msgOk {
			logger.Errorw(ctx, "Omci Msg layer could not be assigned", log.Fields{"device-id": oo.deviceID})
			return
		}
		meName := msgObj.ReportedME.GetName()
		meClassID := msgObj.ReportedME.GetClassID()
		meEntityID := msgObj.ReportedME.GetEntityID()

		logger.Debugw(ctx, "MibUploadNextResponse Data for:", log.Fields{"device-id": oo.deviceID, "meName": meName, "data-fields": msgObj})

		if meName == devdb.CUnknownItuG988ManagedEntity || meName == devdb.CUnknownVendorSpecificManagedEntity {
			logger.Debugw(ctx, "MibUploadNextResponse contains unknown ME", log.Fields{"device-id": oo.deviceID,
				"Me-Name": devdb.UnknownMeOrAttribName(meName), "Me-ClassId": meClassID, "Me-InstId": meEntityID,
				"unknown mask": msgObj.ReportedME.GetAttributeMask(), "unknown attributes": msgObj.Payload})
			oo.pOnuDB.PutUnknownMeOrAttrib(ctx, devdb.UnknownMeOrAttribName(meName), meClassID, meEntityID,
				msgObj.ReportedME.GetAttributeMask(), msgObj.Payload[devdb.CStartUnknownMeAttribsInBaseLayerPayload:])
		} else {
			//with relaxed decoding set in the OMCI-LIB we have the chance to detect if there are some unknown attributes appended which we cannot decode
			if unknownAttrLayer := (*msg.OmciPacket).Layer(omci.LayerTypeUnknownAttributes); unknownAttrLayer != nil {
				logger.Warnw(ctx, "MibUploadNextResponse contains unknown attributes", log.Fields{"device-id": oo.deviceID})
				if unknownAttributes, ok := unknownAttrLayer.(*omci.UnknownAttributes); ok {
					// provide a loop over several ME's here already in preparation of OMCI extended message format
					for _, unknown := range unknownAttributes.Attributes {
						unknownAttrClassID := unknown.EntityClass // ClassID
						unknownAttrInst := unknown.EntityInstance // uint16
						unknownAttrMask := unknown.AttributeMask  // ui
						unknownAttrBlob := unknown.AttributeData  // []byte
						logger.Warnw(ctx, "unknown attributes detected for", log.Fields{"device-id": oo.deviceID,
							"Me-ClassId": unknownAttrClassID, "Me-InstId": unknownAttrInst, "unknown mask": unknownAttrMask,
							"unknown attributes": unknownAttrBlob})
						oo.pOnuDB.PutUnknownMeOrAttrib(ctx, devdb.CUnknownAttributesManagedEntity, unknown.EntityClass, unknown.EntityInstance,
							unknown.AttributeMask, unknown.AttributeData)
					} // for all included ME's with unknown attributes
				} else {
					logger.Errorw(ctx, "unknownAttrLayer could not be decoded", log.Fields{"device-id": oo.deviceID})
				}
			}
			oo.pOnuDB.PutMe(ctx, meClassID, meEntityID, msgObj.ReportedME.GetAttributeValueMap())
		}
		if msg.OmciMsg.DeviceIdentifier == omci.ExtendedIdent {
			for _, additionalME := range msgObj.AdditionalMEs {
				meName := additionalME.GetName()
				meClassID := additionalME.GetClassID()
				meEntityID := additionalME.GetEntityID()
				attributes := additionalME.GetAttributeValueMap()

				if meName == devdb.CUnknownItuG988ManagedEntity || meName == devdb.CUnknownVendorSpecificManagedEntity {
					attribMask := additionalME.GetAttributeMask()
					logger.Debugw(ctx, "MibUploadNextResponse AdditionalData contains unknown ME", log.Fields{"device-id": oo.deviceID,
						"Me-Name": devdb.UnknownMeOrAttribName(meName), "Me-ClassId": meClassID, "Me-InstId": meEntityID,
						"unknown mask": attribMask})

					attribValues := make([]byte, 0)
					for key, value := range attributes {
						if key != cmn.CGenericManagedEntityIDName {
							data, err := me.InterfaceToOctets(value)
							if err != nil {
								logger.Infow(ctx, "MibUploadNextResponse unknown ME AdditionalData attrib - could not decode", log.Fields{"device-id": oo.deviceID, "key": key})
							} else {
								attribValues = append(attribValues[:], data[:]...)
								logger.Debugw(ctx, "MibUploadNextResponse unknown ME AdditionalData attrib", log.Fields{"device-id": oo.deviceID, "attribValues": attribValues, "data": data, "key": key})
							}
						}
					}
					oo.pOnuDB.PutUnknownMeOrAttrib(ctx, devdb.UnknownMeOrAttribName(meName), meClassID, meEntityID, attribMask, attribValues)
				} else {
					logger.Debugw(ctx, "MibUploadNextResponse AdditionalData for:", log.Fields{"device-id": oo.deviceID, "meName": meName, "meEntityID": meEntityID, "attributes": attributes})
					oo.pOnuDB.PutMe(ctx, meClassID, meEntityID, attributes)
				}
			}
		}
	} else {
		logger.Errorw(ctx, "Omci Msg layer could not be detected", log.Fields{"device-id": oo.deviceID})
		//as long as omci-lib does not support decoding of table attribute as 'unknown/unspecified' attribute
		//  we have to verify, if this failure is from table attribute and try to go forward with ignoring the complete message
		errLayer := (*msg.OmciPacket).Layer(gopacket.LayerTypeDecodeFailure)
		if failure, decodeOk := errLayer.(*gopacket.DecodeFailure); decodeOk {
			errMsg := failure.String()
			if !strings.Contains(strings.ToLower(errMsg), "table decode") {
				//something still unexected happened, needs deeper investigation - stop complete MIB upload process (timeout)
				return
			}
			logger.Warnw(ctx, "Decode issue on received MibUploadNextResponse frame - found table attribute(s) (message ignored)",
				log.Fields{"device-id": oo.deviceID, "issue": errMsg})
		}
	}
	if oo.PDevOmciCC.UploadSequNo < oo.PDevOmciCC.UploadNoOfCmds {
		_ = oo.PDevOmciCC.SendMibUploadNext(log.WithSpanFromContext(context.TODO(), ctx),
			oo.baseDeviceHandler.GetOmciTimeout(), true, oo.GetPersIsExtOmciSupported())
		//even though lastTxParameters are currently not used for checking the ResetResponse message we have to ensure
		//  that the lastTxMessageType is correctly set to avoid misinterpreting other responses
		oo.mutexLastTxParamStruct.Lock()
		oo.lastTxParamStruct.lastTxMessageType = omci.MibUploadNextRequestType
		oo.mutexLastTxParamStruct.Unlock()
	} else {
		err := oo.createAndPersistMibTemplate(ctx)
		if err != nil {
			logger.Errorw(ctx, "MibSync - MibTemplate - Failed to create and persist the mib template", log.Fields{"error": err, "device-id": oo.deviceID})
		}

		oo.updateOnuSpecificEntries(ctx)
		logger.Errorw(ctx, "MibSync - Updtaed the ONU Specific MEs ", log.Fields{"device-id": oo.deviceID})

		cmnMEDB, _ := oo.pOpenOnuAc.FetchEntryFromMibDatabaseMap(ctx, oo.mibTemplatePath)
		cmnMEDB.MIBUploadStatus = devdb.Completed
		oo.pOnuDB.CommonMeDb.MeDbLock.Unlock()
		oo.pOpenOnuAc.SetMibTemplatesGenerated(oo.mibTemplatePath, true)
		oo.pOpenOnuAc.UnlockMutexMibTemplateGenerated()

		_ = oo.PMibUploadFsm.PFsm.Event(UlEvSuccess)
	}
}

// nolint: gocyclo
func (oo *OnuDeviceEntry) handleOmciGetResponseMessage(ctx context.Context, msg cmn.OmciMessage) error {
	var err error = nil

	oo.mutexLastTxParamStruct.RLock()
	if oo.lastTxParamStruct.lastTxMessageType != omci.GetRequestType ||
		oo.lastTxParamStruct.pLastTxMeInstance == nil {
		//in case the last request was MibReset this issue may appear if the ONU was online before and has received the MDS GetRequest
		//  with Sequence number 0x8000 as last request before - so it may still respond to that
		//  then we may force the ONU to react on the MIB reset with a new message that uses an increased Sequence number
		if oo.lastTxParamStruct.lastTxMessageType == omci.MibResetRequestType && oo.lastTxParamStruct.repeatCount == 0 {
			logger.Debugw(ctx, "MibSync FSM - repeat mibReset (updated SequenceNumber)", log.Fields{"device-id": oo.deviceID})
			_ = oo.PDevOmciCC.SendMibReset(log.WithSpanFromContext(context.TODO(), ctx), oo.baseDeviceHandler.GetOmciTimeout(), true)
			//TODO: needs extra handling of timeouts
			oo.lastTxParamStruct.repeatCount = 1
			oo.mutexLastTxParamStruct.RUnlock()
			return nil
		}
		oo.mutexLastTxParamStruct.RUnlock()
		logger.Warnw(ctx, "unexpected GetResponse - ignoring", log.Fields{"device-id": oo.deviceID})
		//perhaps some still lingering message from some prior activity, let's wait for the real response
		return nil
	}
	oo.mutexLastTxParamStruct.RUnlock()
	msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeGetResponse)
	if msgLayer == nil {
		logger.Errorw(ctx, "omci Msg layer could not be detected for GetResponse - handling of MibSyncChan stopped",
			log.Fields{"device-id": oo.deviceID})
		_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
		return fmt.Errorf("omci Msg layer could not be detected for GetResponse - handling of MibSyncChan stopped: %s", oo.deviceID)
	}
	msgObj, msgOk := msgLayer.(*omci.GetResponse)
	if !msgOk {
		logger.Errorw(ctx, "omci Msg layer could not be assigned for GetResponse - handling of MibSyncChan stopped",
			log.Fields{"device-id": oo.deviceID})
		_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
		return fmt.Errorf("omci Msg layer could not be assigned for GetResponse - handling of MibSyncChan stopped: %s", oo.deviceID)
	}
	logger.Debugw(ctx, "MibSync FSM - GetResponse Data", log.Fields{"device-id": oo.deviceID, "data-fields": msgObj})
	if msgObj.Result == me.Success {
		oo.mutexLastTxParamStruct.RLock()
		entityID := oo.lastTxParamStruct.pLastTxMeInstance.GetEntityID()
		if msgObj.EntityClass == oo.lastTxParamStruct.pLastTxMeInstance.GetClassID() && msgObj.EntityInstance == entityID {
			meAttributes := msgObj.Attributes
			meInstance := oo.lastTxParamStruct.pLastTxMeInstance.GetName()
			logger.Debugf(ctx, "MibSync FSM - GetResponse Data for %s",
				log.Fields{"device-id": oo.deviceID, "data-fields": msgObj}, meInstance)
			switch meInstance {
			case "OnuG":
				oo.mutexLastTxParamStruct.RUnlock()
				return oo.handleOmciGetResponseOnuG(ctx, meAttributes)
			case "Onu2G":
				oo.mutexLastTxParamStruct.RUnlock()
				var equipmentID string
				if onu2GEquipmentID, ok := meAttributes[me.Onu2G_EquipmentId]; ok {
					equipmentID = cmn.TrimStringFromMeOctet(onu2GEquipmentID)
					if equipmentID == "" {
						logger.Infow(ctx,
							"MibSync FSM - optional attribute EquipmentID is empty in Onu2G instance - fill with appropriate value",
							log.Fields{"device-id": oo.deviceID})
						equipmentID = cEmptyEquipIDString
					}
				} else {
					logger.Infow(ctx,
						"MibSync FSM - optional attribute EquipmentID not present in Onu2G instance - fill with appropriate value",
						log.Fields{"device-id": oo.deviceID})
					equipmentID = cNotPresentEquipIDString
				}
				oo.MutexPersOnuConfig.Lock()
				oo.SOnuPersistentData.PersEquipmentID = equipmentID
				logger.Debugw(ctx, "MibSync FSM - GetResponse Data for Onu2-G - EquipmentId", log.Fields{"device-id": oo.deviceID,
					"onuDeviceEntry.equipmentID": oo.SOnuPersistentData.PersEquipmentID})
				oo.MutexPersOnuConfig.Unlock()

				var omccVersion uint8
				if onu2GOmccVersion, ok := meAttributes[me.Onu2G_OpticalNetworkUnitManagementAndControlChannelOmccVersion]; ok {
					oo.MutexPersOnuConfig.Lock()
					omccVersion = onu2GOmccVersion.(uint8)
					if _, ok := omccVersionSupportsExtendedOmciFormat[omccVersion]; ok {
						oo.SOnuPersistentData.PersIsExtOmciSupported = omccVersionSupportsExtendedOmciFormat[omccVersion]
						if oo.SOnuPersistentData.PersIsExtOmciSupported && !oo.baseDeviceHandler.GetExtendedOmciSupportEnabled() {
							logger.Infow(ctx, "MibSync FSM - ONU supports extended OMCI, but support is disabled in the adapter: reset flag",
								log.Fields{"device-id": oo.deviceID})
							oo.SOnuPersistentData.PersIsExtOmciSupported = false
						}
					} else {
						logger.Infow(ctx, "MibSync FSM - unknown OMCC version in Onu2G instance - disable extended OMCI support",
							log.Fields{"device-id": oo.deviceID})
						oo.SOnuPersistentData.PersIsExtOmciSupported = false
					}
					logger.Debugw(ctx, "MibSync FSM - GetResponse Data for Onu2-G - OMCC version", log.Fields{"device-id": oo.deviceID,
						"omccVersion": omccVersion, "isExtOmciSupported": oo.SOnuPersistentData.PersIsExtOmciSupported})
					oo.MutexPersOnuConfig.Unlock()
				} else {
					logger.Errorw(ctx,
						"MibSync FSM - mandatory attribute OMCC version not present in Onu2G instance - handling of MibSyncChan stopped!",
						log.Fields{"device-id": oo.deviceID})
					_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
					return fmt.Errorf(
						"mibSync FSM - mandatory attribute OMCC version not present in Onu2G instance - handling of MibSyncChan stopped: %s",
						oo.deviceID)
				}
				oo.MutexPersOnuConfig.RLock()
				if oo.SOnuPersistentData.PersIsExtOmciSupported {
					oo.MutexPersOnuConfig.RUnlock()
					// trigger test of OMCI extended msg format
					_ = oo.PMibUploadFsm.PFsm.Event(UlEvTestExtOmciSupport)
					return nil
				}
				oo.MutexPersOnuConfig.RUnlock()
				// trigger retrieval of 1st SW-image info
				_ = oo.PMibUploadFsm.PFsm.Event(UlEvGetFirstSwVersion)
				return nil
			case "SoftwareImage":
				oo.mutexLastTxParamStruct.RUnlock()
				if entityID > cmn.SecondSwImageMeID {
					logger.Errorw(ctx, "mibSync FSM - Failed to GetResponse Data for SoftwareImage with expected EntityId",
						log.Fields{"device-id": oo.deviceID, "entity-ID": entityID})
					return fmt.Errorf("mibSync FSM - SwResponse Data with unexpected EntityId: %s %x",
						oo.deviceID, entityID)
				}
				// need to use function for go lint complexity
				if !oo.HandleSwImageIndications(ctx, entityID, meAttributes) {
					logger.Errorw(ctx,
						"MibSync FSM - Not all mandatory attributes present in in SoftwareImage instance - handling of MibSyncChan stopped!",
						log.Fields{"device-id": oo.deviceID})
					_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
					return fmt.Errorf(
						"mibSync FSM - Not all mandatory attributes present in in SoftwareImage instance - handling of MibSyncChan stopped: %s",
						oo.deviceID)
				}
				return nil
			case "IpHostConfigData":
				oo.mutexLastTxParamStruct.RUnlock()
				oo.MutexPersOnuConfig.Lock()
				if ipHostConfigMacAddress, ok := meAttributes[me.IpHostConfigData_MacAddress]; ok {
					macBytes, _ := me.InterfaceToOctets(ipHostConfigMacAddress)
					if cmn.OmciMacAddressLen == len(macBytes) {
						oo.SOnuPersistentData.PersMacAddress = hex.EncodeToString(macBytes[:])
						logger.Debugw(ctx, "MibSync FSM - GetResponse Data for IpHostConfigData - MacAddress", log.Fields{"device-id": oo.deviceID,
							"macAddress": oo.SOnuPersistentData.PersMacAddress})
					} else {
						logger.Infow(ctx, "MibSync FSM - MacAddress wrong length - fill macAddress with zeros",
							log.Fields{"device-id": oo.deviceID, "length": len(macBytes)})
						oo.SOnuPersistentData.PersMacAddress = cEmptyMacAddrString
					}
				} else {
					// since ONU creates instances of this ME automatically only when IP host services are available,
					// processing continues here despite the error
					logger.Infow(ctx, "MibSync FSM - MacAddress attribute not present in IpHostConfigData instance - fill macAddress with zeros",
						log.Fields{"device-id": oo.deviceID})
					oo.SOnuPersistentData.PersMacAddress = cEmptyMacAddrString
				}
				oo.MutexPersOnuConfig.Unlock()
				// trigger retrieval of mib template
				_ = oo.PMibUploadFsm.PFsm.Event(UlEvGetMibTemplate)
				return nil
			case "OnuData":
				oo.mutexLastTxParamStruct.RUnlock()
				if onuDataMibDataSync, ok := meAttributes[me.OnuData_MibDataSync]; ok {
					oo.checkMdsValue(ctx, onuDataMibDataSync.(uint8))
				} else {
					logger.Errorw(ctx, "MibSync FSM - MibDataSync attribute not present in OnuData instance - handling of MibSyncChan stopped!",
						log.Fields{"device-id": oo.deviceID})
					_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
					return fmt.Errorf("mibSync FSM - VendorId attribute not present in OnuG instance - handling of MibSyncChan stopped: %s",
						oo.deviceID)
				}
				return nil
			default:
				oo.mutexLastTxParamStruct.RUnlock()
				logger.Warnw(ctx, "Unsupported ME name received!",
					log.Fields{"ME name": meInstance, "device-id": oo.deviceID})

			}
		} else {
			oo.mutexLastTxParamStruct.RUnlock()
			logger.Warnf(ctx, "MibSync FSM - Received GetResponse Data for %s with wrong classID or entityID ",
				log.Fields{"device-id": oo.deviceID, "data-fields": msgObj}, msgObj.EntityClass)
		}
	} else {
		if err = oo.handleOmciGetResponseErrors(ctx, msgObj); err == nil {
			return nil
		}
	}
	logger.Info(ctx, "MibSync Msg", log.Fields{"Stopped handling of MibSyncChan for device-id": oo.deviceID})
	_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
	return err
}

// HandleSwImageIndications updates onuSwImageIndications with the ONU data just received
func (oo *OnuDeviceEntry) HandleSwImageIndications(ctx context.Context, entityID uint16, meAttributes me.AttributeValueMap) bool {

	var imageVersion string
	var imageIsCommitted, imageIsActive uint8

	allMandAttribsPresent := false
	if softwareImageIsCommitted, ok := meAttributes[me.SoftwareImage_IsCommitted]; ok {
		if softwareImageIsActiveimage, ok := meAttributes[me.SoftwareImage_IsActive]; ok {
			if softwareImageVersion, ok := meAttributes[me.SoftwareImage_Version]; ok {
				imageVersion = cmn.TrimStringFromMeOctet(softwareImageVersion)
				imageIsActive = softwareImageIsActiveimage.(uint8)
				imageIsCommitted = softwareImageIsCommitted.(uint8)
				allMandAttribsPresent = true
			}
		}
	}
	if !allMandAttribsPresent {
		logger.Errorw(ctx, "MibSync FSM - Not all mandatory attributes present in SoftwareImage instance - skip processing!", log.Fields{"device-id": oo.deviceID})
		return allMandAttribsPresent
	}
	oo.MutexPersOnuConfig.RLock()
	logger.Infow(ctx, "MibSync FSM - GetResponse Data for SoftwareImage",
		log.Fields{"device-id": oo.deviceID, "entityID": entityID,
			"version": imageVersion, "isActive": imageIsActive, "isCommitted": imageIsCommitted, "SNR": oo.SOnuPersistentData.PersSerialNumber})
	oo.MutexPersOnuConfig.RUnlock()
	if cmn.FirstSwImageMeID == entityID {
		//always accept the state of the first image (2nd image info should not yet be available)
		oo.mutexOnuSwImageIndications.Lock()
		if imageIsActive == cmn.SwIsActive {
			oo.onuSwImageIndications.ActiveEntityEntry.EntityID = entityID
			oo.onuSwImageIndications.ActiveEntityEntry.Valid = true
			oo.onuSwImageIndications.ActiveEntityEntry.Version = imageVersion
			oo.onuSwImageIndications.ActiveEntityEntry.IsCommitted = imageIsCommitted
			//as the SW version indication may stem from some ONU Down/up event
			//the complementary image state is to be invalidated
			//  (state of the second image is always expected afterwards or just invalid)
			oo.onuSwImageIndications.InActiveEntityEntry.Valid = false
		} else {
			oo.onuSwImageIndications.InActiveEntityEntry.EntityID = entityID
			oo.onuSwImageIndications.InActiveEntityEntry.Valid = true
			oo.onuSwImageIndications.InActiveEntityEntry.Version = imageVersion
			oo.onuSwImageIndications.InActiveEntityEntry.IsCommitted = imageIsCommitted
			//as the SW version indication may stem form some ONU Down/up event
			//the complementary image state is to be invalidated
			//  (state of the second image is always expected afterwards or just invalid)
			oo.onuSwImageIndications.ActiveEntityEntry.Valid = false
		}
		oo.mutexOnuSwImageIndications.Unlock()
		_ = oo.PMibUploadFsm.PFsm.Event(UlEvGetSecondSwVersion)
		return allMandAttribsPresent
	} else if cmn.SecondSwImageMeID == entityID {
		//2nd image info might conflict with first image info, in which case we priorize first image info!
		oo.mutexOnuSwImageIndications.Lock()
		if imageIsActive == cmn.SwIsActive { //2nd image reported to be active
			if oo.onuSwImageIndications.ActiveEntityEntry.Valid {
				//conflict exists - state of first image is left active
				logger.Warnw(ctx, "mibSync FSM - both ONU images are reported as active - assuming 2nd to be inactive",
					log.Fields{"device-id": oo.deviceID})
				oo.onuSwImageIndications.InActiveEntityEntry.EntityID = entityID
				oo.onuSwImageIndications.InActiveEntityEntry.Valid = true ////to indicate that at least something has been reported
				oo.onuSwImageIndications.InActiveEntityEntry.Version = imageVersion
				oo.onuSwImageIndications.InActiveEntityEntry.IsCommitted = imageIsCommitted
			} else { //first image inactive, this one active
				oo.onuSwImageIndications.ActiveEntityEntry.EntityID = entityID
				oo.onuSwImageIndications.ActiveEntityEntry.Valid = true
				oo.onuSwImageIndications.ActiveEntityEntry.Version = imageVersion
				oo.onuSwImageIndications.ActiveEntityEntry.IsCommitted = imageIsCommitted
			}
		} else { //2nd image reported to be inactive
			if oo.onuSwImageIndications.InActiveEntityEntry.Valid {
				//conflict exists - both images inactive - regard it as ONU failure and assume first image to be active
				logger.Warnw(ctx, "mibSync FSM - both ONU images are reported as inactive, defining first to be active",
					log.Fields{"device-id": oo.deviceID})
				oo.onuSwImageIndications.ActiveEntityEntry.EntityID = cmn.FirstSwImageMeID
				oo.onuSwImageIndications.ActiveEntityEntry.Valid = true //to indicate that at least something has been reported
				//copy active commit/version from the previously stored inactive position
				oo.onuSwImageIndications.ActiveEntityEntry.Version = oo.onuSwImageIndications.InActiveEntityEntry.Version
				oo.onuSwImageIndications.ActiveEntityEntry.IsCommitted = oo.onuSwImageIndications.InActiveEntityEntry.IsCommitted
			}
			//in any case we indicate (and possibly overwrite) the second image indications as inactive
			oo.onuSwImageIndications.InActiveEntityEntry.EntityID = entityID
			oo.onuSwImageIndications.InActiveEntityEntry.Valid = true
			oo.onuSwImageIndications.InActiveEntityEntry.Version = imageVersion
			oo.onuSwImageIndications.InActiveEntityEntry.IsCommitted = imageIsCommitted
		}
		oo.mutexOnuSwImageIndications.Unlock()
		_ = oo.PMibUploadFsm.PFsm.Event(UlEvGetMacAddress)
	}
	return allMandAttribsPresent
}

func (oo *OnuDeviceEntry) handleOmciMessage(ctx context.Context, msg cmn.OmciMessage) {
	logger.Debugw(ctx, "MibSync Msg", log.Fields{"OmciMessage received for device-id": oo.deviceID,
		"msgType": msg.OmciMsg.MessageType, "msg": msg})
	//further analysis could be done here based on msg.OmciMsg.Payload, e.g. verification of error code ...
	switch msg.OmciMsg.MessageType {
	case omci.MibResetResponseType:
		oo.handleOmciMibResetResponseMessage(ctx, msg)

	case omci.MibUploadResponseType:
		oo.handleOmciMibUploadResponseMessage(ctx, msg)

	case omci.MibUploadNextResponseType:
		oo.handleOmciMibUploadNextResponseMessage(ctx, msg)

	case omci.GetResponseType:
		//TODO: error handling
		_ = oo.handleOmciGetResponseMessage(ctx, msg)

	default:
		logger.Warnw(ctx, "Unknown Message Type", log.Fields{"device-id": oo.deviceID, "msgType": msg.OmciMsg.MessageType})

	}
}

func (oo *OnuDeviceEntry) handleOmciGetResponseOnuG(ctx context.Context, meAttributes me.AttributeValueMap) error {
	currentState := oo.PMibUploadFsm.PFsm.Current()
	switch currentState {
	case UlStGettingVendorAndSerial:
		if onuGVendorID, ok := meAttributes[me.OnuG_VendorId]; ok {
			vendorID := cmn.TrimStringFromMeOctet(onuGVendorID)
			if vendorID == "" {
				logger.Infow(ctx,
					"MibSync FSM - mandatory attribute VendorId is empty in OnuG instance - fill with appropriate value",
					log.Fields{"device-id": oo.deviceID})
				vendorID = cEmptyVendorIDString
			}
			oo.MutexPersOnuConfig.Lock()
			oo.SOnuPersistentData.PersVendorID = vendorID
			oo.MutexPersOnuConfig.Unlock()
		} else {
			logger.Errorw(ctx,
				"MibSync FSM - mandatory attribute VendorId not present in OnuG instance - handling of MibSyncChan stopped!",
				log.Fields{"device-id": oo.deviceID})
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
			return fmt.Errorf(
				"mibSync FSM - mandatory attribute VendorId not present in OnuG instance - handling of MibSyncChan stopped: %s",
				oo.deviceID)
		}
		if onuGSerialNumber, ok := meAttributes[me.OnuG_SerialNumber]; ok {
			oo.MutexPersOnuConfig.Lock()
			snBytes, _ := me.InterfaceToOctets(onuGSerialNumber)
			if cmn.OnugSerialNumberLen == len(snBytes) {
				snVendorPart := string(snBytes[:4])
				snNumberPart := hex.EncodeToString(snBytes[4:])
				oo.SOnuPersistentData.PersSerialNumber = snVendorPart + snNumberPart
			} else {
				logger.Infow(ctx, "MibSync FSM - SerialNumber has wrong length - fill serialNumber with zeros",
					log.Fields{"device-id": oo.deviceID, "length": len(snBytes)})
				oo.SOnuPersistentData.PersSerialNumber = cEmptySerialNumberString
			}
			oo.MutexPersOnuConfig.Unlock()
		} else {
			logger.Errorw(ctx,
				"MibSync FSM - mandatory attribute SerialNumber not present in OnuG instance - handling of MibSyncChan stopped!",
				log.Fields{"device-id": oo.deviceID})
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
			return fmt.Errorf(
				"mibSync FSM - mandatory attribute SerialNumber not present in OnuG instance - handling of MibSyncChan stopped: %s",
				oo.deviceID)
		}
		oo.MutexPersOnuConfig.Lock()
		logger.Debugw(ctx, "MibSync FSM - GetResponse Data for Onu-G - VendorId/SerialNumber", log.Fields{"device-id": oo.deviceID,
			"onuDeviceEntry.vendorID":     oo.SOnuPersistentData.PersVendorID,
			"onuDeviceEntry.serialNumber": oo.SOnuPersistentData.PersSerialNumber})
		oo.MutexPersOnuConfig.Unlock()
		// trigger retrieval of Version
		_ = oo.PMibUploadFsm.PFsm.Event(UlEvGetVersion)
		return nil
	case UlStGettingVersion:
		if onuGVersion, ok := meAttributes[me.OnuG_Version]; ok {
			version := cmn.TrimStringFromMeOctet(onuGVersion)
			if version == "" {
				logger.Infow(ctx, "MibSync FSM - mandatory attribute Version is empty in OnuG instance - fill with appropriate value",
					log.Fields{"device-id": oo.deviceID})
				version = cEmptyVersionString
			}
			oo.MutexPersOnuConfig.Lock()
			oo.SOnuPersistentData.PersVersion = version
			oo.MutexPersOnuConfig.Unlock()
		} else {
			logger.Errorw(ctx,
				"MibSync FSM - mandatory attribute Version not present in OnuG instance - handling of MibSyncChan stopped!",
				log.Fields{"device-id": oo.deviceID})
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
			return fmt.Errorf(
				"mibSync FSM - mandatory attribute Version not present in OnuG instance - handling of MibSyncChan stopped: %s",
				oo.deviceID)
		}
		oo.MutexPersOnuConfig.Lock()
		logger.Debugw(ctx, "MibSync FSM - GetResponse Data for Onu-G - Version", log.Fields{"device-id": oo.deviceID,
			"onuDeviceEntry.version": oo.SOnuPersistentData.PersVersion})
		oo.MutexPersOnuConfig.Unlock()
		// trigger retrieval of EquipmentId and OMCC version
		_ = oo.PMibUploadFsm.PFsm.Event(UlEvGetEquipIDAndOmcc)
		return nil
	default:
		logger.Errorw(ctx, "MibSync FSM - wrong state OnuG response processing - handling of MibSyncChan stopped!",
			log.Fields{"currentState": currentState, "device-id": oo.deviceID})
		_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
		return fmt.Errorf("mibSync FSM - wrong state OnuG response processing - handling of MibSyncChan stopped: %s",
			oo.deviceID)
	}
}

func (oo *OnuDeviceEntry) handleOmciGetResponseErrors(ctx context.Context, msgObj *omci.GetResponse) error {
	var err error = nil
	logger.Debugf(ctx, "MibSync FSM - erroneous result in GetResponse Data: %s", log.Fields{"device-id": oo.deviceID, "data-fields": msgObj}, msgObj.Result)
	// Up to now the following erroneous results have been seen for different ONU-types to indicate an unsupported ME
	if msgObj.Result == me.UnknownInstance || msgObj.Result == me.UnknownEntity || msgObj.Result == me.ProcessingError || msgObj.Result == me.NotSupported {
		oo.mutexLastTxParamStruct.RLock()
		if oo.lastTxParamStruct.pLastTxMeInstance != nil {
			entityID := oo.lastTxParamStruct.pLastTxMeInstance.GetEntityID()
			if msgObj.EntityClass == oo.lastTxParamStruct.pLastTxMeInstance.GetClassID() && msgObj.EntityInstance == entityID {
				meInstance := oo.lastTxParamStruct.pLastTxMeInstance.GetName()
				switch meInstance {
				case "IpHostConfigData":
					oo.mutexLastTxParamStruct.RUnlock()
					logger.Debugw(ctx, "MibSync FSM - erroneous result for IpHostConfigData received - ONU doesn't support ME - fill macAddress with zeros",
						log.Fields{"device-id": oo.deviceID, "data-fields": msgObj})
					oo.MutexPersOnuConfig.Lock()
					oo.SOnuPersistentData.PersMacAddress = cEmptyMacAddrString
					oo.MutexPersOnuConfig.Unlock()
					// trigger retrieval of mib template
					_ = oo.PMibUploadFsm.PFsm.Event(UlEvGetMibTemplate)
					return nil
				default:
					oo.mutexLastTxParamStruct.RUnlock()
					logger.Warnf(ctx, "MibSync FSM - erroneous result for %s received - no exceptional treatment defined", log.Fields{"device-id": oo.deviceID, "data-fields": msgObj}, meInstance)
					err = fmt.Errorf("erroneous result for %s received - no exceptional treatment defined: %s", meInstance, oo.deviceID)
				}
			} else {
				oo.mutexLastTxParamStruct.RUnlock()
			}
		} else {
			oo.mutexLastTxParamStruct.RUnlock()
			logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oo.deviceID})
		}
	} else {
		logger.Errorf(ctx, "MibSync FSM - erroneous result in GetResponse Data: %s", log.Fields{"device-id": oo.deviceID, "data-fields": msgObj}, msgObj.Result)
		err = fmt.Errorf("erroneous result in GetResponse Data: %s - %s", msgObj.Result, oo.deviceID)
	}
	return err
}

// IsNewOnu - TODO: add comment
func (oo *OnuDeviceEntry) IsNewOnu() bool {
	oo.MutexPersOnuConfig.RLock()
	defer oo.MutexPersOnuConfig.RUnlock()
	return oo.SOnuPersistentData.PersMibLastDbSync == 0
}

func isSupportedClassID(meClassID me.ClassID) bool {
	for _, v := range supportedClassIds {
		if v == meClassID {
			return true
		}
	}
	return false
}

func (oo *OnuDeviceEntry) mibDbVolatileDict(ctx context.Context) error {
	logger.Debug(ctx, "MibVolatileDict- running from default Entry code")
	return errors.New("not_implemented")
}

// createAndPersistMibTemplate method creates a mib template for the device id when operator enables the ONU device for the first time.
// We are creating a placeholder for "SerialNumber" for ME Class ID 6 and 256 and "MacAddress" for ME Class ID 134 in the template
// and then storing the template into etcd "service/voltha/omci_mibs/go_templates/verdor_id/equipment_id/software_version" path.
func (oo *OnuDeviceEntry) createAndPersistMibTemplate(ctx context.Context) error {
	logger.Debugw(ctx, "MibSync - MibTemplate - path name", log.Fields{"path": oo.mibTemplatePath,
		"device-id": oo.deviceID})

	//Fetch the MEs dependent on serial number and MAC address and add them to onuSpecific ME DB
	//Modify the serial number and MAC address with generic content in the common DB.
	knownAttributeMEDb := oo.pOnuDB.CommonMeDb.MeDb
	for firstLevelKey, firstLevelValue := range knownAttributeMEDb {
		classID := strconv.Itoa(int(firstLevelKey))

		for secondLevelKey, secondLevelValue := range firstLevelValue {
			switch classID {
			case "6", "256":
				if _, exists := secondLevelValue["SerialNumber"]; exists {
					oo.pOnuDB.PutOnuSpeficMe(ctx, firstLevelKey, secondLevelKey, secondLevelValue)
					secondLevelValue["SerialNumber"] = "%SERIAL_NUMBER%"
				}
			case "134":
				if _, exists := secondLevelValue["MacAddress"]; exists {
					oo.pOnuDB.PutOnuSpeficMe(ctx, firstLevelKey, secondLevelKey, secondLevelValue)
					secondLevelValue["MacAddress"] = "%MAC_ADDRESS%"
				}
			}
		}
	}

	//Create the MIB Template
	currentTime := time.Now()
	templateMap := make(map[string]interface{})
	templateMap["TemplateName"] = oo.mibTemplatePath
	templateMap["TemplateCreated"] = currentTime.Format("2006-01-02 15:04:05.000000")

	firstLevelMap := oo.pOnuDB.CommonMeDb.MeDb
	for firstLevelKey, firstLevelValue := range firstLevelMap {
		logger.Debugw(ctx, "MibSync - MibTemplate - firstLevelKey", log.Fields{"firstLevelKey": firstLevelKey})
		classID := strconv.Itoa(int(firstLevelKey))

		secondLevelMap := make(map[string]interface{})
		for secondLevelKey, secondLevelValue := range firstLevelValue {
			// ManagedEntityId is already key of secondLevelMap - remove this redundant attribute from secondLevelValue
			delete(secondLevelValue, cmn.CGenericManagedEntityIDName)
			thirdLevelMap := make(map[string]interface{})
			entityID := strconv.Itoa(int(secondLevelKey))
			thirdLevelMap["Attributes"] = secondLevelValue
			secondLevelMap[entityID] = thirdLevelMap
			if classID == "6" || classID == "256" {
				forthLevelMap := map[string]interface{}(thirdLevelMap["Attributes"].(me.AttributeValueMap))
				delete(forthLevelMap, "SerialNumber")
				forthLevelMap["SerialNumber"] = "%SERIAL_NUMBER%"

			}
			if classID == "134" {
				forthLevelMap := map[string]interface{}(thirdLevelMap["Attributes"].(me.AttributeValueMap))
				delete(forthLevelMap, "MacAddress")
				forthLevelMap["MacAddress"] = "%MAC_ADDRESS%"
			}
		}
		templateMap[classID] = secondLevelMap
	}
	unknownMeAndAttribMap := oo.pOnuDB.CommonMeDb.UnknownMeAndAttribDb
	for unknownMeAndAttribMapKey := range unknownMeAndAttribMap {
		templateMap[string(unknownMeAndAttribMapKey)] = unknownMeAndAttribMap[unknownMeAndAttribMapKey]
	}
	mibTemplate, err := json.Marshal(&templateMap)
	if err != nil {
		logger.Errorw(ctx, "MibSync - MibTemplate - Failed to marshal mibTemplate", log.Fields{"error": err, "device-id": oo.deviceID})
		oo.pOpenOnuAc.SetMibTemplatesGenerated(oo.mibTemplatePath, false)
		return err
	}
	err = oo.mibTemplateKVStore.Put(log.WithSpanFromContext(context.TODO(), ctx), oo.mibTemplatePath, string(mibTemplate))
	if err != nil {
		logger.Errorw(ctx, "MibSync - MibTemplate - Failed to store template in etcd", log.Fields{"error": err, "device-id": oo.deviceID})
		oo.pOpenOnuAc.SetMibTemplatesGenerated(oo.mibTemplatePath, false)
		return err
	}
	logger.Debugw(ctx, "MibSync - MibTemplate - Stored the template to etcd", log.Fields{"device-id": oo.deviceID})
	return nil
}

func (oo *OnuDeviceEntry) requestMdsValue(ctx context.Context) {
	logger.Debugw(ctx, "Request MDS value", log.Fields{"device-id": oo.deviceID})
	requestedAttributes := me.AttributeValueMap{me.OnuData_MibDataSync: ""}
	meInstance, err := oo.PDevOmciCC.SendGetMe(log.WithSpanFromContext(context.TODO(), ctx),
		me.OnuDataClassID, cmn.OnuDataMeID, requestedAttributes, oo.baseDeviceHandler.GetOmciTimeout(), true,
		oo.PMibUploadFsm.CommChan, oo.GetPersIsExtOmciSupported())
	//accept also nil as (error) return value for writing to LastTx
	//  - this avoids misinterpretation of new received OMCI messages
	if err != nil {
		logger.Errorw(ctx, "ONUData get failed, aborting MibSync FSM!", log.Fields{"device-id": oo.deviceID})
		pMibUlFsm := oo.PMibUploadFsm
		if pMibUlFsm != nil {
			//nolint:unparam
			go func(a_pAFsm *cmn.AdapterFsm) {
				_ = oo.PMibUploadFsm.PFsm.Event(UlEvStop)
			}(pMibUlFsm)
		}
		return
	}
	oo.mutexLastTxParamStruct.Lock()
	oo.lastTxParamStruct.lastTxMessageType = omci.GetRequestType
	oo.lastTxParamStruct.pLastTxMeInstance = meInstance
	oo.lastTxParamStruct.repeatCount = 0
	oo.mutexLastTxParamStruct.Unlock()
}

func (oo *OnuDeviceEntry) checkMdsValue(ctx context.Context, mibDataSyncOnu uint8) {
	oo.MutexPersOnuConfig.RLock()
	logger.Debugw(ctx, "MibSync FSM - GetResponse Data for Onu-Data - MibDataSync", log.Fields{"device-id": oo.deviceID,
		"mibDataSyncOnu": mibDataSyncOnu, "PersMibDataSyncAdpt": oo.SOnuPersistentData.PersMibDataSyncAdpt})

	mdsValuesAreEqual := oo.SOnuPersistentData.PersMibDataSyncAdpt == mibDataSyncOnu
	oo.MutexPersOnuConfig.RUnlock()
	if oo.PMibUploadFsm.PFsm.Is(UlStAuditing) {
		if mdsValuesAreEqual {
			logger.Debugw(ctx, "MibSync FSM - mib audit - MDS check ok", log.Fields{"device-id": oo.deviceID})
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvSuccess)
		} else {
			logger.Warnw(ctx, "MibSync FSM - mib audit - MDS check failed for the first time!", log.Fields{"device-id": oo.deviceID})
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvMismatch)
		}
	} else if oo.PMibUploadFsm.PFsm.Is(UlStReAuditing) {
		if mdsValuesAreEqual {
			logger.Debugw(ctx, "MibSync FSM - mib reaudit - MDS check ok", log.Fields{"device-id": oo.deviceID})
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvSuccess)
		} else {
			logger.Errorw(ctx, "MibSync FSM - mib reaudit - MDS check failed for the second time - send ONU device event and reconcile!",
				log.Fields{"device-id": oo.deviceID})
			oo.SendOnuDeviceEvent(ctx, cmn.OnuMibAuditFailureMds, cmn.OnuMibAuditFailureMdsDesc)
			// To reconcile ONU with active adapter later on, we have to retrieve TP instances from parent adapter.
			// In the present use case inconsistencies between TP pathes stored in kv store and TP instances retrieved
			// should not occur. Nevertheless, the respective code is inserted to catch the unlikely case.
			if !oo.getAllStoredTpInstFromParentAdapter(ctx) {
				logger.Debugw(ctx, "MibSync FSM - mib reaudit - inconsistencies between TP pathes stored in kv and parent adapter instances",
					log.Fields{"device-id": oo.deviceID})
				oo.baseDeviceHandler.SetReconcilingReasonUpdate(true)
				go func() {
					if err := oo.baseDeviceHandler.StorePersistentData(ctx); err != nil {
						logger.Warnw(ctx,
							"MibSync FSM - mib reaudit - store persistent data error - continue for now as there will be additional write attempts",
							log.Fields{"device-id": oo.deviceID, "err": err})
					}
				}()
			}
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvMismatch)
		}
	} else if oo.PMibUploadFsm.PFsm.Is(UlStExaminingMds) {
		if mdsValuesAreEqual && mibDataSyncOnu != 0 {
			logger.Debugw(ctx, "MibSync FSM - MDS examination ok", log.Fields{"device-id": oo.deviceID})
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvSuccess)
		} else {
			logger.Debugw(ctx, "MibSync FSM - MDS examination failed - new provisioning", log.Fields{"device-id": oo.deviceID})
			_ = oo.PMibUploadFsm.PFsm.Event(UlEvMismatch)
		}
	} else {
		logger.Warnw(ctx, "wrong state for MDS evaluation!", log.Fields{"state": oo.PMibUploadFsm.PFsm.Current(), "device-id": oo.deviceID})
	}
}

// GetActiveImageMeID returns the Omci MeId of the active ONU image together with error code for validity
func (oo *OnuDeviceEntry) GetActiveImageMeID(ctx context.Context) (uint16, error) {
	oo.mutexOnuSwImageIndications.RLock()
	if oo.onuSwImageIndications.ActiveEntityEntry.Valid {
		value := oo.onuSwImageIndications.ActiveEntityEntry.EntityID
		oo.mutexOnuSwImageIndications.RUnlock()
		return value, nil
	}
	oo.mutexOnuSwImageIndications.RUnlock()
	return 0xFFFF, fmt.Errorf("no valid active image found: %s", oo.deviceID)
}

// GetInactiveImageMeID returns the Omci MeId of the inactive ONU image together with error code for validity
func (oo *OnuDeviceEntry) GetInactiveImageMeID(ctx context.Context) (uint16, error) {
	oo.mutexOnuSwImageIndications.RLock()
	if oo.onuSwImageIndications.InActiveEntityEntry.Valid {
		value := oo.onuSwImageIndications.InActiveEntityEntry.EntityID
		oo.mutexOnuSwImageIndications.RUnlock()
		return value, nil
	}
	oo.mutexOnuSwImageIndications.RUnlock()
	return 0xFFFF, fmt.Errorf("no valid inactive image found: %s", oo.deviceID)
}

// IsImageToBeCommitted returns true if the active image is still uncommitted
func (oo *OnuDeviceEntry) IsImageToBeCommitted(ctx context.Context, aImageID uint16) bool {
	oo.mutexOnuSwImageIndications.RLock()
	if oo.onuSwImageIndications.ActiveEntityEntry.Valid {
		if oo.onuSwImageIndications.ActiveEntityEntry.EntityID == aImageID {
			if oo.onuSwImageIndications.ActiveEntityEntry.IsCommitted == cmn.SwIsUncommitted {
				oo.mutexOnuSwImageIndications.RUnlock()
				return true
			}
		}
	}
	oo.mutexOnuSwImageIndications.RUnlock()
	return false //all other case are treated as 'nothing to commit
}
func (oo *OnuDeviceEntry) getMibFromTemplate(ctx context.Context) bool {

	oo.mibTemplatePath = oo.buildMibTemplatePath()
	logger.Debugw(ctx, "MibSync FSM - get Mib from template", log.Fields{"path": fmt.Sprintf("%s/%s", cBasePathMibTemplateKvStore, oo.mibTemplatePath),
		"device-id": oo.deviceID})

	restoredFromMibTemplate := false
	oo.pOpenOnuAc.LockMutexMibTemplateGenerated()
	defer oo.pOpenOnuAc.UnlockMutexMibTemplateGenerated()
	if meDbValue, ok := oo.pOpenOnuAc.FetchEntryFromMibDatabaseMap(ctx, oo.mibTemplatePath); ok {
		logger.Infow(ctx, "Found MIB common DB Instance , copy  and use", log.Fields{"path": oo.mibTemplatePath, "device-id": oo.deviceID})
		oo.pOnuDB.CommonMeDb = meDbValue
		oo.updateOnuSpecificEntries(ctx)
		restoredFromMibTemplate = true
	} else {
		//Create a common ME MIB Instance as it doesn't prior exists.
		Value, err := oo.mibTemplateKVStore.Get(log.WithSpanFromContext(context.TODO(), ctx), oo.mibTemplatePath)
		//Unmarshal the MIB template and create the entry in the ONU Common Device DB
		if err == nil {
			if Value != nil {
				oo.processMibTemplate(ctx, Value)
				restoredFromMibTemplate = true
			} else {
				logger.Infow(ctx, "No MIB template found", log.Fields{"path": oo.mibTemplatePath, "device-id": oo.deviceID})
			}
		} else {
			logger.Errorw(ctx, "Get from kvstore operation failed for path",
				log.Fields{"path": oo.mibTemplatePath, "device-id": oo.deviceID})
		}

	}
	return restoredFromMibTemplate
}

func (oo *OnuDeviceEntry) getAllStoredTpInstFromParentAdapter(ctx context.Context) bool {

	allTpInstPresent := true
	oo.MutexPersOnuConfig.Lock()
	oo.MutexReconciledTpInstances.Lock()
	for indexUni, uniData := range oo.SOnuPersistentData.PersUniConfig {
		uniID := uniData.PersUniID
		oo.ReconciledTpInstances[uniID] = make(map[uint8]inter_adapter.TechProfileDownloadMessage)
		for tpID, tpPath := range uniData.PersTpPathMap {
			if tpPath != "" {
				logger.Infow(ctx, "Starting retrieval for TechProfileInstance", log.Fields{
					"uniID": uniID, "tpID": tpID, "tpPath": tpPath, "device-id": oo.deviceID,
				})
				// Attempt the initial call before entering the retry loop
				iaTechTpInst, err := oo.baseDeviceHandler.GetTechProfileInstanceFromParentAdapter(ctx, uniID, tpPath)
				if err != nil {
					var ticker *time.Ticker

					// Log the initial failure and proceed to the retry mechanism
					logger.Warnw(ctx, "Initial TechProfileInstance API call failed, entering retry mechanism", log.Fields{
						"tp-id": tpID, "tpPath": tpPath, "uni-id": uniID, "device-id": oo.deviceID, "err": err,
					})
					// Retry logic
					/*
					   We are retrying here because the OLT adapter may not have been up and reachable
					   at the time of the initial attempt. This prevents the ONU adapter from fetching
					   the TechProfile.

					   This issue might occur when both the ONU and OLT adapters are restarted simultaneously,
					   causing a temporary mismatch in their availability. The retry logic ensures that
					   the ONU adapter periodically attempts to fetch the TechProfile, allowing time for the
					   OLT adapter to come up and become operational,and once the OLT adaptor is up with this retry attempts we can fetch the tech profile.This helps prevent the reconciliation
					   process from failing prematurely due to transient unavailability of the OLT adapter.
					*/

					for tpRetryAttempt := initialRetryAttempt; tpRetryAttempt <= maxRetries; tpRetryAttempt++ {
						ticker = time.NewTicker(retryDelay)
						select {
						case _, ok := <-oo.baseDeviceHandler.GetDeviceDeleteCommChan(ctx):
							if !ok {
								logger.Warnw(ctx, "Device deletion channel closed - aborting retry", log.Fields{"device-id": oo.deviceID})
								ticker.Stop()
								return false
							}
						case <-ticker.C:
							iaTechTpInst, err = oo.baseDeviceHandler.GetTechProfileInstanceFromParentAdapter(ctx, uniID, tpPath)
							if err != nil {
								logger.Warnw(ctx, "TechProfileInstance API will be retried", log.Fields{
									"tp-id": tpID, "tpPath": tpPath, "uni-id": uniID,
									"device-id": oo.deviceID, "err": err, "retry": tpRetryAttempt,
									"totalRetries": maxRetries,
								})
								continue
							}

							logger.Info(ctx, "Successfully retrieved TechProfileInstance after retry", log.Fields{
								"retry": tpRetryAttempt, "device-id": oo.deviceID,
							})
						}
						if err == nil {
							break // Exit the retry loop upon success
						}

					}
				}
				if err == nil && iaTechTpInst != nil {
					logger.Debugw(ctx, "reconciling - store Tp instance", log.Fields{"uniID": uniID, "tpID": tpID,
						"*iaTechTpInst": iaTechTpInst, "device-id": oo.deviceID})
					oo.ReconciledTpInstances[uniID][tpID] = *iaTechTpInst
				} else {
					// During the absence of the ONU adapter there seem to have been TP specific configurations!
					// The no longer available TP and the associated flows must be deleted from the ONU KV store
					// and after a MIB reset a new reconciling attempt with OMCI configuration must be started.
					allTpInstPresent = false
					logger.Infow(ctx, "reconciling - can't get tp instance - delete tp and associated flows",
						log.Fields{"tp-id": tpID, "tpPath": tpPath, "uni-id": uniID, "device-id": oo.deviceID, "err": err})
					delete(oo.SOnuPersistentData.PersUniConfig[indexUni].PersTpPathMap, tpID)
					flowSlice := oo.SOnuPersistentData.PersUniConfig[indexUni].PersFlowParams
					for indexFlow, flowData := range flowSlice {
						if flowData.VlanRuleParams.TpID == tpID {
							if len(flowSlice) == 1 {
								flowSlice = []cmn.UniVlanFlowParams{}
							} else {
								flowSlice = append(flowSlice[:indexFlow], flowSlice[indexFlow+1:]...)
							}
							oo.SOnuPersistentData.PersUniConfig[indexUni].PersFlowParams = flowSlice
						}
					}
				}
			}
		}
	}
	oo.MutexReconciledTpInstances.Unlock()
	oo.MutexPersOnuConfig.Unlock()
	return allTpInstPresent
}

// CancelProcessing terminates potentially running reconciling processes and stops the FSM
func (oo *OnuDeviceEntry) CancelProcessing(ctx context.Context) {
	logger.Debugw(ctx, "CancelProcessing entered", log.Fields{"device-id": oo.deviceID})
	if oo.isReconcilingFlows() {
		oo.SendChReconcilingFlowsFinished(ctx, false)
	}
	//the MibSync FSM might be active all the ONU-active time,
	// hence it must be stopped unconditionally
	oo.mutexMibSyncMsgProcessorRunning.RLock()
	defer oo.mutexMibSyncMsgProcessorRunning.RUnlock()
	if oo.mibSyncMsgProcessorRunning {
		pMibUlFsm := oo.PMibUploadFsm
		if pMibUlFsm != nil {
			// abort running message processing
			fsmAbortMsg := cmn.Message{
				Type: cmn.TestMsg,
				Data: cmn.TestMessage{
					TestMessageVal: cmn.AbortMessageProcessing,
				},
			}
			pMibUlFsm.CommChan <- fsmAbortMsg
			_ = pMibUlFsm.PFsm.Event(UlEvStop)
		}
	}
}

// Updates Serial Number and MAC Address in the database
func (oo *OnuDeviceEntry) updateOnuSpecificEntries(ctx context.Context) {
	knownAttributeMEDb := oo.pOnuDB.CommonMeDb.MeDb

	for firstLevelKey, firstLevelValue := range knownAttributeMEDb {
		classID := strconv.Itoa(int(firstLevelKey))

		for secondLevelKey, secondLevelValue := range firstLevelValue {
			switch classID {
			case "6", "256":
				oo.updateAttribute(ctx, "SerialNumber", secondLevelValue, firstLevelKey, secondLevelKey, oo.SOnuPersistentData.PersSerialNumber)
			case "134":
				oo.updateAttribute(ctx, "MacAddress", secondLevelValue, firstLevelKey, secondLevelKey, oo.SOnuPersistentData.PersMacAddress)
			}
		}
	}
}

// Updates a specific attribute in the MIB database
func (oo *OnuDeviceEntry) updateAttribute(ctx context.Context, attributeName string, attributesMap me.AttributeValueMap, classID me.ClassID, entityID uint16, newValue interface{}) {
	if _, exists := attributesMap[attributeName]; exists {
		logger.Debugw(ctx, "Updating "+attributeName, log.Fields{"classID": strconv.Itoa(int(classID)), "oldValue": attributesMap[attributeName], "newValue": newValue})
		attributeCopy := make(me.AttributeValueMap)
		for k, v := range attributesMap {
			attributeCopy[k] = v
		}
		attributeCopy[attributeName] = newValue
		oo.pOnuDB.PutOnuSpeficMe(ctx, classID, entityID, attributeCopy)
	} else {
		logger.Warnw(ctx, attributeName+" key not found", log.Fields{"classID": strconv.Itoa(int(classID))})
	}
}

// Processes the MIB template by replacing tokens and unmarshaling it
func (oo *OnuDeviceEntry) processMibTemplate(ctx context.Context, value *kvstore.KVPair) {
	mibTmpString, _ := kvstore.ToString(value.Value)
	mibTmpString2 := strings.ReplaceAll(mibTmpString, "\x00", "")
	mibTmpBytes := []byte(mibTmpString2)

	logger.Debugf(ctx, "MibSync FSM - Mib template tokens swapped out: %s", mibTmpBytes)

	var firstLevelMap map[string]interface{}
	if err := json.Unmarshal(mibTmpBytes, &firstLevelMap); err != nil {
		logger.Errorw(ctx, "MibSync FSM - Failed to unmarshal template", log.Fields{"error": err, "device-id": oo.deviceID})
		return
	}

	cmnMEDbValue, _ := oo.pOpenOnuAc.CreateEntryAtMibDatabaseMap(ctx, oo.mibTemplatePath)
	oo.populateMibDatabase(ctx, cmnMEDbValue, firstLevelMap)
	cmnMEDbValue.MIBUploadStatus = devdb.Completed
	oo.pOnuDB.CommonMeDb = cmnMEDbValue
}

// Populates the MIB database with parsed data
func (oo *OnuDeviceEntry) populateMibDatabase(ctx context.Context, cmnMEDbValue *devdb.OnuCmnMEDB, firstLevelMap map[string]interface{}) {
	cmnMEDbValue.MeDbLock.Lock()
	defer cmnMEDbValue.MeDbLock.Unlock()

	logger.Debugw(ctx, "Populating MIbDatabase with the template information ", log.Fields{"device-id": oo.deviceID})
	for firstLevelKey, firstLevelValue := range firstLevelMap {
		if uint16ValidNumber, err := strconv.ParseUint(firstLevelKey, 10, 16); err == nil {
			meClassID := me.ClassID(uint16ValidNumber)
			if isSupportedClassID(meClassID) {
				secondLevelMap := firstLevelValue.(map[string]interface{})
				for secondLevelKey, secondLevelValue := range secondLevelMap {
					if uint16ValidNumber, err := strconv.ParseUint(secondLevelKey, 10, 16); err == nil {
						meEntityID := uint16(uint16ValidNumber)
						thirdLevelMap := secondLevelValue.(map[string]interface{})
						if attributes, exists := thirdLevelMap["Attributes"].(map[string]interface{}); exists {
							if _, found := cmnMEDbValue.MeDb[meClassID]; !found {
								cmnMEDbValue.MeDb[meClassID] = make(map[uint16]me.AttributeValueMap)
							}
							if _, found := cmnMEDbValue.MeDb[meClassID][meEntityID]; !found {
								cmnMEDbValue.MeDb[meClassID][meEntityID] = make(me.AttributeValueMap)
							}
							for attrKey, attrValue := range attributes {
								cmnMEDbValue.MeDb[meClassID][meEntityID][attrKey] = attrValue
							}
						}

					}
				}
			}
		}
	}
}
