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, req)
	assert.NotNil(t, result)
	assert.IsType(t, &resources.PricedComputeResource{}, result)
}

func TestComputeResourcePricingProfile_GetPrice_CPUs(t *testing.T) {
	start := time.Now()
	end := start.Add(1 * time.Hour)
	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.GetPrice(2, 3600, start, end, "cpus", "Xeon")
	require.NoError(t, err)
	assert.Greater(t, price, float64(0))
}

func TestComputeResourcePricingProfile_GetPrice_InvalidParams(t *testing.T) {
	profile := resources.ComputeResourcePricingProfile{}
	_, err := profile.GetPrice(1, 3600, time.Now(), time.Now())
	assert.Error(t, err)
	assert.Equal(t, "params must be set", err.Error())
}

func TestPricedComputeResource_GetPrice(t *testing.T) {
	start := time.Now()
	end := start.Add(1 * time.Hour)
	profile := &resources.ComputeResourcePricingProfile{
		CPUsPrices: map[string]float64{"Xeon": 1.0},
		GPUsPrices: map[string]float64{"Tesla": 2.0},
		RAMPrice:   0.5,
		ExploitPricingProfile: pricing.ExploitPricingProfile[pricing.TimePricingStrategy]{
			AccessPricingProfile: pricing.AccessPricingProfile[pricing.TimePricingStrategy]{
				Pricing: pricing.PricingStrategy[pricing.TimePricingStrategy]{Price: 1.0},
			},
		},
	}
	r := resources.PricedComputeResource{
		PricedResource: resources.PricedResource{
			ResourceID:               "comp456",
			PricingProfiles:          []pricing.PricingProfileITF{profile},
			UsageStart:               &start,
			UsageEnd:                 &end,
			ExplicitBookingDurationS: 3600,
		},
		CPUsLocated: map[string]float64{"Xeon": 2},
		GPUsLocated: map[string]float64{"Tesla": 1},
		RAMLocated:  4,
	}

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

func TestPricedComputeResource_GetPrice_MissingProfile(t *testing.T) {
	r := resources.PricedComputeResource{
		PricedResource: resources.PricedResource{
			ResourceID: "comp789",
		},
	}
	_, err := r.GetPrice()
	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)
}