package oci

import (
	"context"
	"fmt"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/oracle/oci-go-sdk/v65/core"
)

// TestRealOCIImages 用试用期 key 验证镜像列表查询（只读）。
func TestRealOCIImages(t *testing.T) {
	if os.Getenv("OCI_INTEGRATION_TEST") != "1" {
		t.Skip("set OCI_INTEGRATION_TEST=1 to run real OCI tests")
	}
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
	defer cancel()
	cred := loadTestCredentials(t, "试用期")
	client := NewClient()

	images, err := client.ListImages(ctx, cred, ImagesQuery{
		OperatingSystem: "Canonical Ubuntu",
		Shape:           "VM.Standard.A1.Flex",
	})
	if err != nil {
		t.Fatalf("ListImages: %v", err)
	}
	if len(images) == 0 {
		t.Fatal("ListImages returned empty list for Ubuntu on A1")
	}
	for _, img := range images[:min(3, len(images))] {
		t.Logf("image %s os=%s %s size=%dMB", img.DisplayName, img.OperatingSystem, img.OperatingSystemVersion, img.SizeInMBs)
		if img.ID == "" || img.OperatingSystem == "" {
			t.Errorf("image %+v has empty mandatory field", img)
		}
	}
}

// TestRealOCINetworkCRUDAndIPv6 走全链路：建 VCN → 建子网 → 建安全列表 →
// 一键 IPv6 → 校验，结束后尽力清理全部资源。
// 会创建真实云资源，资源名前缀 oci-portal-e2e-<timestamp>。
func TestRealOCINetworkCRUDAndIPv6(t *testing.T) {
	if os.Getenv("OCI_INTEGRATION_TEST") != "1" {
		t.Skip("set OCI_INTEGRATION_TEST=1 to run real OCI tests")
	}
	ctx, cancel := context.WithTimeout(context.Background(), 8*time.Minute)
	defer cancel()
	cred := loadTestCredentials(t, "试用期")
	client := NewClient()
	prefix := fmt.Sprintf("oci-portal-e2e-%d", time.Now().Unix())

	vcn, err := client.CreateVCN(ctx, cred, CreateVCNInput{
		DisplayName: prefix + "-vcn",
		CidrBlock:   "10.99.0.0/16",
		DnsLabel:    "ociportale2e",
	})
	if err != nil {
		t.Fatalf("CreateVCN: %v", err)
	}
	t.Logf("created vcn %s (%s)", vcn.DisplayName, vcn.ID)
	defer cleanupVCN(t, client, cred, vcn.ID)

	if _, err := client.UpdateVCN(ctx, cred, "", vcn.ID, prefix+"-vcn-renamed"); err != nil {
		t.Errorf("UpdateVCN: %v", err)
	}

	subnet, err := client.CreateSubnet(ctx, cred, CreateSubnetInput{
		VcnID:       vcn.ID,
		DisplayName: prefix + "-subnet",
		CidrBlock:   "10.99.1.0/24",
		DnsLabel:    "e2esub",
	})
	if err != nil {
		t.Fatalf("CreateSubnet: %v", err)
	}
	t.Logf("created subnet %s (%s)", subnet.DisplayName, subnet.ID)

	secList, err := client.CreateSecurityList(ctx, cred, CreateSecurityListInput{
		VcnID:       vcn.ID,
		DisplayName: prefix + "-seclist",
		IngressRules: []SecurityRule{
			{Protocol: "6", Source: "0.0.0.0/0", PortMin: intPtr(22), PortMax: intPtr(22), Description: "ssh"},
		},
		EgressRules: []SecurityRule{{Protocol: "all", Destination: "0.0.0.0/0"}},
	})
	if err != nil {
		t.Fatalf("CreateSecurityList: %v", err)
	}
	t.Logf("created security list %s (%s)", secList.DisplayName, secList.ID)
	if len(secList.IngressRules) != 1 || secList.IngressRules[0].PortMin == nil || *secList.IngressRules[0].PortMin != 22 {
		t.Errorf("ingress rules = %+v, want one ssh rule with port 22", secList.IngressRules)
	}

	updated, err := client.UpdateSecurityList(ctx, cred, secList.ID, UpdateSecurityListInput{
		IngressRules: &[]SecurityRule{
			{Protocol: "6", Source: "0.0.0.0/0", PortMin: intPtr(22), PortMax: intPtr(22), Description: "ssh"},
			{Protocol: "6", Source: "0.0.0.0/0", PortMin: intPtr(443), PortMax: intPtr(443), Description: "https"},
		},
	})
	if err != nil {
		t.Fatalf("UpdateSecurityList: %v", err)
	}
	if len(updated.IngressRules) != 2 {
		t.Errorf("ingress rules after update = %d, want 2", len(updated.IngressRules))
	}

	steps, err := client.EnableVCNIPv6(ctx, cred, "", vcn.ID)
	if err != nil {
		t.Fatalf("EnableVCNIPv6: %v", err)
	}
	for _, s := range steps {
		t.Logf("ipv6 step=%s status=%s %s", s.Step, s.Status, s.Detail)
		if s.Status == "failed" {
			t.Errorf("ipv6 step %s failed: %s", s.Step, s.Detail)
		}
	}
	vcnAfter, err := client.GetVCN(ctx, cred, "", vcn.ID)
	if err != nil {
		t.Fatalf("GetVCN after ipv6: %v", err)
	}
	if len(vcnAfter.Ipv6CidrBlocks) == 0 {
		t.Error("vcn has no ipv6 cidr after enable-ipv6")
	}
	subnetAfter, err := client.GetSubnet(ctx, cred, "", subnet.ID)
	if err != nil {
		t.Fatalf("GetSubnet after ipv6: %v", err)
	}
	if subnetAfter.Ipv6CidrBlock == "" {
		t.Error("subnet has no ipv6 cidr after enable-ipv6")
	}
	t.Logf("vcn ipv6=%v subnet ipv6=%s", vcnAfter.Ipv6CidrBlocks, subnetAfter.Ipv6CidrBlock)

	// 幂等：重复执行应全部 skipped
	steps2, err := client.EnableVCNIPv6(ctx, cred, "", vcn.ID)
	if err != nil {
		t.Fatalf("EnableVCNIPv6 twice: %v", err)
	}
	for _, s := range steps2 {
		if s.Status != "skipped" {
			t.Errorf("rerun step %s status = %s, want skipped", s.Step, s.Status)
		}
	}

	if err := client.DeleteSecurityList(ctx, cred, "", secList.ID); err != nil {
		t.Errorf("DeleteSecurityList: %v", err)
	}
	if err := client.DeleteSubnet(ctx, cred, "", subnet.ID); err != nil {
		t.Errorf("DeleteSubnet: %v", err)
	}
}

// cleanupVCN 尽力清理 VCN 及其关联资源：清空默认路由表、删 IGW、
// 删残留子网与自建安全列表，最后删 VCN。
func cleanupVCN(t *testing.T, client *RealClient, cred Credentials, vcnID string) {
	t.Helper()
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
	defer cancel()
	vn, err := client.vcnClient(cred, "")
	if err != nil {
		t.Errorf("cleanup: vcn client: %v", err)
		return
	}
	vcnResp, err := vn.GetVcn(ctx, core.GetVcnRequest{VcnId: &vcnID})
	if err != nil {
		t.Errorf("cleanup: get vcn: %v", err)
		return
	}
	if _, err := vn.UpdateRouteTable(ctx, core.UpdateRouteTableRequest{
		RtId:                    vcnResp.DefaultRouteTableId,
		UpdateRouteTableDetails: core.UpdateRouteTableDetails{RouteRules: []core.RouteRule{}},
	}); err != nil {
		t.Logf("cleanup: clear route table: %v", err)
	}
	igws, err := vn.ListInternetGateways(ctx, core.ListInternetGatewaysRequest{
		CompartmentId: &cred.TenancyOCID, VcnId: &vcnID,
	})
	if err == nil {
		for _, igw := range igws.Items {
			if _, err := vn.DeleteInternetGateway(ctx, core.DeleteInternetGatewayRequest{IgId: igw.Id}); err != nil {
				t.Logf("cleanup: delete igw: %v", err)
			}
		}
	}
	if subnets, err := listSubnets(ctx, vn, cred.TenancyOCID, vcnID); err == nil {
		for _, s := range subnets {
			if err := deleteSubnetWithRetry(ctx, client, cred, s.ID); err != nil {
				t.Logf("cleanup: delete subnet %s: %v", s.DisplayName, err)
			}
		}
	}
	if lists, err := client.ListSecurityLists(ctx, cred, "", vcnID); err == nil {
		for _, sl := range lists {
			// 默认安全列表的名字包含 VCN 名，须用前缀匹配排除，它随 VCN 一起删除
			if strings.HasPrefix(sl.DisplayName, "oci-portal-e2e-") {
				if err := client.DeleteSecurityList(ctx, cred, "", sl.ID); err != nil {
					t.Logf("cleanup: delete security list %s: %v", sl.DisplayName, err)
				}
			}
		}
	}
	if err := client.DeleteVCN(ctx, cred, "", vcnID); err != nil {
		t.Errorf("cleanup: delete vcn: %v (manual cleanup may be required)", err)
		return
	}
	t.Logf("cleanup: vcn %s deleted", vcnID)
}

// deleteSubnetWithRetry 删除子网；实例终止后 VNIC 异步释放，子网短暂
// 保持被引用状态，409 Conflict 时等待重试，最长约 60 秒。
func deleteSubnetWithRetry(ctx context.Context, client *RealClient, cred Credentials, subnetID string) error {
	var err error
	for i := 0; i < 12; i++ {
		if err = client.DeleteSubnet(ctx, cred, "", subnetID); err == nil {
			return nil
		}
		if !strings.Contains(err.Error(), "Conflict") {
			return err
		}
		select {
		case <-ctx.Done():
			return err
		case <-time.After(5 * time.Second):
		}
	}
	return err
}

func intPtr(n int) *int { return &n }
