package resources_test

import (
	"testing"
	"time"

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

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestComputeResource_GetType(t *testing.T) {
	r := &resources.ComputeResource{}
	assert.Equal(t, tools.COMPUTE_RESOURCE.String(), r.GetType())
}

func TestComputeResource_GetAccessor(t *testing.T) {
	req := &tools.APIRequest{}
	cr := &resources.ComputeResource{}
	accessor := cr.GetAccessor(req)
	assert.NotNil(t, accessor)
}

func TestComputeResource_ConvertToPricedResource(t *testing.T) {
	req := &tools.APIRequest{}
	cr := &resources.ComputeResource{}
	cr.UUID = "comp123"
	cr.AbstractInstanciatedResource.UUID = cr.UUID
	result, _ := cr.ConvertToPricedResource(tools.COMPUTE_RESOURCE, nil, nil, nil, nil, nil, req)
	assert.NotNil(t, result)
	assert.IsType(t, &resources.PricedComputeResource{}, result)
}

func TestComputeResourcePricingProfile_GetPriceHT_CPUs(t *testing.T) {
	start := time.Now()
	end := start.Add(5 * time.Minute)
	profile := resources.ComputeResourcePricingProfile{
		CPUsPrices: map[string]float64{"Xeon": 2.0},
		ExploitPricingProfile: pricing.ExploitPricingProfile[pricing.TimePricingStrategy]{
			AccessPricingProfile: pricing.AccessPricingProfile[pricing.TimePricingStrategy]{
				Pricing: pricing.PricingStrategy[pricing.TimePricingStrategy]{Price: 1.0},
			},
		},
	}

	price, err := profile.GetPriceHT(2, 3600, start, end, []*pricing.PricingVariation{}, "cpus", "Xeon")
	require.NoError(t, err)
	assert.Greater(t, price, float64(0))
}

func TestComputeResourcePricingProfile_GetPriceHT_InvalidParams(t *testing.T) {
	profile := resources.ComputeResourcePricingProfile{}
	_, err := profile.GetPriceHT(1, 3600, time.Now(), time.Now(), []*pricing.PricingVariation{})
	assert.Error(t, err)
	assert.Equal(t, "params must be set", err.Error())
}

func TestPricedComputeResource_GetPriceHT(t *testing.T) {
	start := time.Now()
	end := start.Add(5 * time.Minute)
	r := resources.PricedComputeResource{
		PricedResource: resources.PricedResource[*resources.ComputeResourcePricingProfile]{
			ResourceID: "comp456",
			SelectedPricing: &resources.ComputeResourcePricingProfile{
				CPUsPrices: map[string]float64{"Xeon": 2.0},
				ExploitPricingProfile: pricing.ExploitPricingProfile[pricing.TimePricingStrategy]{
					AccessPricingProfile: pricing.AccessPricingProfile[pricing.TimePricingStrategy]{
						Pricing: pricing.PricingStrategy[pricing.TimePricingStrategy]{Price: 1.0},
					},
				},
			},
			BookingConfiguration: &resources.BookingConfiguration{
				UsageStart:               &start,
				UsageEnd:                 &end,
				ExplicitBookingDurationS: 3600,
			},
		},
		CPUsLocated: map[string]float64{"Xeon": 2},
		GPUsLocated: map[string]float64{},
		RAMLocated:  0,
	}

	price, err := r.GetPriceHT()
	require.NoError(t, err)
	assert.Greater(t, price, float64(0))
}

func TestPricedComputeResource_GetPriceHT_MissingProfile(t *testing.T) {
	r := resources.PricedComputeResource{
		PricedResource: resources.PricedResource[*resources.ComputeResourcePricingProfile]{
			ResourceID: "comp789",
		},
	}
	_, err := r.GetPriceHT()
	require.Error(t, err)
	assert.Contains(t, err.Error(), "pricing profile must be set")
}

func TestPricedComputeResource_FillWithDefaultProcessingUsage(t *testing.T) {
	usage := &resources.ProcessingUsage{
		CPUs: map[string]*models.CPU{"t": {Model: "Xeon", Cores: 4}},
		GPUs: map[string]*models.GPU{"t1": {Model: "Tesla"}},
		RAM:  &models.RAM{SizeGb: 16},
	}
	r := &resources.PricedComputeResource{
		CPUsLocated: make(map[string]float64),
		GPUsLocated: make(map[string]float64),
		RAMLocated:  0,
	}
	r.FillWithDefaultProcessingUsage(usage)
	assert.Equal(t, float64(4), r.CPUsLocated["Xeon"])
	assert.Equal(t, float64(1), r.GPUsLocated["Tesla"])
	assert.Equal(t, float64(16), r.RAMLocated)
}
