package sub

import (
	"strings"
	"testing"

	"github.com/mhsanaei/3x-ui/v3/internal/database/model"
)

// Issue #5232: a vision flow set on a VLESS+XHTTP+REALITY (vlessenc) client
// must survive into subscription output, not just the inbound JSON.

const testMlkemEncryption = "mlkem768x25519plus.native.0rtt.dGVzdC1rZXk"

func TestVlessFlowAllowed(t *testing.T) {
	enc := map[string]any{"encryption": testMlkemEncryption}
	noEnc := map[string]any{"encryption": "none"}

	tests := []struct {
		name     string
		network  string
		security string
		settings map[string]any
		want     bool
	}{
		{"tcp tls", "tcp", "tls", noEnc, true},
		{"tcp reality", "tcp", "reality", noEnc, true},
		{"tcp none", "tcp", "none", noEnc, false},
		{"tcp none vlessenc", "tcp", "none", enc, false},
		{"xhttp none vlessenc", "xhttp", "none", enc, true},
		{"xhttp reality vlessenc (#5232)", "xhttp", "reality", enc, true},
		{"xhttp tls vlessenc", "xhttp", "tls", enc, true},
		{"xhttp reality no vlessenc", "xhttp", "reality", noEnc, false},
		{"ws tls", "ws", "tls", noEnc, false},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			if got := vlessFlowAllowed(tc.network, tc.security, tc.settings); got != tc.want {
				t.Fatalf("vlessFlowAllowed(%q, %q, %v) = %v, want %v", tc.network, tc.security, tc.settings, got, tc.want)
			}
		})
	}
}

func flowTestInbound(streamSettings, encryption string) *model.Inbound {
	return &model.Inbound{
		Listen:   "203.0.113.1",
		Port:     443,
		Protocol: model.VLESS,
		Remark:   "flowtest",
		Settings: `{"clients":[{"id":"11111111-2222-4333-8444-555555555555","email":"user","flow":"xtls-rprx-vision"}],` +
			`"decryption":"` + encryption + `","encryption":"` + encryption + `"}`,
		StreamSettings: streamSettings,
	}
}

const xhttpRealityStream = `{
	"network": "xhttp",
	"security": "reality",
	"xhttpSettings": {"path": "/", "mode": "auto"},
	"realitySettings": {
		"serverNames": ["example.com"],
		"shortIds": ["abcd"],
		"settings": {"publicKey": "pub", "fingerprint": "chrome"}
	}
}`

func TestGenVlessLink_FlowXhttpRealityVlessenc(t *testing.T) {
	s := &SubService{}
	link := s.genVlessLink(flowTestInbound(xhttpRealityStream, testMlkemEncryption), "user")
	if !strings.Contains(link, "flow=xtls-rprx-vision") {
		t.Fatalf("xhttp+reality+vlessenc link must carry the vision flow (#5232), got %q", link)
	}
}

func TestGenVlessLink_NoFlowXhttpRealityWithoutVlessenc(t *testing.T) {
	s := &SubService{}
	link := s.genVlessLink(flowTestInbound(xhttpRealityStream, "none"), "user")
	if strings.Contains(link, "flow=") {
		t.Fatalf("xhttp+reality without vlessenc must not carry a flow, got %q", link)
	}
}

func TestGenVlessLink_DisableFlowSuppressesFlow(t *testing.T) {
	s := &SubService{}
	ib := flowTestInbound(xhttpRealityStream, testMlkemEncryption)
	ib.DisableFlow = true
	link := s.genVlessLink(ib, "user")
	if strings.Contains(link, "flow=") {
		t.Fatalf("DisableFlow inbound must not carry a flow even when the transport is capable, got %q", link)
	}
}

func TestGenVlessLink_FlowTcpRealityStillWorks(t *testing.T) {
	stream := `{
		"network": "tcp",
		"security": "reality",
		"tcpSettings": {"header": {"type": "none"}},
		"realitySettings": {
			"serverNames": ["example.com"],
			"shortIds": ["abcd"],
			"settings": {"publicKey": "pub", "fingerprint": "chrome"}
		}
	}`
	s := &SubService{}
	link := s.genVlessLink(flowTestInbound(stream, "none"), "user")
	if !strings.Contains(link, "flow=xtls-rprx-vision") {
		t.Fatalf("tcp+reality link must keep the vision flow, got %q", link)
	}
}
