package keeper

import (
	"time"

	errorsmod "cosmossdk.io/errors"
	sdk "github.com/cosmos/cosmos-sdk/types"

	"github.com/0glabs/0g-chain/x/issuance/types"
)

// CreateNewAssetSupply creates a new AssetSupply in the store for the input denom
func (k Keeper) CreateNewAssetSupply(ctx sdk.Context, denom string) types.AssetSupply {
	supply := types.NewAssetSupply(
		sdk.NewCoin(denom, sdk.ZeroInt()), time.Duration(0))
	k.SetAssetSupply(ctx, supply, denom)
	return supply
}

// IncrementCurrentAssetSupply increments an asset's supply by the coin
func (k Keeper) IncrementCurrentAssetSupply(ctx sdk.Context, coin sdk.Coin) error {
	supply, found := k.GetAssetSupply(ctx, coin.Denom)
	if !found {
		return errorsmod.Wrap(types.ErrAssetNotFound, coin.Denom)
	}

	limit, err := k.GetRateLimit(ctx, coin.Denom)
	if err != nil {
		return err
	}

	if limit.Active {
		supplyLimit := sdk.NewCoin(coin.Denom, limit.Limit)
		// Resulting current supply must be under asset's limit
		if supplyLimit.IsLT(supply.CurrentSupply.Add(coin)) {
			return errorsmod.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, supply.CurrentSupply, supplyLimit)
		}
		supply.CurrentSupply = supply.CurrentSupply.Add(coin)
		k.SetAssetSupply(ctx, supply, coin.Denom)
	}
	return nil
}

// UpdateTimeBasedSupplyLimits updates the time based supply for each asset, resetting it if the current time window has elapsed.
func (k Keeper) UpdateTimeBasedSupplyLimits(ctx sdk.Context) {
	params := k.GetParams(ctx)
	previousBlockTime, found := k.GetPreviousBlockTime(ctx)
	if !found {
		previousBlockTime = ctx.BlockTime()
		k.SetPreviousBlockTime(ctx, previousBlockTime)
	}
	timeElapsed := ctx.BlockTime().Sub(previousBlockTime)
	for _, asset := range params.Assets {
		supply, found := k.GetAssetSupply(ctx, asset.Denom)
		// if a new asset has been added by governance, create a new asset supply for it in the store
		if !found {
			supply = k.CreateNewAssetSupply(ctx, asset.Denom)
		}
		if !asset.RateLimit.Active {
			// rate limiting is not active, reset supply
			supply.CurrentSupply = sdk.NewCoin(asset.Denom, sdk.ZeroInt())
			supply.TimeElapsed = time.Duration(0)
			k.SetAssetSupply(ctx, supply, asset.Denom)
			continue
		}
		if asset.RateLimit.TimePeriod > supply.TimeElapsed+timeElapsed {
			// rate limiting is active, the rate-limiting period has not expired
			supply.TimeElapsed = supply.TimeElapsed + timeElapsed
			k.SetAssetSupply(ctx, supply, asset.Denom)
			continue
		}
		// rate limiting is active, the rate-limiting period has expired, and is now reset
		supply.TimeElapsed = time.Duration(0)
		supply.CurrentSupply = sdk.NewCoin(asset.Denom, sdk.ZeroInt())
		k.SetAssetSupply(ctx, supply, asset.Denom)
	}
	k.SetPreviousBlockTime(ctx, ctx.BlockTime())
}