package models

import (
	"github.com/analogj/scrutiny/webapp/backend/pkg"
	"github.com/analogj/scrutiny/webapp/backend/pkg/models/collector"
	"time"
)

type DeviceWrapper struct {
	Success bool     `json:"success"`
	Errors  []error  `json:"errors"`
	Data    []Device `json:"data"`
}

type Device struct {
	//GORM attributes, see: http://gorm.io/docs/conventions.html
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt *time.Time

	WWN string `json:"wwn" gorm:"primary_key"`

	DeviceName     string `json:"device_name"`
	DeviceUUID     string `json:"device_uuid"`
	DeviceSerialID string `json:"device_serial_id"`
	DeviceLabel    string `json:"device_label"`

	Manufacturer   string `json:"manufacturer"`
	ModelName      string `json:"model_name"`
	InterfaceType  string `json:"interface_type"`
	InterfaceSpeed string `json:"interface_speed"`
	SerialNumber   string `json:"serial_number"`
	Firmware       string `json:"firmware"`
	RotationSpeed  int    `json:"rotational_speed"`
	Capacity       int64  `json:"capacity"`
	FormFactor     string `json:"form_factor"`
	SmartSupport   bool   `json:"smart_support"`
	DeviceProtocol string `json:"device_protocol"` //protocol determines which smart attribute types are available (ATA, NVMe, SCSI)
	DeviceType     string `json:"device_type"`     //device type is used for querying with -d/t flag, should only be used by collector.

	// User provided metadata
	Label  string `json:"label"`
	HostId string `json:"host_id"`

	// Data set by Scrutiny
	DeviceStatus pkg.DeviceStatus `json:"device_status"`
}

func (dv *Device) IsAta() bool {
	return dv.DeviceProtocol == pkg.DeviceProtocolAta
}

func (dv *Device) IsScsi() bool {
	return dv.DeviceProtocol == pkg.DeviceProtocolScsi
}

func (dv *Device) IsNvme() bool {
	return dv.DeviceProtocol == pkg.DeviceProtocolNvme
}

//
////This method requires a device with an array of SmartResults.
////It will remove all SmartResults other than the first (the latest one)
////All removed SmartResults, will be processed, grouping SmartAtaAttribute by attribute_id
//// and adding theme to an array called History.
//func (dv *Device) SquashHistory() error {
//	if len(dv.SmartResults) <= 1 {
//		return nil //no ataHistory found. ignore
//	}
//
//	latestSmartResultSlice := dv.SmartResults[0:1]
//	historicalSmartResultSlice := dv.SmartResults[1:]
//
//	//re-assign the latest slice to the SmartResults field
//	dv.SmartResults = latestSmartResultSlice
//
//	//process the historical slice for ATA data
//	if len(dv.SmartResults[0].AtaAttributes) > 0 {
//		ataHistory := map[int][]SmartAtaAttribute{}
//		for _, smartResult := range historicalSmartResultSlice {
//			for _, smartAttribute := range smartResult.AtaAttributes {
//				if _, ok := ataHistory[smartAttribute.AttributeId]; !ok {
//					ataHistory[smartAttribute.AttributeId] = []SmartAtaAttribute{}
//				}
//				ataHistory[smartAttribute.AttributeId] = append(ataHistory[smartAttribute.AttributeId], smartAttribute)
//			}
//		}
//
//		//now assign the historical slices to the AtaAttributes in the latest SmartResults
//		for sandx, smartAttribute := range dv.SmartResults[0].AtaAttributes {
//			if attributeHistory, ok := ataHistory[smartAttribute.AttributeId]; ok {
//				dv.SmartResults[0].AtaAttributes[sandx].History = attributeHistory
//			}
//		}
//	}
//
//	//process the historical slice for Nvme data
//	if len(dv.SmartResults[0].NvmeAttributes) > 0 {
//		nvmeHistory := map[string][]SmartNvmeAttribute{}
//		for _, smartResult := range historicalSmartResultSlice {
//			for _, smartAttribute := range smartResult.NvmeAttributes {
//				if _, ok := nvmeHistory[smartAttribute.AttributeId]; !ok {
//					nvmeHistory[smartAttribute.AttributeId] = []SmartNvmeAttribute{}
//				}
//				nvmeHistory[smartAttribute.AttributeId] = append(nvmeHistory[smartAttribute.AttributeId], smartAttribute)
//			}
//		}
//
//		//now assign the historical slices to the AtaAttributes in the latest SmartResults
//		for sandx, smartAttribute := range dv.SmartResults[0].NvmeAttributes {
//			if attributeHistory, ok := nvmeHistory[smartAttribute.AttributeId]; ok {
//				dv.SmartResults[0].NvmeAttributes[sandx].History = attributeHistory
//			}
//		}
//	}
//	//process the historical slice for Scsi data
//	if len(dv.SmartResults[0].ScsiAttributes) > 0 {
//		scsiHistory := map[string][]SmartScsiAttribute{}
//		for _, smartResult := range historicalSmartResultSlice {
//			for _, smartAttribute := range smartResult.ScsiAttributes {
//				if _, ok := scsiHistory[smartAttribute.AttributeId]; !ok {
//					scsiHistory[smartAttribute.AttributeId] = []SmartScsiAttribute{}
//				}
//				scsiHistory[smartAttribute.AttributeId] = append(scsiHistory[smartAttribute.AttributeId], smartAttribute)
//			}
//		}
//
//		//now assign the historical slices to the AtaAttributes in the latest SmartResults
//		for sandx, smartAttribute := range dv.SmartResults[0].ScsiAttributes {
//			if attributeHistory, ok := scsiHistory[smartAttribute.AttributeId]; ok {
//				dv.SmartResults[0].ScsiAttributes[sandx].History = attributeHistory
//			}
//		}
//	}
//	return nil
//}
//
//func (dv *Device) ApplyMetadataRules() error {
//
//	//embed metadata in the latest smart attributes object
//	if len(dv.SmartResults) > 0 {
//		for ndx, attr := range dv.SmartResults[0].AtaAttributes {
//			attr.PopulateAttributeStatus()
//			dv.SmartResults[0].AtaAttributes[ndx] = attr
//		}
//
//		for ndx, attr := range dv.SmartResults[0].NvmeAttributes {
//			attr.PopulateAttributeStatus()
//			dv.SmartResults[0].NvmeAttributes[ndx] = attr
//
//		}
//
//		for ndx, attr := range dv.SmartResults[0].ScsiAttributes {
//			attr.PopulateAttributeStatus()
//			dv.SmartResults[0].ScsiAttributes[ndx] = attr
//
//		}
//	}
//	return nil
//}

// This function is called every time the collector sends SMART data to the API.
// It can be used to update device data that can change over time.
func (dv *Device) UpdateFromCollectorSmartInfo(info collector.SmartInfo) error {
	dv.Firmware = info.FirmwareVersion
	dv.DeviceProtocol = info.Device.Protocol

	if !info.SmartStatus.Passed {
		dv.DeviceStatus = pkg.DeviceStatusSet(dv.DeviceStatus, pkg.DeviceStatusFailedSmart)
	}

	return nil
}
