package resources

import (
	"errors"
	"time"

	"cloud.o-forge.io/core/oc-lib/models/common/enum"
	"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/utils"
	"cloud.o-forge.io/core/oc-lib/tools"
	"github.com/google/uuid"
)

type ProcessingUsage struct {
	CPUs map[string]*models.CPU `bson:"cpus,omitempty" json:"cpus,omitempty"` // CPUs is the list of CPUs key is model
	GPUs map[string]*models.GPU `bson:"gpus,omitempty" json:"gpus,omitempty"` // GPUs is the list of GPUs key is model
	RAM  *models.RAM            `bson:"ram,omitempty" json:"ram,omitempty"`   // RAM is the RAM

	StorageGb    float64 `bson:"storage,omitempty" json:"storage,omitempty"` // Storage is the storage
	Hypothesis   string  `bson:"hypothesis,omitempty" json:"hypothesis,omitempty"`
	ScalingModel string  `bson:"scaling_model,omitempty" json:"scaling_model,omitempty"` // ScalingModel is the scaling model
}

/*
* ProcessingResource is a struct that represents a processing resource
* it defines the resource processing
 */
type ProcessingResource struct {
	AbstractInstanciatedResource[*ProcessingInstance]
	Infrastructure enum.InfrastructureType `json:"infrastructure" bson:"infrastructure" default:"-1"`
	Usage          *ProcessingUsage        `bson:"usage,omitempty" json:"usage,omitempty"`
	OpenSource     bool                    `json:"open_source" bson:"open_source" default:"false"`
	// IsService marks a long-running processing that acts as a persistent service.
	// Such processings do not require a Compute booking (they manage their own lifecycle).
	IsService bool   `json:"is_service" bson:"is_service" default:"false"`
	Maturity  string `json:"maturity,omitempty" bson:"maturity,omitempty"`
	// License is now in AbstractResource — kept here as alias for backward compat with existing DB docs.
	// New code should use AbstractResource.License.
}

func (r *ProcessingResource) GetType() string {
	return tools.PROCESSING_RESOURCE.String()
}

type ProcessingInstance struct {
	ResourceInstance[*ResourcePartnerShip[*ProcessingResourcePricingProfile]]
	Access      *ResourceAccess `json:"access,omitempty" bson:"access,omitempty"` // Access is the access
	SizeGB      int             `json:"size_gb,omitempty" bson:"size_gb,omitempty"`
	ContentType string          `json:"content_type,omitempty" bson:"content_type,omitempty"`
}

func NewProcessingInstance(name string, peerID string) ResourceInstanceITF {
	return &ProcessingInstance{
		ResourceInstance: ResourceInstance[*ResourcePartnerShip[*ProcessingResourcePricingProfile]]{
			AbstractObject: utils.AbstractObject{
				UUID: uuid.New().String(),
				Name: name,
			},
		},
	}
}

type ProcessingResourcePartnership struct {
	ResourcePartnerShip[*ProcessingResourcePricingProfile]
}

type PricedProcessingResource struct {
	PricedResource[*ProcessingResourcePricingProfile]
}

func (r *PricedProcessingResource) ensurePricing() {
	if r.SelectedPricing == nil {
		r.SelectedPricing = &ProcessingResourcePricingProfile{}
	}
}

func (r *PricedProcessingResource) IsPurchasable() bool {
	r.ensurePricing()
	return r.SelectedPricing.IsPurchasable()
}

func (r *PricedProcessingResource) IsBooked() bool {
	r.ensurePricing()
	return r.SelectedPricing.IsBooked()
}

func (r *PricedProcessingResource) GetPriceHT() (float64, error) {
	r.ensurePricing()
	return r.PricedResource.GetPriceHT()
}

func (r *PricedProcessingResource) GetType() tools.DataType {
	return tools.PROCESSING_RESOURCE
}

func (a *PricedProcessingResource) GetExplicitDurationInS() float64 {
	if a.BookingConfiguration == nil {
		a.BookingConfiguration = &BookingConfiguration{}
	}
	if a.BookingConfiguration.ExplicitBookingDurationS == 0 {
		if a.BookingConfiguration.UsageStart == nil {
			return (5 * time.Minute).Seconds()
		}
		return a.BookingConfiguration.UsageEnd.Sub(*a.BookingConfiguration.UsageStart).Seconds()
	}
	return a.BookingConfiguration.ExplicitBookingDurationS
}

func (d *ProcessingResource) GetAccessor(request *tools.APIRequest) utils.Accessor {
	return NewAccessor[*ProcessingResource](tools.PROCESSING_RESOURCE, request) // Create a new instance of the accessor
}

func (abs *ProcessingResource) ConvertToPricedResource(t tools.DataType, selectedInstance *int, selectedPartnership *int, selectedBuyingStrategy *int, selectedStrategy *int, selectedBookingModeIndex *int, request *tools.APIRequest) (pricing.PricedItemITF, error) {
	if t != tools.PROCESSING_RESOURCE {
		return nil, errors.New("not the proper type expected : cannot convert to priced resource : have " + t.String() + " wait Data")
	}
	p, err := ConvertToPricedResource[*DataResourcePricingProfile](t, selectedInstance, selectedPartnership, selectedBuyingStrategy, selectedStrategy, selectedBookingModeIndex, abs, request)
	if err != nil {
		return nil, err
	}
	priced := p.(*PricedResource[*DataResourcePricingProfile])
	return &PricedDataResource{
		PricedResource: *priced,
	}, nil
}

type ProcessingResourcePricingProfile struct {
	pricing.AccessPricingProfile[pricing.TimePricingStrategy] // AccessPricingProfile is the pricing profile of a data it means that we can access the data for an amount of time
}

func (p *ProcessingResourcePricingProfile) IsPurchasable() bool {
	return p.Pricing.BuyingStrategy == pricing.PERMANENT
}

func (p *ProcessingResourcePricingProfile) IsBooked() bool {
	return p.Pricing.BuyingStrategy != pricing.PERMANENT
}
