package lifecyclecompare

import (
	"fmt"

	"cloud.google.com/go/storage"
	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
	s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
)

type awsLifecycleRuleSpec struct {
	id               string
	prefix           string
	status           string
	expirationDays   int32
	coldDays         int32
	coldStorageClass string
}

func awsLifecycleRuleSpecFromS3(rule s3types.LifecycleRule) awsLifecycleRuleSpec {
	spec := awsLifecycleRuleSpec{
		status: string(rule.Status),
	}
	if rule.ID != nil {
		spec.id = *rule.ID
	}
	if rule.Filter != nil && rule.Filter.Prefix != nil {
		spec.prefix = *rule.Filter.Prefix
	}
	if rule.Expiration != nil && rule.Expiration.Days != nil {
		spec.expirationDays = *rule.Expiration.Days
	}
	if len(rule.Transitions) > 0 {
		if rule.Transitions[0].Days != nil {
			spec.coldDays = *rule.Transitions[0].Days
		}
		spec.coldStorageClass = string(rule.Transitions[0].StorageClass)
	}
	return spec
}

func AWSLifecycleRulesEqual(a, b []s3types.LifecycleRule) bool {
	if len(a) != len(b) {
		return false
	}
	rulesB := make(map[string]awsLifecycleRuleSpec, len(b))
	for _, rule := range b {
		spec := awsLifecycleRuleSpecFromS3(rule)
		rulesB[spec.id] = spec
	}
	for _, rule := range a {
		spec := awsLifecycleRuleSpecFromS3(rule)
		other, ok := rulesB[spec.id]
		if !ok || spec != other {
			return false
		}
	}
	return true
}

type ossLifecycleRuleSpec struct {
	id               string
	prefix           string
	status           string
	expirationDays   int32
	coldDays         int32
	coldStorageClass string
}

func ossLifecycleRuleSpecFromRule(rule oss.LifecycleRule) ossLifecycleRuleSpec {
	spec := ossLifecycleRuleSpec{}
	if rule.ID != nil {
		spec.id = *rule.ID
	}
	if rule.Prefix != nil {
		spec.prefix = *rule.Prefix
	}
	if rule.Status != nil {
		spec.status = *rule.Status
	}
	if rule.Expiration != nil && rule.Expiration.Days != nil {
		spec.expirationDays = *rule.Expiration.Days
	}
	if len(rule.Transitions) > 0 {
		if rule.Transitions[0].Days != nil {
			spec.coldDays = *rule.Transitions[0].Days
		}
		spec.coldStorageClass = string(rule.Transitions[0].StorageClass)
	}
	return spec
}

func OSSLifecycleRulesEqual(a, b []oss.LifecycleRule) bool {
	if len(a) != len(b) {
		return false
	}
	rulesB := make(map[string]ossLifecycleRuleSpec, len(b))
	for _, rule := range b {
		spec := ossLifecycleRuleSpecFromRule(rule)
		rulesB[spec.id] = spec
	}
	for _, rule := range a {
		spec := ossLifecycleRuleSpecFromRule(rule)
		other, ok := rulesB[spec.id]
		if !ok || spec != other {
			return false
		}
	}
	return true
}

func gcsLifecycleRuleKey(rule storage.LifecycleRule) string {
	prefix := ""
	if len(rule.Condition.MatchesPrefix) > 0 {
		prefix = rule.Condition.MatchesPrefix[0]
	}
	if rule.Action.Type == storage.SetStorageClassAction {
		return fmt.Sprintf("%s_%s_%s", prefix, rule.Action.Type, rule.Action.StorageClass)
	}
	return fmt.Sprintf("%s_%s", prefix, rule.Action.Type)
}

func GCSLifecycleRulesEqual(a, b []storage.LifecycleRule) bool {
	if len(a) != len(b) {
		return false
	}
	rulesB := make(map[string]storage.LifecycleRule, len(b))
	for _, rule := range b {
		rulesB[gcsLifecycleRuleKey(rule)] = rule
	}
	for _, rule := range a {
		other, ok := rulesB[gcsLifecycleRuleKey(rule)]
		if !ok {
			return false
		}
		if rule.Action.Type != other.Action.Type ||
			rule.Action.StorageClass != other.Action.StorageClass ||
			rule.Condition.AgeInDays != other.Condition.AgeInDays ||
			len(rule.Condition.MatchesPrefix) != len(other.Condition.MatchesPrefix) {
			return false
		}
		for i := range rule.Condition.MatchesPrefix {
			if rule.Condition.MatchesPrefix[i] != other.Condition.MatchesPrefix[i] {
				return false
			}
		}
	}
	return true
}

type azureLifecycleRuleSpec struct {
	name       string
	enabled    bool
	prefix     string
	deleteDays float32
	coolDays   float32
}

func azureLifecycleRuleSpecFromRule(rule *armstorage.ManagementPolicyRule) azureLifecycleRuleSpec {
	spec := azureLifecycleRuleSpec{}
	if rule.Name != nil {
		spec.name = *rule.Name
	}
	if rule.Enabled != nil {
		spec.enabled = *rule.Enabled
	}
	if rule.Definition != nil && rule.Definition.Filters != nil && len(rule.Definition.Filters.PrefixMatch) > 0 && rule.Definition.Filters.PrefixMatch[0] != nil {
		spec.prefix = *rule.Definition.Filters.PrefixMatch[0]
	}
	if rule.Definition != nil && rule.Definition.Actions != nil && rule.Definition.Actions.BaseBlob != nil {
		if rule.Definition.Actions.BaseBlob.Delete != nil && rule.Definition.Actions.BaseBlob.Delete.DaysAfterModificationGreaterThan != nil {
			spec.deleteDays = *rule.Definition.Actions.BaseBlob.Delete.DaysAfterModificationGreaterThan
		}
		if rule.Definition.Actions.BaseBlob.TierToCool != nil && rule.Definition.Actions.BaseBlob.TierToCool.DaysAfterModificationGreaterThan != nil {
			spec.coolDays = *rule.Definition.Actions.BaseBlob.TierToCool.DaysAfterModificationGreaterThan
		}
	}
	return spec
}

func AzureLifecycleRulesEqual(a, b []*armstorage.ManagementPolicyRule) bool {
	if len(a) != len(b) {
		return false
	}
	rulesB := make(map[string]azureLifecycleRuleSpec, len(b))
	for _, rule := range b {
		spec := azureLifecycleRuleSpecFromRule(rule)
		rulesB[spec.name] = spec
	}
	for _, rule := range a {
		spec := azureLifecycleRuleSpecFromRule(rule)
		other, ok := rulesB[spec.name]
		if !ok || spec != other {
			return false
		}
	}
	return true
}
