// MCP form command quoting, environment parsing, and credential retention semantics.
// Wire-format command tokenization and sentinel deletion operate inside unexported form
// submission models before reaching any daemon RPC, unreachable by daemon E2E.
package tui

import (
	"strings"
	"testing"

	"albedo/cli/internal/config"
	"albedo/cli/internal/daemon"

	tea "charm.land/bubbletea/v2"
	"github.com/charmbracelet/x/ansi"
)

func typeText(m CapabilityPageModel, text string) CapabilityPageModel {
	m, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyExtended, Text: text})
	return m
}

func key(m CapabilityPageModel, k rune) CapabilityPageModel {
	m, _ = m.Update(tea.KeyPressMsg{Code: k})
	return m
}

func mcpPage(t *testing.T) CapabilityPageModel {
	t.Helper()
	m := NewCapabilityPageModel(nil, "s", "mcp")
	m.SetSize(100, 30)
	m.Loading = false
	m.ExtensionEnabled = true
	return m
}

func TestAddingAnHTTPServerAsksForAuthBeforeSaving(t *testing.T) {
	m := mcpPage(t)
	m = typeText(m, "n")
	if m.Form == nil || m.Form.current() != fieldURL {
		t.Fatal("n should open the add form on the URL")
	}
	m = typeText(m, "http://100.64.0.19:8787/mcp")
	if got := m.Form.Inputs[fieldName].Value(); got != "mcp-100-64-0-19" {
		t.Fatalf("name should follow the URL, got %q", got)
	}
	m = key(m, tea.KeyEnter) // name
	m = key(m, tea.KeyEnter) // bearer token
	if m.Form.current() != fieldToken || m.Saving {
		t.Fatalf("enter should walk to the bearer token without saving, at %q", m.Form.current())
	}
	m = typeText(m, "sensitive-token")
	if strings.Contains(ansi.Strip(m.View()), "sensitive-token") || !strings.Contains(ansi.Strip(m.View()), "bearer token") {
		t.Fatalf("the token field must be labelled and masked:\n%s", ansi.Strip(m.View()))
	}
	sub, err := m.Form.submission(nil)
	if err != nil {
		t.Fatal(err)
	}
	if sub.Name != "mcp-100-64-0-19" || sub.Server.Type != "http" || sub.Server.URL != "http://100.64.0.19:8787/mcp" || (sub.Secrets.BearerToken == nil || *sub.Secrets.BearerToken != "sensitive-token") {
		t.Fatalf("unexpected submission %+v", sub)
	}
}

func TestStdioServerParsesItsCommandAndSuggestsAName(t *testing.T) {
	m := mcpPage(t)
	m = typeText(m, "n")
	m = key(m, tea.KeyUp)
	m = key(m, tea.KeyRight)
	if m.Form.Transport != "stdio" {
		t.Fatal("← → should switch the transport")
	}
	m = key(m, tea.KeyDown)
	m = typeText(m, `npx -y @modelcontextprotocol/server-filesystem "/tmp/my dir"`)
	m = key(m, tea.KeyDown)
	m = key(m, tea.KeyDown)
	m = typeText(m, "TOKEN=abc DEBUG=1")
	sub, err := m.Form.submission(nil)
	if err != nil {
		t.Fatal(err)
	}
	if sub.Name != "filesystem" || sub.Server.Command != "npx" || strings.Join(sub.Server.Args, "|") != "-y|@modelcontextprotocol/server-filesystem|/tmp/my dir" {
		t.Fatalf("unexpected stdio submission %+v", sub)
	}
	env := sub.Secrets.Env
	if env["TOKEN"] == nil || *env["TOKEN"] != "abc" || env["DEBUG"] == nil || *env["DEBUG"] != "1" || !sub.Secrets.RemoveBearerToken {
		t.Fatalf("env should be stored privately, got %+v", sub.Secrets)
	}
	if strings.Contains(ansi.Strip(m.View()), "abc") {
		t.Fatal("env values must be masked")
	}
}

func TestEditingKeepsOrRemovesStoredSecrets(t *testing.T) {
	stored := daemon.MCPSecretNames{BearerToken: true, Headers: []string{"X-Key"}}
	enabled := false
	f := newMCPForm("docs", config.MCPServer{Type: "http", URL: "https://docs.example/mcp", Enabled: &enabled}, stored)
	sub, err := f.submission([]capabilityItem{{ID: "docs"}})
	if err != nil || sub.Secrets.BearerToken != nil || sub.Secrets.RemoveBearerToken || sub.Secrets.Headers != nil || sub.Secrets.ClearHeaders || sub.Server.Enabled == nil {
		t.Fatalf("a blank edit must keep stored secrets and other fields: %+v %v", sub, err)
	}

	f.Inputs[fieldToken].SetValue("-")
	f.Inputs[fieldHeader].SetValue("X-Key")
	f.Inputs[fieldValue].SetValue("-")
	sub, _ = f.submission(nil)
	removed := sub.Secrets.Headers
	header, named := removed["X-Key"]
	if !sub.Secrets.RemoveBearerToken || !named || header != nil {
		t.Fatalf("- should remove stored secrets: %+v", sub.Secrets)
	}
}
