package cli

import (
	"bytes"
	"context"
	"errors"
	"strings"
	"testing"

	"git.cloudinit.dev/coreci/orca/internal/emitter"
)

func TestNftCmd_Registered(t *testing.T) {
	found := false
	for _, c := range rootCmd.Commands() {
		if c.Name() == "nft" {
			found = true
			break
		}
	}
	if !found {
		t.Fatal("nft command not registered on root")
	}
}

func TestNftShowCmd(t *testing.T) {
	_, cleanup := initTestEnv(t)
	defer cleanup()
	origTransport := nftTransportOverride
	origPeer := nftLeadPeerOverride
	defer func() {
		nftTransportOverride = origTransport
		nftLeadPeerOverride = origPeer
	}()
	nftLeadPeerOverride = "lead"
	want := "table inet orca-ingress { dnat }"
	nftTransportOverride = &mockNftTransport{
		execOut: map[string][]byte{
			"nft list table inet orca-ingress": []byte(want),
		},
	}
	resetRootFlags(t)
	var buf bytes.Buffer
	rootCmd.SetOut(&buf)
	rootCmd.SetErr(&buf)
	rootCmd.SetArgs([]string{"nft", "show"})
	if err := rootCmd.Execute(); err != nil {
		t.Fatalf("nft show: %v", err)
	}
	if !strings.Contains(buf.String(), want) {
		t.Errorf("nft show output missing %q:\n%s", want, buf.String())
	}
}

func TestNftShowCmd_PeerFlag(t *testing.T) {
	_, cleanup := initTestEnv(t)
	defer cleanup()
	origTransport := nftTransportOverride
	defer func() { nftTransportOverride = origTransport }()
	mt := &mockNftTransport{
		execOut: map[string][]byte{"nft list table inet orca-ingress": []byte("ok")},
	}
	nftTransportOverride = mt
	resetRootFlags(t)
	var buf bytes.Buffer
	rootCmd.SetOut(&buf)
	rootCmd.SetErr(&buf)
	rootCmd.SetArgs([]string{"nft", "show", "--peer", "node-2"})
	if err := rootCmd.Execute(); err != nil {
		t.Fatalf("nft show: %v", err)
	}
}

func TestNftDiffCmd_NoDrift(t *testing.T) {
	_, cleanup := initTestEnv(t)
	defer cleanup()
	origTransport := nftTransportOverride
	origPeer := nftLeadPeerOverride
	defer func() {
		nftTransportOverride = origTransport
		nftLeadPeerOverride = origPeer
	}()
	nftLeadPeerOverride = "lead"
	// Use the full default nft render so the diff detects no drift
	rendered, rerr := emitter.NftEmitter{}.RenderNftConfig(emitter.NftClusterConfig{})
	if rerr != nil {
		t.Fatalf("render: %v", rerr)
	}
	live := string(rendered[0].Content)
	nftTransportOverride = &mockNftTransport{
		execOut: map[string][]byte{
			"nft list table inet orca-ingress": []byte(live),
		},
	}
	resetRootFlags(t)
	var buf bytes.Buffer
	rootCmd.SetOut(&buf)
	rootCmd.SetErr(&buf)
	rootCmd.SetArgs([]string{"nft", "diff", "--against", "T-abcdef0123456789"})
	if err := rootCmd.Execute(); err != nil {
		t.Fatalf("nft diff: %v", err)
	}
	if !strings.Contains(buf.String(), "no drift") {
		t.Errorf("expected no drift, got:\n%s", buf.String())
	}
}

func TestNftDiffCmd_RequiresAgainst(t *testing.T) {
	_, cleanup := initTestEnv(t)
	defer cleanup()
	resetRootFlags(t)
	var buf bytes.Buffer
	rootCmd.SetOut(&buf)
	rootCmd.SetErr(&buf)
	rootCmd.SetArgs([]string{"nft", "diff"})
	if err := rootCmd.Execute(); err == nil {
		t.Fatal("expected error for missing --against, got nil")
	}
}

func TestNftRateLimitSetCmd(t *testing.T) {
	_, cleanup := initTestEnv(t)
	defer cleanup()
	origTransport := nftTransportOverride
	origPeer := nftLeadPeerOverride
	defer func() {
		nftTransportOverride = origTransport
		nftLeadPeerOverride = origPeer
	}()
	nftLeadPeerOverride = "lead"
	mt := &mockNftTransport{
		execOut: map[string][]byte{},
	}
	nftTransportOverride = mt
	resetRootFlags(t)
	var buf bytes.Buffer
	rootCmd.SetOut(&buf)
	rootCmd.SetErr(&buf)
	rootCmd.SetArgs([]string{"nft", "rate", "limit", "set", "--rate", "250"})
	if err := rootCmd.Execute(); err != nil {
		t.Fatalf("nft rate limit set: %v", err)
	}
	if !strings.Contains(buf.String(), "rate-limit set to 250/s burst 500") {
		t.Errorf("unexpected output:\n%s", buf.String())
	}
}

func TestNftRateLimitSetCmd_RequiresRate(t *testing.T) {
	_, cleanup := initTestEnv(t)
	defer cleanup()
	resetRootFlags(t)
	var buf bytes.Buffer
	rootCmd.SetOut(&buf)
	rootCmd.SetErr(&buf)
	rootCmd.SetArgs([]string{"nft", "rate", "limit", "set"})
	if err := rootCmd.Execute(); err == nil {
		t.Fatal("expected error for missing --rate, got nil")
	}
}

func TestNftCountryBlockAddCmd(t *testing.T) {
	_, cleanup := initTestEnv(t)
	defer cleanup()
	origTransport := nftTransportOverride
	origPeer := nftLeadPeerOverride
	defer func() {
		nftTransportOverride = origTransport
		nftLeadPeerOverride = origPeer
	}()
	nftLeadPeerOverride = "lead"
	mt := &mockNftTransport{execOut: map[string][]byte{}}
	nftTransportOverride = mt
	resetRootFlags(t)
	var buf bytes.Buffer
	rootCmd.SetOut(&buf)
	rootCmd.SetErr(&buf)
	rootCmd.SetArgs([]string{"nft", "country", "block", "add", "RU,CN"})
	if err := rootCmd.Execute(); err != nil {
		t.Fatalf("nft country block add: %v", err)
	}
	if !strings.Contains(buf.String(), "added 2 country code(s)") {
		t.Errorf("unexpected output:\n%s", buf.String())
	}
}

func TestNftCountryBlockAddCmd_BadCode(t *testing.T) {
	_, cleanup := initTestEnv(t)
	defer cleanup()
	resetRootFlags(t)
	var buf bytes.Buffer
	rootCmd.SetOut(&buf)
	rootCmd.SetErr(&buf)
	rootCmd.SetArgs([]string{"nft", "country", "block", "add", "USA"})
	if err := rootCmd.Execute(); err == nil {
		t.Fatal("expected error for bad country code, got nil")
	}
}

func TestNftDoctorAliasCmd(t *testing.T) {
	_, cleanup := initTestEnv(t)
	defer cleanup()
	origTransport := nftTransportOverride
	origPeer := nftLeadPeerOverride
	defer func() {
		nftTransportOverride = origTransport
		nftLeadPeerOverride = origPeer
	}()
	nftLeadPeerOverride = "lead"
	nftTransportOverride = &mockNftTransport{
		execOut: map[string][]byte{
			"nft list table inet orca-ingress": []byte("dnat to 127.0.0.1:8443 dnat to 127.0.0.1:8080 ora_rl"),
		},
	}
	resetRootFlags(t)
	var buf bytes.Buffer
	rootCmd.SetOut(&buf)
	rootCmd.SetErr(&buf)
	rootCmd.SetArgs([]string{"nft", "doctor"})
	if err := rootCmd.Execute(); err != nil {
		t.Fatalf("nft doctor: %v", err)
	}
	if !strings.Contains(buf.String(), "nft:table") {
		t.Errorf("nft doctor alias did not run doctor nft checks:\n%s", buf.String())
	}
}

func TestNftShowCmd_ExecError(t *testing.T) {
	_, cleanup := initTestEnv(t)
	defer cleanup()
	origTransport := nftTransportOverride
	origPeer := nftLeadPeerOverride
	defer func() {
		nftTransportOverride = origTransport
		nftLeadPeerOverride = origPeer
	}()
	nftLeadPeerOverride = "lead"
	nftTransportOverride = &mockNftTransport{
		execErr: map[string]error{
			"nft list table inet orca-ingress": errors.New("connection refused"),
		},
	}
	resetRootFlags(t)
	var buf bytes.Buffer
	rootCmd.SetOut(&buf)
	rootCmd.SetErr(&buf)
	rootCmd.SetArgs([]string{"nft", "show"})
	if err := rootCmd.Execute(); err == nil {
		t.Fatal("expected error from exec failure, got nil")
	}
}

var _ context.Context = context.Background()
