package oci

import (
	"context"
	"fmt"
	"net/netip"
	"time"

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

// ipv6AnyCIDR 是 IPv6 默认路由与放行目标。
const ipv6AnyCIDR = "::/0"

// IPv6Step 是一键启用 IPv6 过程中的一个步骤结果。
type IPv6Step struct {
	Step   string `json:"step"`
	Status string `json:"status"` // done / skipped / failed
	Detail string `json:"detail,omitempty"`
}

// EnableVCNIPv6 实现 Client：为 VCN 一键启用 IPv6。依次执行：
//  1. VCN 分配 Oracle GUA /56（已有则跳过）；
//  2. 每个无 IPv6 的子网从 /56 中分配一个 /64；
//  3. 默认路由表补 ::/0 → Internet Gateway（无 IGW 时自动创建）；
//  4. 默认安全列表补 egress ::/0 放行。
//
// 步骤级失败记录在返回的步骤列表中，不中断后续无依赖的步骤。
func (c *RealClient) EnableVCNIPv6(ctx context.Context, cred Credentials, region, vcnID string) ([]IPv6Step, error) {
	vn, err := c.vcnClient(cred, region)
	if err != nil {
		return nil, err
	}
	vcnResp, err := vn.GetVcn(ctx, core.GetVcnRequest{VcnId: &vcnID})
	if err != nil {
		return nil, fmt.Errorf("get vcn %s: %w", vcnID, err)
	}
	// 子网 / IGW 的关联查询须用 VCN 实际所在 compartment，传租户根会漏查
	vcnCompartment := *hostCompartment(cred, deref(vcnResp.CompartmentId))
	var steps []IPv6Step
	vcnCidr, step := ensureVCNIPv6Cidr(ctx, vn, vcnResp.Vcn)
	steps = append(steps, step)
	steps = append(steps, ensureSubnetsIPv6(ctx, vn, vcnCompartment, vcnID, vcnCidr)...)
	steps = append(steps, ensureIPv6Route(ctx, vn, vcnCompartment, vcnResp.Vcn))
	steps = append(steps, ensureIPv6Egress(ctx, vn, vcnResp.Vcn))
	return orEmpty(steps), nil
}

// ensureVCNIPv6Cidr 确保 VCN 拥有 IPv6 GUA，返回其 /56 前缀。
func ensureVCNIPv6Cidr(ctx context.Context, vn core.VirtualNetworkClient, vcn core.Vcn) (string, IPv6Step) {
	if len(vcn.Ipv6CidrBlocks) > 0 {
		return vcn.Ipv6CidrBlocks[0], IPv6Step{Step: "vcn-ipv6-cidr", Status: "skipped", Detail: vcn.Ipv6CidrBlocks[0]}
	}
	enabled := true
	_, err := vn.AddIpv6VcnCidr(ctx, core.AddIpv6VcnCidrRequest{
		VcnId:                 vcn.Id,
		AddVcnIpv6CidrDetails: core.AddVcnIpv6CidrDetails{IsOracleGuaAllocationEnabled: &enabled},
	})
	if err != nil {
		return "", IPv6Step{Step: "vcn-ipv6-cidr", Status: "failed", Detail: err.Error()}
	}
	cidr, err := waitVCNIPv6Cidr(ctx, vn, *vcn.Id)
	if err != nil {
		return "", IPv6Step{Step: "vcn-ipv6-cidr", Status: "failed", Detail: err.Error()}
	}
	return cidr, IPv6Step{Step: "vcn-ipv6-cidr", Status: "done", Detail: cidr}
}

// waitVCNIPv6Cidr 轮询等待 Oracle GUA 分配完成（异步操作）。
func waitVCNIPv6Cidr(ctx context.Context, vn core.VirtualNetworkClient, vcnID string) (string, error) {
	for i := 0; i < 30; i++ {
		resp, err := vn.GetVcn(ctx, core.GetVcnRequest{VcnId: &vcnID})
		if err != nil {
			return "", fmt.Errorf("poll vcn ipv6 cidr: %w", err)
		}
		if len(resp.Ipv6CidrBlocks) > 0 {
			return resp.Ipv6CidrBlocks[0], nil
		}
		select {
		case <-ctx.Done():
			return "", fmt.Errorf("poll vcn ipv6 cidr: %w", ctx.Err())
		case <-time.After(2 * time.Second):
		}
	}
	return "", fmt.Errorf("poll vcn ipv6 cidr: timed out")
}

// ensureSubnetsIPv6 为每个还没有 IPv6 的子网从 VCN /56 中分配 /64；
// compartmentID 须是 VCN 实际所在 compartment，否则列不到子网。
func ensureSubnetsIPv6(ctx context.Context, vn core.VirtualNetworkClient, compartmentID, vcnID, vcnCidr string) []IPv6Step {
	if vcnCidr == "" {
		return []IPv6Step{{Step: "subnet-ipv6-cidr", Status: "failed", Detail: "vcn has no ipv6 cidr"}}
	}
	subnets, err := listSubnets(ctx, vn, compartmentID, vcnID)
	if err != nil {
		return []IPv6Step{{Step: "subnet-ipv6-cidr", Status: "failed", Detail: err.Error()}}
	}
	used := make([]string, 0, len(subnets))
	for _, s := range subnets {
		if s.Ipv6CidrBlock != "" {
			used = append(used, s.Ipv6CidrBlock)
		}
	}
	var steps []IPv6Step
	for _, s := range subnets {
		if s.Ipv6CidrBlock != "" {
			steps = append(steps, IPv6Step{Step: "subnet-ipv6-cidr", Status: "skipped", Detail: s.DisplayName + " " + s.Ipv6CidrBlock})
			continue
		}
		steps = append(steps, addSubnetIPv6(ctx, vn, s, vcnCidr, &used))
	}
	return orEmpty(steps)
}

func addSubnetIPv6(ctx context.Context, vn core.VirtualNetworkClient, s Subnet, vcnCidr string, used *[]string) IPv6Step {
	cidr, err := nextSubnetIPv6CIDR(vcnCidr, *used)
	if err != nil {
		return IPv6Step{Step: "subnet-ipv6-cidr", Status: "failed", Detail: s.DisplayName + ": " + err.Error()}
	}
	_, err = vn.AddIpv6SubnetCidr(ctx, core.AddIpv6SubnetCidrRequest{
		SubnetId:                 &s.ID,
		AddSubnetIpv6CidrDetails: core.AddSubnetIpv6CidrDetails{Ipv6CidrBlock: &cidr},
	})
	if err != nil {
		return IPv6Step{Step: "subnet-ipv6-cidr", Status: "failed", Detail: s.DisplayName + ": " + err.Error()}
	}
	*used = append(*used, cidr)
	return IPv6Step{Step: "subnet-ipv6-cidr", Status: "done", Detail: s.DisplayName + " " + cidr}
}

// nextSubnetIPv6CIDR 从 VCN 的 /56 前缀中选出未被占用的 /64：
// /56 的前 7 字节固定，第 8 字节 0~255 即 256 个可用 /64。
func nextSubnetIPv6CIDR(vcnCidr string, used []string) (string, error) {
	prefix, err := netip.ParsePrefix(vcnCidr)
	if err != nil {
		return "", fmt.Errorf("parse vcn ipv6 cidr %s: %w", vcnCidr, err)
	}
	base := prefix.Addr().As16()
	taken := make(map[byte]bool, len(used))
	for _, u := range used {
		p, err := netip.ParsePrefix(u)
		if err != nil {
			continue
		}
		b := p.Addr().As16()
		if [7]byte(b[:7]) == [7]byte(base[:7]) {
			taken[b[7]] = true
		}
	}
	for i := 0; i <= 255; i++ {
		if taken[byte(i)] {
			continue
		}
		candidate := base
		candidate[7] = base[7] | byte(i)
		return netip.PrefixFrom(netip.AddrFrom16(candidate), 64).String(), nil
	}
	return "", fmt.Errorf("no free /64 left in %s", vcnCidr)
}

// ensureIPv6Route 在默认路由表补 ::/0 → IGW；没有 IGW 时自动创建。
// compartmentID 须是 VCN 实际所在 compartment，IGW 查找与创建都在其中进行。
func ensureIPv6Route(ctx context.Context, vn core.VirtualNetworkClient, compartmentID string, vcn core.Vcn) IPv6Step {
	rtResp, err := vn.GetRouteTable(ctx, core.GetRouteTableRequest{RtId: vcn.DefaultRouteTableId})
	if err != nil {
		return IPv6Step{Step: "route-ipv6-default", Status: "failed", Detail: err.Error()}
	}
	for _, rule := range rtResp.RouteRules {
		if deref(rule.Destination) == ipv6AnyCIDR {
			return IPv6Step{Step: "route-ipv6-default", Status: "skipped", Detail: "::/0 route exists"}
		}
	}
	igwID, err := ensureInternetGateway(ctx, vn, compartmentID, *vcn.Id)
	if err != nil {
		return IPv6Step{Step: "route-ipv6-default", Status: "failed", Detail: err.Error()}
	}
	dest := ipv6AnyCIDR
	rules := append(rtResp.RouteRules, core.RouteRule{
		Destination:     &dest,
		DestinationType: core.RouteRuleDestinationTypeCidrBlock,
		NetworkEntityId: &igwID,
	})
	_, err = vn.UpdateRouteTable(ctx, core.UpdateRouteTableRequest{
		RtId:                    vcn.DefaultRouteTableId,
		UpdateRouteTableDetails: core.UpdateRouteTableDetails{RouteRules: rules},
	})
	if err != nil {
		return IPv6Step{Step: "route-ipv6-default", Status: "failed", Detail: err.Error()}
	}
	return IPv6Step{Step: "route-ipv6-default", Status: "done", Detail: "::/0 -> " + igwID}
}

// ensureInternetGateway 返回 VCN 已有的 IGW，没有则创建一个；
// compartmentID 须是 VCN 实际所在 compartment，否则查不到已有 IGW。
func ensureInternetGateway(ctx context.Context, vn core.VirtualNetworkClient, compartmentID, vcnID string) (string, error) {
	listResp, err := vn.ListInternetGateways(ctx, core.ListInternetGatewaysRequest{
		CompartmentId: &compartmentID,
		VcnId:         &vcnID,
	})
	if err != nil {
		return "", fmt.Errorf("list internet gateways: %w", err)
	}
	if len(listResp.Items) > 0 {
		return deref(listResp.Items[0].Id), nil
	}
	enabled := true
	name := "oci-portal-igw"
	createResp, err := vn.CreateInternetGateway(ctx, core.CreateInternetGatewayRequest{
		CreateInternetGatewayDetails: core.CreateInternetGatewayDetails{
			CompartmentId: &compartmentID,
			VcnId:         &vcnID,
			IsEnabled:     &enabled,
			DisplayName:   &name,
		},
	})
	if err != nil {
		return "", fmt.Errorf("create internet gateway: %w", err)
	}
	return deref(createResp.InternetGateway.Id), nil
}

// AddInstanceIpv6 实现 Client：为实例主 VNIC 分配一个 IPv6 地址并返回。
// address 为空时由子网 /64 自动分配；子网未启用 IPv6 时 OCI 返回错误。
func (c *RealClient) AddInstanceIpv6(ctx context.Context, cred Credentials, region, instanceID, address string) (string, error) {
	vnic, err := c.primaryVnic(ctx, cred, region, instanceID)
	if err != nil {
		return "", err
	}
	return c.AddVnicIpv6(ctx, cred, region, deref(vnic.Id), address)
}

// AddVnicIpv6 实现 Client:为指定 VNIC 分配一个 IPv6 地址并返回(多网卡场景)。
func (c *RealClient) AddVnicIpv6(ctx context.Context, cred Credentials, region, vnicID, address string) (string, error) {
	vn, err := c.vcnClient(cred, region)
	if err != nil {
		return "", err
	}
	details := core.CreateIpv6Details{VnicId: &vnicID}
	if address != "" {
		details.IpAddress = &address
	}
	resp, err := vn.CreateIpv6(ctx, core.CreateIpv6Request{CreateIpv6Details: details})
	if err != nil {
		return "", fmt.Errorf("create ipv6 on vnic %s: %w", vnicID, err)
	}
	return deref(resp.IpAddress), nil
}

// DeleteInstanceIpv6 实现 Client：取消分配实例上的指定 IPv6 地址。
// 遍历实例 VNIC 找到地址对应的 Ipv6 对象后删除；地址不存在时报错。
func (c *RealClient) DeleteInstanceIpv6(ctx context.Context, cred Credentials, region, instanceID, address string) error {
	vn, err := c.vcnClient(cred, region)
	if err != nil {
		return err
	}
	vnics, _, err := c.instanceVnics(ctx, cred, region, instanceID)
	if err != nil {
		return err
	}
	for _, vnic := range vnics {
		resp, err := vn.ListIpv6s(ctx, core.ListIpv6sRequest{VnicId: vnic.Id})
		if err != nil {
			continue
		}
		for _, ip := range resp.Items {
			if !sameIPv6(deref(ip.IpAddress), address) {
				continue
			}
			if _, err := vn.DeleteIpv6(ctx, core.DeleteIpv6Request{Ipv6Id: ip.Id}); err != nil {
				return fmt.Errorf("delete ipv6 %s: %w", address, err)
			}
			return nil
		}
	}
	return fmt.Errorf("delete ipv6: address %s not found on instance %s", address, instanceID)
}

// sameIPv6 规范化比较两个 IPv6 地址（兼容大小写与缩写形式差异）。
func sameIPv6(a, b string) bool {
	pa, errA := netip.ParseAddr(a)
	pb, errB := netip.ParseAddr(b)
	if errA != nil || errB != nil {
		return a == b
	}
	return pa == pb
}

// ensureIPv6Egress 在默认安全列表补 egress ::/0 全协议放行。
func ensureIPv6Egress(ctx context.Context, vn core.VirtualNetworkClient, vcn core.Vcn) IPv6Step {
	slResp, err := vn.GetSecurityList(ctx, core.GetSecurityListRequest{SecurityListId: vcn.DefaultSecurityListId})
	if err != nil {
		return IPv6Step{Step: "seclist-ipv6-egress", Status: "failed", Detail: err.Error()}
	}
	for _, rule := range slResp.EgressSecurityRules {
		if deref(rule.Destination) == ipv6AnyCIDR {
			return IPv6Step{Step: "seclist-ipv6-egress", Status: "skipped", Detail: "::/0 egress exists"}
		}
	}
	dest := ipv6AnyCIDR
	protocol := "all"
	rules := append(slResp.EgressSecurityRules, core.EgressSecurityRule{
		Protocol:        &protocol,
		Destination:     &dest,
		DestinationType: core.EgressSecurityRuleDestinationTypeCidrBlock,
	})
	_, err = vn.UpdateSecurityList(ctx, core.UpdateSecurityListRequest{
		SecurityListId:            vcn.DefaultSecurityListId,
		UpdateSecurityListDetails: core.UpdateSecurityListDetails{EgressSecurityRules: rules},
	})
	if err != nil {
		return IPv6Step{Step: "seclist-ipv6-egress", Status: "failed", Detail: err.Error()}
	}
	return IPv6Step{Step: "seclist-ipv6-egress", Status: "done", Detail: "egress ::/0 allowed"}
}
