package provider

import (
	"encoding/json"
	"testing"
)

func TestAWSParseRaw(t *testing.T) {
	awsJSON := []byte(`{
		"manifest":{"currencyCode":"USD"},
		"regions":{
			"US East (N. Virginia)":{
				"t3.micro":{
					"price":"0.0104",
					"Location":"US East (N. Virginia)",
					"Instance Family":"General purpose",
					"vCPU":"2",
					"Instance Type":"t3.micro",
					"Memory":"1 GiB",
					"Storage":"EBS only",
					"Network Performance":"Low",
					"Operating System":"Linux"
				}
			}
		}
	}`)

	raw := &RawBundle{
		Provider: "aws",
		Region:   "us-east-1",
		Parts: []RawPart{{
			Scope: "us-east-1",
			Name:  "pricing",
			Data:  awsJSON,
		}},
	}

	p := NewAWSProvider()
	ud, err := p.ParseRaw(raw)
	if err != nil {
		t.Fatalf("parse raw: %v", err)
	}

	if len(ud.Instances["us-east-1"]) != 1 {
		t.Fatalf("expected 1 instance")
	}
	if ud.Instances["us-east-1"][0].Currency != "USD" {
		t.Fatalf("expected USD currency")
	}
}

func TestAzureParseRaw(t *testing.T) {
	pricing := []AzurePricingItem{{
		CurrencyCode:  "USD",
		RetailPrice:   0.1,
		ArmRegionName: "eastus",
		SkuName:       "Standard_D2_v5",
		ArmSkuName:    "Standard_D2_v5",
		Type:          "Consumption",
		ProductName:   "Virtual Machines D2 v5",
		ServiceName:   "Virtual Machines",
	}}
	pricingBytes, _ := json.Marshal(pricing)

	metadata := []byte(`{"attributesByOffer":{"linux-d2v5-standard":{"series":"Dsv5","cores":2,"category":"generalpurpose","ram":8,"diskSize":0,"type":"linux"}}}`)

	raw := &RawBundle{
		Provider: "azure",
		Region:   "eastus",
		Parts: []RawPart{
			{Scope: "eastus", Name: "pricing", Data: pricingBytes},
			{Scope: "global", Name: "metadata", Data: metadata},
		},
	}

	p := NewAzureProvider()
	ud, err := p.ParseRaw(raw)
	if err != nil {
		t.Fatalf("parse raw: %v", err)
	}
	if len(ud.Instances["eastus"]) != 1 {
		t.Fatalf("expected 1 instance")
	}
}

func TestAzureParseRawLocalSSD(t *testing.T) {
	pricing := []AzurePricingItem{{
		CurrencyCode:  "USD",
		RetailPrice:   0.25,
		ArmRegionName: "eastus",
		SkuName:       "Standard_L8as_v3",
		ArmSkuName:    "Standard_L8as_v3",
		Type:          "Consumption",
		ProductName:   "Virtual Machines L8as v3",
		ServiceName:   "Virtual Machines",
	}}
	pricingBytes, _ := json.Marshal(pricing)

	skus := []AzureSku{{
		Name:         "Standard_L8as_v3",
		ResourceType: "virtualMachines",
		Locations:    []string{"eastus"},
		Family:       "standardLASv3Family",
		Capabilities: []AzureSkuCapability{
			{Name: "vCPUs", Value: "8"},
			{Name: "MemoryGB", Value: "64"},
			{Name: "NvmeDiskSizeInMiB", Value: "1831054"},
		},
	}}
	skuBytes, _ := json.Marshal(skus)

	raw := &RawBundle{
		Provider: "azure",
		Region:   "eastus",
		Parts: []RawPart{
			{Scope: "eastus", Name: "pricing", Data: pricingBytes},
			{Scope: "global", Name: "compute_skus", Data: skuBytes},
		},
	}

	p := NewAzureProvider()
	ud, err := p.ParseRaw(raw)
	if err != nil {
		t.Fatalf("parse raw: %v", err)
	}
	instances := ud.Instances["eastus"]
	if len(instances) != 1 {
		t.Fatalf("expected 1 instance")
	}
	if instances[0].Storage != "Local SSD" {
		t.Fatalf("expected Local SSD storage, got %q", instances[0].Storage)
	}
}

func TestAliyunParseRaw(t *testing.T) {
	instanceTypes := []byte(`[{"InstanceTypeId":"ecs.g6.large","CpuCoreCount":2,"MemorySize":8.0,"InstanceTypeFamily":"ecs.g6"}]`)
	available := []byte(`["ecs.g6.large"]`)
	priceMap := []byte(`{"ecs.g6.large":{"price":0.2,"currency":"CNY"}}`)

	raw := &RawBundle{
		Provider: "aliyun",
		Region:   "cn-hangzhou",
		Parts: []RawPart{
			{Scope: "global", Name: "instance_types", Data: instanceTypes},
			{Scope: "cn-hangzhou", Name: "available_instance_types", Data: available},
			{Scope: "cn-hangzhou", Name: "price_map", Data: priceMap},
		},
	}

	p := NewAliyunProvider("ak", "sk", "")
	ud, err := p.ParseRaw(raw)
	if err != nil {
		t.Fatalf("parse raw: %v", err)
	}
	if len(ud.Instances["cn-hangzhou"]) != 1 {
		t.Fatalf("expected 1 instance")
	}
}

func TestGCPParseRaw(t *testing.T) {
	skus := []gcpSku{
		{
			Description:    "N1 Standard Instance CPU",
			Category:       gcpSkuCategory{ResourceFamily: "Compute", UsageType: "OnDemand"},
			ServiceRegions: []string{"us-central1"},
			PricingInfo:    []gcpPricingInfo{{PricingExpression: gcpPricingExpression{TieredRates: []gcpTieredRate{{UnitPrice: gcpUnitPrice{Units: "0", Nanos: 200000000}}}}}},
		},
		{
			Description:    "N1 Standard Instance Ram",
			Category:       gcpSkuCategory{ResourceFamily: "Compute", UsageType: "OnDemand"},
			ServiceRegions: []string{"us-central1"},
			PricingInfo:    []gcpPricingInfo{{PricingExpression: gcpPricingExpression{TieredRates: []gcpTieredRate{{UnitPrice: gcpUnitPrice{Units: "0", Nanos: 100000000}}}}}},
		},
	}
	machineTypes := []gcpMachineType{
		{Name: "n1-standard-1", GuestCpus: 1, MemoryMb: 3840, Zone: "us-central1-a"},
		{Name: "c3-standard-4-lssd", GuestCpus: 4, MemoryMb: 8192, Zone: "us-central1-a"},
		{Name: "z3-standard-8", GuestCpus: 8, MemoryMb: 16384, Zone: "us-central1-a"},
	}

	skuBytes, _ := json.Marshal(skus)
	machineBytes, _ := json.Marshal(machineTypes)

	raw := &RawBundle{
		Provider: "gcp",
		Region:   "us-central1",
		Parts: []RawPart{
			{Scope: "global", Name: "billing_skus", Data: skuBytes},
			{Scope: "us-central1", Name: "machine_types", Data: machineBytes},
		},
	}

	p := NewGCPProvider()
	ud, err := p.ParseRaw(raw)
	if err != nil {
		t.Fatalf("parse raw: %v", err)
	}
	instances := ud.Instances["us-central1"]
	if len(instances) != 3 {
		t.Fatalf("expected 3 instances")
	}

	foundLocal := false
	foundZ3 := false
	for _, inst := range instances {
		switch inst.InstanceType {
		case "c3-standard-4-lssd":
			if inst.Storage != "Local SSD" {
				t.Fatalf("expected Local SSD for c3 lssd")
			}
			foundLocal = true
		case "z3-standard-8":
			if inst.Storage != "Local SSD" {
				t.Fatalf("expected Local SSD for z3")
			}
			foundZ3 = true
		}
	}
	if !foundLocal || !foundZ3 {
		t.Fatalf("missing expected local SSD instances")
	}
}
