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/tools"
	"github.com/stretchr/testify/assert"

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

func TestStorageResource_GetType(t *testing.T) {
	res := &resources.StorageResource{}
	assert.Equal(t, tools.STORAGE_RESOURCE.String(), res.GetType())
}

func TestStorageResource_GetAccessor(t *testing.T) {
	res := &resources.StorageResource{}
	req := &tools.APIRequest{}
	accessor := res.GetAccessor(req)
	assert.NotNil(t, accessor)
}

func TestStorageResource_ConvertToPricedResource_ValidType(t *testing.T) {
	res := &resources.StorageResource{}
	res.AbstractInstanciatedResource.CreatorID = "creator"
	res.AbstractInstanciatedResource.UUID = "res-id"
	priced := res.ConvertToPricedResource(tools.STORAGE_RESOURCE, &tools.APIRequest{})
	assert.NotNil(t, priced)
	assert.IsType(t, &resources.PricedStorageResource{}, priced)
}

func TestStorageResource_ConvertToPricedResource_InvalidType(t *testing.T) {
	res := &resources.StorageResource{}
	priced := res.ConvertToPricedResource(tools.COMPUTE_RESOURCE, &tools.APIRequest{})
	assert.Nil(t, priced)
}

func TestStorageResourceInstance_ClearEnv(t *testing.T) {
	inst := &resources.StorageResourceInstance{
		Credentials: &resources.Credentials{Login: "test"},
		ResourceInstance: resources.ResourceInstance[*resources.StorageResourcePartnership]{
			Env:     []models.Param{{Attr: "A"}},
			Inputs:  []models.Param{{Attr: "B"}},
			Outputs: []models.Param{{Attr: "C"}},
		},
	}

	inst.ClearEnv()
	assert.Nil(t, inst.Credentials)
	assert.Empty(t, inst.Env)
	assert.Empty(t, inst.Inputs)
	assert.Empty(t, inst.Outputs)
}

func TestStorageResourceInstance_StoreDraftDefault(t *testing.T) {
	inst := &resources.StorageResourceInstance{
		Source: "my-source",
		ResourceInstance: resources.ResourceInstance[*resources.StorageResourcePartnership]{
			Env: []models.Param{},
		},
	}

	inst.StoreDraftDefault()
	assert.Len(t, inst.Env, 1)
	assert.Equal(t, "source", inst.Env[0].Attr)
	assert.Equal(t, "my-source", inst.Env[0].Value)
	assert.True(t, inst.Env[0].Readonly)
}

func TestStorageResourcePricingStrategy_GetQuantity(t *testing.T) {
	tests := []struct {
		strategy resources.StorageResourcePricingStrategy
		dataGB   float64
		expect   float64
	}{
		{resources.PER_DATA_STORED, 1.2, 1.2},
		{resources.PER_TB_STORED, 1.2, 1200},
		{resources.PER_GB_STORED, 2.5, 2.5},
		{resources.PER_MB_STORED, 1.0, 1000},
		{resources.PER_KB_STORED, 0.1, 100000},
	}

	for _, tt := range tests {
		q, err := tt.strategy.GetQuantity(tt.dataGB)
		assert.NoError(t, err)
		assert.Equal(t, tt.expect, q)
	}
}

func TestStorageResourcePricingStrategy_GetQuantity_Invalid(t *testing.T) {
	invalid := resources.StorageResourcePricingStrategy(99)
	q, err := invalid.GetQuantity(1.0)
	assert.Error(t, err)
	assert.Equal(t, 0.0, q)
}

func TestPricedStorageResource_GetPrice_NoProfiles(t *testing.T) {
	res := &resources.PricedStorageResource{
		PricedResource: resources.PricedResource{
			ResourceID: "res-id",
		},
	}
	_, err := res.GetPrice()
	assert.Error(t, err)
}

func TestPricedStorageResource_GetPrice_WithPricing(t *testing.T) {
	now := time.Now()
	end := now.Add(2 * time.Hour)
	profile := &resources.StorageResourcePricingProfile{
		ExploitPricingProfile: pricing.ExploitPricingProfile[resources.StorageResourcePricingStrategy]{
			AccessPricingProfile: pricing.AccessPricingProfile[resources.StorageResourcePricingStrategy]{
				Pricing: pricing.PricingStrategy[resources.StorageResourcePricingStrategy]{
					BuyingStrategy: pricing.PAY_PER_USE,
					Price:          42.0,
				},
			},
		},
	}
	res := &resources.PricedStorageResource{
		PricedResource: resources.PricedResource{
			UsageStart:      &now,
			UsageEnd:        &end,
			PricingProfiles: []pricing.PricingProfileITF{profile},
		},
		UsageStorageGB: 1.0,
	}
	price, err := res.GetPrice()
	assert.NoError(t, err)
	assert.Equal(t, 42.0, price)
}

func TestStorageResourcePricingProfile_IsPurchased(t *testing.T) {
	p := &resources.StorageResourcePricingProfile{
		ExploitPricingProfile: pricing.ExploitPricingProfile[resources.StorageResourcePricingStrategy]{
			AccessPricingProfile: pricing.AccessPricingProfile[resources.StorageResourcePricingStrategy]{
				Pricing: pricing.PricingStrategy[resources.StorageResourcePricingStrategy]{BuyingStrategy: pricing.PAY_PER_USE},
			},
		},
	}
	assert.False(t, p.IsPurchased())

	p.Pricing.BuyingStrategy = pricing.UNLIMITED
	assert.True(t, p.IsPurchased())
}