package cmd

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/desktop/internal/tokenanalysis"
)

func TestRootExposesOnlyTokenCommands(t *testing.T) {
	names := make(map[string]bool)
	for _, command := range rootCmd.Commands() {
		names[command.Name()] = true
	}
	require.True(t, names["token"])

	subcommands := make(map[string]bool)
	for _, command := range tokenCmd.Commands() {
		subcommands[command.Name()] = true
	}
	require.Equal(t, map[string]bool{"analyze": true, "compare": true, "ui": true}, subcommands)
}

func TestTokenAnalyzeFailsClosedWhenDaemonUnavailable(t *testing.T) {
	previous := newTokenAnalysisClient
	newTokenAnalysisClient = func() (tokenanalysis.Client, bool, error) {
		return tokenanalysis.Client{}, false, errors.New("daemon_unavailable")
	}
	t.Cleanup(func() { newTokenAnalysisClient = previous })

	command := newTokenAnalyzeCommand()
	var output bytes.Buffer
	command.SetOut(&output)
	command.SetErr(&output)
	command.SetArgs(nil)
	err := command.Execute()
	require.EqualError(t, err, "daemon_unavailable")
}

func TestTokenAnalyzeRendersAutoEnableAndProtectsArtifact(t *testing.T) {
	report := tokenCommandReport()
	client := tokenanalysis.Client{
		BaseURL: "http://127.0.0.1:43002",
		APIKey:  "daemon-key",
		HTTP: &http.Client{Transport: commandRoundTrip(func(request *http.Request) (*http.Response, error) {
			data, err := json.Marshal(report)
			require.NoError(t, err)
			return &http.Response{
				StatusCode: http.StatusOK,
				Header:     make(http.Header),
				Body:       io.NopCloser(bytes.NewReader(data)),
				Request:    request,
			}, nil
		})},
	}
	previous := newTokenAnalysisClient
	newTokenAnalysisClient = func() (tokenanalysis.Client, bool, error) { return client, false, nil }
	t.Cleanup(func() { newTokenAnalysisClient = previous })

	path := filepath.Join(t.TempDir(), "report.json")
	command := newTokenAnalyzeCommand()
	var output bytes.Buffer
	command.SetOut(&output)
	command.SetErr(&output)
	command.SetArgs([]string{"--output", path})
	require.NoError(t, command.Execute())
	require.Contains(t, output.String(), "已自动开启")

	info, err := os.Stat(path)
	require.NoError(t, err)
	require.Equal(t, os.FileMode(0o600), info.Mode().Perm())
	data, err := os.ReadFile(path)
	require.NoError(t, err)
	require.NotContains(t, string(data), "private-agent")
	require.NotContains(t, string(data), "gpt-private")
}

func TestTokenAnalyzeStopIsRejectedForAgentCaller(t *testing.T) {
	previous := newTokenAnalysisClient
	newTokenAnalysisClient = func() (tokenanalysis.Client, bool, error) {
		return tokenanalysis.Client{}, true, nil
	}
	t.Cleanup(func() { newTokenAnalysisClient = previous })

	command := newTokenAnalyzeCommand()
	command.SetArgs([]string{"--stop"})
	require.EqualError(t, command.Execute(), "Agent caller cannot change Token collection")
}

func TestTokenAnalyzeRequiresRunForDeclaredVariable(t *testing.T) {
	command := newTokenAnalyzeCommand()
	command.SetArgs([]string{"--changed-variable", "platform.agentteam.exec"})
	require.EqualError(t, command.Execute(), "--changed-variable requires --run")
}

func TestTokenAnalyzeAccepts24HourAliasAndForwardsExperimentMetadata(t *testing.T) {
	report := tokenCommandReport()
	var query string
	client := tokenanalysis.Client{
		BaseURL: "http://127.0.0.1:43002",
		APIKey:  "daemon-key",
		HTTP: &http.Client{Transport: commandRoundTrip(func(request *http.Request) (*http.Response, error) {
			query = request.URL.RawQuery
			data, err := json.Marshal(report)
			require.NoError(t, err)
			return &http.Response{
				StatusCode: http.StatusOK,
				Header:     make(http.Header),
				Body:       io.NopCloser(bytes.NewReader(data)),
				Request:    request,
			}, nil
		})},
	}
	previous := newTokenAnalysisClient
	newTokenAnalysisClient = func() (tokenanalysis.Client, bool, error) { return client, false, nil }
	t.Cleanup(func() { newTokenAnalysisClient = previous })

	command := newTokenAnalyzeCommand()
	command.SetOut(io.Discard)
	command.SetArgs([]string{
		"--window", "24h",
		"--run", "run_0000000000000001",
		"--case", "exec-case",
		"--changed-variable", "platform.agentteam.exec",
		"--workspace-revision", "revision",
		"--tool-profile", "profile",
		"--cache-state", "warm",
		"--outcome", "success",
	})
	require.NoError(t, command.Execute())
	require.Contains(t, query, "window=24h")
	require.Contains(t, query, "changedVariable=platform.agentteam.exec")
	require.Contains(t, query, "cacheState=warm")
	require.Contains(t, query, "outcome=success")
}

func TestTokenCompareRequiresBothArtifacts(t *testing.T) {
	command := newTokenCompareCommand()
	command.SetArgs([]string{"--baseline", "baseline.json"})
	require.EqualError(t, command.Execute(), "--baseline and --candidate are required")
}

type commandRoundTrip func(*http.Request) (*http.Response, error)

func (fn commandRoundTrip) RoundTrip(request *http.Request) (*http.Response, error) {
	return fn(request)
}

func tokenCommandReport() tokenanalysis.Report {
	service := tokenanalysis.New(tokenanalysis.Dependencies{
		Agents: func() []tokenanalysis.Agent {
			return []tokenanalysis.Agent{{
				ID: "agent-private", Name: "private-agent", Runtime: "codex", Model: "gpt-private",
			}}
		},
	})
	report, err := service.Analyze(tokenanalysis.Request{Window: "1d"}, true, false, "", true)
	if err != nil {
		panic(err)
	}
	service.SetCollectionEnabled(false)
	return report
}
