package pricing_test

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"

	"cloud.o-forge.io/core/oc-lib/models/common/pricing"
)

type DummyStrategy int

func (d DummyStrategy) GetStrategy() string   { return "DUMMY" }
func (d DummyStrategy) GetStrategyValue() int { return int(d) }

func TestBuyingStrategy_String(t *testing.T) {
	assert.Equal(t, "PERMANENT", pricing.PERMANENT.String())
	assert.Equal(t, "UNDEFINED_SUBSCRIPTION", pricing.UNDEFINED_SUBSCRIPTION.String())
	assert.Equal(t, "SUBSCRIPTION", pricing.SUBSCRIPTION.String())
}

func TestBuyingStrategyList(t *testing.T) {
	list := pricing.BuyingStrategyList()
	assert.Equal(t, 3, len(list))
	assert.Contains(t, list, pricing.SUBSCRIPTION)
}

func TestTimePricingStrategy_String(t *testing.T) {
	assert.Equal(t, "ONCE", pricing.ONCE.String())
	assert.Equal(t, "PER SECOND", pricing.PER_SECOND.String())
	assert.Equal(t, "PER MONTH", pricing.PER_MONTH.String())
}

func TestTimePricingStrategyList(t *testing.T) {
	list := pricing.TimePricingStrategyList()
	assert.Equal(t, 7, len(list))
	assert.Contains(t, list, pricing.PER_DAY)
}

func TestTimePricingStrategy_Methods(t *testing.T) {
	ts := pricing.PER_MINUTE
	assert.Equal(t, "PER_MINUTE", ts.GetStrategy())
	assert.Equal(t, 2, ts.GetStrategyValue())
}

func Test_getAverageTimeInSecond_WithEnd(t *testing.T) {
	start := time.Now()
	end := start.Add(30 * time.Minute)

	_, err := pricing.BookingEstimation(pricing.PER_MINUTE, 2.0, 1200, start, &end)
	assert.NoError(t, err)
}

func Test_getAverageTimeInSecond_WithoutEnd(t *testing.T) {
	start := time.Now()

	// getAverageTimeInSecond is tested via BookingEstimation
	price, err := pricing.BookingEstimation(pricing.PER_HOUR, 10.0, 100, start, nil)
	assert.NoError(t, err)
	assert.True(t, price > 0)
}

func TestBookingEstimation(t *testing.T) {
	start := time.Now()
	end := start.Add(10 * time.Minute)
	strategies := map[pricing.TimePricingStrategy]float64{
		pricing.ONCE:       50,
		pricing.PER_HOUR:   10,
		pricing.PER_MINUTE: 1,
		pricing.PER_SECOND: 0.1,
		pricing.PER_DAY:    100,
		pricing.PER_WEEK:   500,
		pricing.PER_MONTH:  2000,
	}

	for strategy, price := range strategies {
		t.Run(strategy.String(), func(t *testing.T) {
			cost, err := pricing.BookingEstimation(strategy, price, 3600, start, &end)
			assert.NoError(t, err)
			assert.True(t, cost >= 0)
		})
	}

	_, err := pricing.BookingEstimation(999, 10, 3600, start, &end)
	assert.Error(t, err)
}

func TestPricingStrategy_Getters(t *testing.T) {
	ps := pricing.PricingStrategy[DummyStrategy]{
		Price:               20,
		Currency:            "USD",
		BuyingStrategy:      pricing.SUBSCRIPTION,
		TimePricingStrategy: pricing.PER_MINUTE,
		OverrideStrategy:    DummyStrategy(1),
	}

	assert.Equal(t, pricing.SUBSCRIPTION, ps.GetBuyingStrategy())
	assert.Equal(t, pricing.PER_MINUTE, ps.GetTimePricingStrategy())
	assert.Equal(t, DummyStrategy(1), ps.GetOverrideStrategy())
}

func TestPricingStrategy_GetPriceHT(t *testing.T) {
	start := time.Now()
	end := start.Add(5 * time.Minute)

	// SUBSCRIPTION case
	ps := pricing.PricingStrategy[DummyStrategy]{
		Price:               5,
		BuyingStrategy:      pricing.SUBSCRIPTION,
		TimePricingStrategy: pricing.PER_HOUR,
	}

	p, err := ps.GetPriceHT(2, 3600, start, &end, nil)
	assert.NoError(t, err)
	assert.True(t, p > 0)

	// UNLIMITED case
	ps.BuyingStrategy = pricing.PERMANENT
	p, err = ps.GetPriceHT(10, 0, start, &end, nil)
	assert.NoError(t, err)
	assert.Equal(t, 5.0, p)

	// UNDEFINED_SUBSCRIPTION case: price * quantity
	ps.BuyingStrategy = pricing.UNDEFINED_SUBSCRIPTION
	p, err = ps.GetPriceHT(3, 0, start, &end, nil)
	assert.NoError(t, err)
	assert.Equal(t, 15.0, p)
}
