package db

import (
	"github.com/analogj/scrutiny/webapp/backend/pkg/metadata"
	"gorm.io/gorm"
	"strings"
)

const SmartAttributeStatusPassed = "passed"
const SmartAttributeStatusFailed = "failed"
const SmartAttributeStatusWarning = "warn"

type SmartAtaAttribute struct {
	gorm.Model

	SmartId int    `json:"smart_id"`
	Smart   Device `json:"-" gorm:"foreignkey:SmartId"` // use SmartId as foreign key

	AttributeId int    `json:"attribute_id"`
	Name        string `json:"name"`
	Value       int    `json:"value"`
	Worst       int    `json:"worst"`
	Threshold   int    `json:"thresh"`
	RawValue    int64  `json:"raw_value"`
	RawString   string `json:"raw_string"`
	WhenFailed  string `json:"when_failed"`

	TransformedValue int64               `json:"transformed_value"`
	Status           string              `gorm:"-" json:"status,omitempty"`
	StatusReason     string              `gorm:"-" json:"status_reason,omitempty"`
	FailureRate      float64             `gorm:"-" json:"failure_rate,omitempty"`
	History          []SmartAtaAttribute `gorm:"-" json:"history,omitempty"`
}

//populate attribute status, using SMART Thresholds & Observed Metadata
func (sa *SmartAtaAttribute) PopulateAttributeStatus() {
	if strings.ToUpper(sa.WhenFailed) == SmartWhenFailedFailingNow {
		//this attribute has previously failed
		sa.Status = SmartAttributeStatusFailed
		sa.StatusReason = "Attribute is failing manufacturer SMART threshold"

	} else if strings.ToUpper(sa.WhenFailed) == SmartWhenFailedInThePast {
		sa.Status = SmartAttributeStatusWarning
		sa.StatusReason = "Attribute has previously failed manufacturer SMART threshold"
	}

	if smartMetadata, ok := metadata.AtaMetadata[sa.AttributeId]; ok {
		sa.MetadataObservedThresholdStatus(smartMetadata)
	}

	//check if status is blank, set to "passed"
	if len(sa.Status) == 0 {
		sa.Status = SmartAttributeStatusPassed
	}
}

// compare the attribute (raw, normalized, transformed) value to observed thresholds, and update status if necessary
func (sa *SmartAtaAttribute) MetadataObservedThresholdStatus(smartMetadata metadata.AtaAttributeMetadata) {
	//TODO: multiple rules
	// try to predict the failure rates for observed thresholds that have 0 failure rate and error bars.
	// - if the attribute is critical
	//		- the failure rate is over 10 - set to failed
	//		- the attribute does not match any threshold, set to warn
	// - if the attribute is not critical
	//		- if failure rate is above 20 - set to failed
	// 		- if failure rate is above 10 but below 20 - set to warn

	//update the smart attribute status based on Observed thresholds.
	var value int64
	if smartMetadata.DisplayType == metadata.AtaSmartAttributeDisplayTypeNormalized {
		value = int64(sa.Value)
	} else if smartMetadata.DisplayType == metadata.AtaSmartAttributeDisplayTypeTransformed {
		value = sa.TransformedValue
	} else {
		value = sa.RawValue
	}

	for _, obsThresh := range smartMetadata.ObservedThresholds {

		//check if "value" is in this bucket
		if ((obsThresh.Low == obsThresh.High) && value == obsThresh.Low) ||
			(obsThresh.Low < value && value <= obsThresh.High) {
			sa.FailureRate = obsThresh.AnnualFailureRate

			if smartMetadata.Critical {
				if obsThresh.AnnualFailureRate >= 0.10 {
					sa.Status = SmartAttributeStatusFailed
					sa.StatusReason = "Observed Failure Rate for Critical Attribute is greater than 10%"
				}
			} else {
				if obsThresh.AnnualFailureRate >= 0.20 {
					sa.Status = SmartAttributeStatusFailed
					sa.StatusReason = "Observed Failure Rate for Attribute is greater than 20%"
				} else if obsThresh.AnnualFailureRate >= 0.10 {
					sa.Status = SmartAttributeStatusWarning
					sa.StatusReason = "Observed Failure Rate for Attribute is greater than 10%"
				}
			}

			//we've found the correct bucket, we can drop out of this loop
			return
		}
	}
	// no bucket found
	if smartMetadata.Critical {
		sa.Status = SmartAttributeStatusWarning
		sa.StatusReason = "Could not determine Observed Failure Rate for Critical Attribute"
	}

	return
}
