package service

import (
	"testing"

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

// The node-page client breakdown must classify by EMAIL, not by
// client_traffics.inbound_id — that column goes stale after an inbound is
// delete+recreated, so filtering by it drops almost every row and undercounts
// active/disabled/ended. Here the traffic rows carry a bogus inbound_id (999)
// yet must still be matched to the node's clients by email.
func TestGetAll_ClientBreakdownMatchesByEmailNotStaleInboundId(t *testing.T) {
	setupConflictDB(t)
	db := database.GetDB()
	svc := NodeService{}

	if err := db.Create(&model.Node{Id: 1, Name: "n", Address: "10.0.0.1", Port: 2053, ApiToken: "t", Guid: "g"}).Error; err != nil {
		t.Fatalf("create node: %v", err)
	}
	nid := 1
	ib := &model.Inbound{Tag: "n1-in", Enable: true, Port: 443, Protocol: model.VLESS, Settings: `{"clients":[]}`, NodeID: &nid}
	if err := db.Create(ib).Error; err != nil {
		t.Fatalf("create inbound: %v", err)
	}

	mk := func(id int, email string, enable bool) {
		if err := db.Create(&model.ClientRecord{Id: id, Email: email, Enable: enable}).Error; err != nil {
			t.Fatalf("create client %s: %v", email, err)
		}
		if !enable {
			// Enable has gorm:"default:true", so a zero-value (false) create is
			// dropped and the DB applies true — force the disabled state explicitly.
			if err := db.Model(&model.ClientRecord{}).Where("id = ?", id).Update("enable", false).Error; err != nil {
				t.Fatalf("disable client %s: %v", email, err)
			}
		}
		if err := db.Create(&model.ClientInbound{ClientId: id, InboundId: ib.Id}).Error; err != nil {
			t.Fatalf("attach client %s: %v", email, err)
		}
	}
	mk(1, "active@x", true)
	mk(2, "disabled@x", false)
	mk(3, "depleted@x", true)

	// Traffic rows carry a STALE inbound_id (999), unrelated to the node inbound.
	const staleInboundID = 999
	rows := []*xray.ClientTraffic{
		{InboundId: staleInboundID, Email: "active@x", Enable: true},
		{InboundId: staleInboundID, Email: "disabled@x", Enable: false},
		{InboundId: staleInboundID, Email: "depleted@x", Enable: true, Total: 100, Up: 60, Down: 60}, // exhausted
	}
	for _, r := range rows {
		if err := db.Create(r).Error; err != nil {
			t.Fatalf("create traffic %s: %v", r.Email, err)
		}
	}

	nodes, err := svc.GetAll()
	if err != nil {
		t.Fatalf("GetAll: %v", err)
	}
	var n *model.Node
	for _, x := range nodes {
		if x.Id == 1 {
			n = x
		}
	}
	if n == nil {
		t.Fatal("node 1 not found")
		return
	}
	if n.ClientCount != 3 {
		t.Errorf("ClientCount = %d, want 3", n.ClientCount)
	}
	if n.ActiveCount != 1 {
		t.Errorf("ActiveCount = %d, want 1 (matched by email despite stale inbound_id)", n.ActiveCount)
	}
	if n.DisabledCount != 1 {
		t.Errorf("DisabledCount = %d, want 1", n.DisabledCount)
	}
	if n.DepletedCount != 1 {
		t.Errorf("DepletedCount = %d, want 1", n.DepletedCount)
	}
}
