// Copyright IBM Corp. 2016, 2025
// SPDX-License-Identifier: MPL-2.0

package billing

import (
	"context"
	"fmt"
	"sync"
	"sync/atomic"
	"time"

	log "github.com/hashicorp/go-hclog"
	"github.com/hashicorp/vault/sdk/logical"
)

const (
	BillingSubPath                          = "billing/"
	ReplicatedPrefix                        = "replicated/"
	RoleHWMCountsHWM                        = "maxRoleCounts/"
	KvHWMCountsHWM                          = "maxKvCounts/"
	TransitDataProtectionCallCountsPrefix   = "transitDataProtectionCallCounts/"
	TransformDataProtectionCallCountsPrefix = "transformDataProtectionCallCounts/"
	LocalPrefix                             = "local/"
	ThirdPartyPluginsPrefix                 = "thirdPartyPluginCounts/"
	KmipEnabledPrefix                       = "kmipEnabled/"

	BillingWriteInterval = 10 * time.Minute
)

var BillingMonthStorageFormat = "%s%d/%02d/%s" // e.g replicated/2026/01/maxKvCounts/

type ConsumptionBilling struct {
	// BillingStorageLock controls access to the billing storage paths
	BillingStorageLock sync.RWMutex

	BillingConfig            BillingConfig
	DataProtectionCallCounts DataProtectionCallCounts
	Logger                   log.Logger

	// KmipSeenEnabledThisMonth tracks whether KMIP has been enabled during the current billing month.
	// This is used to avoid scanning all mounts every 10 minutes for KMIP billing detection.
	KmipSeenEnabledThisMonth atomic.Bool
}

type BillingConfig struct {
	// For testing purposes. The cadence at which billing metrics are updated
	MetricsUpdateCadence time.Duration
}

func GetMonthlyBillingPath(localPrefix string, now time.Time, billingMetric string) string {
	// Normalize to avoid double slashes since our prefixes include trailing "/".
	// Example: localPrefix="replicated/", billingMetric="maxKvCounts/" =>
	// "replicated/2026/01/maxKvCounts/"
	year := now.Year()
	month := int(now.Month())
	return fmt.Sprintf(BillingMonthStorageFormat, localPrefix, year, month, billingMetric)
}

type DataProtectionCallCounts struct {
	Transit   *atomic.Uint64 `json:"transit,omitempty"`
	Transform *atomic.Uint64 `json:"transform,omitempty"`
}

var _ logical.ConsumptionBillingManager = (*ConsumptionBilling)(nil)

func (s *ConsumptionBilling) WriteBillingData(ctx context.Context, mountType string, data map[string]interface{}) error {
	switch mountType {
	case "transit":
		val, ok := data["count"].(uint64)
		if !ok {
			err := fmt.Errorf("invalid value type for transit")
			return err
		}

		s.DataProtectionCallCounts.Transit.Add(val)
	case "transform":
		val, ok := data["count"].(uint64)
		if !ok {
			err := fmt.Errorf("invalid value type for transform")
			return err
		}

		s.DataProtectionCallCounts.Transform.Add(val)
	default:
		err := fmt.Errorf("unknown metric type: %s", mountType)
		return err
	}
	return nil
}
