package operator

import (
	"fmt"
	"time"

	"github.com/go-logr/logr"
	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
	"github.com/kyverno/kyverno/pkg/engine/context"
)

// NewDurationOperatorHandler returns handler to manage the provided duration operations (>, >=, <=, <)
func NewDurationOperatorHandler(log logr.Logger, ctx context.EvalInterface, op kyvernov1.ConditionOperator) OperatorHandler {
	return DurationOperatorHandler{
		ctx:       ctx,
		log:       log,
		condition: op,
	}
}

// DurationOperatorHandler provides implementation to handle Duration Operations associated with policies
type DurationOperatorHandler struct {
	ctx       context.EvalInterface
	log       logr.Logger
	condition kyvernov1.ConditionOperator
}

// durationCompareByCondition compares a time.Duration key with a time.Duration value on the basis of the provided operator
func durationCompareByCondition(key time.Duration, value time.Duration, op kyvernov1.ConditionOperator, log logr.Logger) bool {
	switch op {
	case kyvernov1.ConditionOperators["DurationGreaterThanOrEquals"]:
		return key >= value
	case kyvernov1.ConditionOperators["DurationGreaterThan"]:
		return key > value
	case kyvernov1.ConditionOperators["DurationLessThanOrEquals"]:
		return key <= value
	case kyvernov1.ConditionOperators["DurationLessThan"]:
		return key < value
	default:
		log.V(2).Info(fmt.Sprintf("Expected operator, one of [DurationGreaterThanOrEquals, DurationGreaterThan, DurationLessThanOrEquals, DurationLessThan], found %s", op))
		return false
	}
}

func (doh DurationOperatorHandler) Evaluate(key, value interface{}) bool {
	switch typedKey := key.(type) {
	case int:
		return doh.validateValueWithIntPattern(int64(typedKey), value)
	case int64:
		return doh.validateValueWithIntPattern(typedKey, value)
	case float64:
		return doh.validateValueWithFloatPattern(typedKey, value)
	case string:
		return doh.validateValueWithStringPattern(typedKey, value)
	default:
		doh.log.V(2).Info("Unsupported type", "value", typedKey, "type", fmt.Sprintf("%T", typedKey))
		return false
	}
}

func (doh DurationOperatorHandler) validateValueWithIntPattern(key int64, value interface{}) bool {
	switch typedValue := value.(type) {
	case int:
		return durationCompareByCondition(time.Duration(key)*time.Second, time.Duration(typedValue)*time.Second, doh.condition, doh.log)
	case int64:
		return durationCompareByCondition(time.Duration(key)*time.Second, time.Duration(typedValue)*time.Second, doh.condition, doh.log)
	case float64:
		return durationCompareByCondition(time.Duration(key)*time.Second, time.Duration(typedValue)*time.Second, doh.condition, doh.log)
	case string:
		duration, err := time.ParseDuration(typedValue)
		if err == nil {
			return durationCompareByCondition(time.Duration(key)*time.Second, duration, doh.condition, doh.log)
		}
		doh.log.Error(fmt.Errorf("parse error: "), "Failed to parse time duration from the string value")
		return false
	default:
		doh.log.V(2).Info("Unexpected type", "value", value, "type", fmt.Sprintf("%T", value))
		return false
	}
}

func (doh DurationOperatorHandler) validateValueWithFloatPattern(key float64, value interface{}) bool {
	switch typedValue := value.(type) {
	case int:
		return durationCompareByCondition(time.Duration(key)*time.Second, time.Duration(typedValue)*time.Second, doh.condition, doh.log)
	case int64:
		return durationCompareByCondition(time.Duration(key)*time.Second, time.Duration(typedValue)*time.Second, doh.condition, doh.log)
	case float64:
		return durationCompareByCondition(time.Duration(key)*time.Second, time.Duration(typedValue)*time.Second, doh.condition, doh.log)
	case string:
		duration, err := time.ParseDuration(typedValue)
		if err == nil {
			return durationCompareByCondition(time.Duration(key)*time.Second, duration, doh.condition, doh.log)
		}
		doh.log.Error(fmt.Errorf("parse error: "), "Failed to parse time duration from the string value")
		return false
	default:
		doh.log.V(2).Info("Unexpected type", "value", value, "type", fmt.Sprintf("%T", value))
		return false
	}
}

func (doh DurationOperatorHandler) validateValueWithStringPattern(key string, value interface{}) bool {
	duration, err := time.ParseDuration(key)
	if err != nil {
		doh.log.Error(err, "Failed to parse time duration from the string key")
		return false
	}
	switch typedValue := value.(type) {
	case int:
		return durationCompareByCondition(duration, time.Duration(typedValue)*time.Second, doh.condition, doh.log)
	case int64:
		return durationCompareByCondition(duration, time.Duration(typedValue)*time.Second, doh.condition, doh.log)
	case float64:
		return durationCompareByCondition(duration, time.Duration(typedValue)*time.Second, doh.condition, doh.log)
	case string:
		durationValue, err := time.ParseDuration(typedValue)
		if err == nil {
			return durationCompareByCondition(duration, durationValue, doh.condition, doh.log)
		}
		doh.log.Error(fmt.Errorf("parse error: "), "Failed to parse time duration from the string value")
		return false
	default:
		doh.log.V(2).Info("Unexpected type", "value", value, "type", fmt.Sprintf("%T", value))
		return false
	}
}

// the following functions are unreachable because the key is strictly supposed to be a duration
// still the following functions are just created to make DurationOperatorHandler struct implement OperatorHandler interface
func (doh DurationOperatorHandler) validateValueWithBoolPattern(key bool, value interface{}) bool {
	return false
}

func (doh DurationOperatorHandler) validateValueWithMapPattern(key map[string]interface{}, value interface{}) bool {
	return false
}

func (doh DurationOperatorHandler) validateValueWithSlicePattern(key []interface{}, value interface{}) bool {
	return false
}
