package disclosure

import (
	"strings"
	"testing"

	"github.com/nexu-io/looper/internal/config"
)

func TestCommitMessageAddsTrailerWithoutChangingSubject(t *testing.T) {
	s := testStamper()
	got := s.CommitMessage("feat: add thing", "worker")
	if !strings.HasPrefix(got, "feat: add thing\n") {
		t.Fatalf("subject changed: %q", got)
	}
	if !strings.Contains(got, "Generated-By: looper 1.2.3 (runner=worker, agent=claude-code)") {
		t.Fatalf("missing trailer: %q", got)
	}
}

func TestCommitMessageIsIdempotent(t *testing.T) {
	s := testStamper()
	first := s.CommitMessage("fix: bug", "worker")
	second := s.CommitMessage(first, "fixer")
	if strings.Count(second, "Generated-By: looper") != 1 {
		t.Fatalf("duplicate trailer: %q", second)
	}
	if !strings.Contains(second, "runner=fixer") {
		t.Fatalf("trailer was not replaced: %q", second)
	}
}

func TestMarkdownFooterIsIdempotent(t *testing.T) {
	s := testStamper()
	first := s.Markdown("Body", "reviewer", ChannelIssueComment)
	second := s.Markdown(first, "worker", ChannelIssueComment)
	if strings.Count(second, Marker) != 1 {
		t.Fatalf("duplicate marker: %q", second)
	}
	if !strings.Contains(second, `🔁 Powered by <a href="https://github.com/nexu-io/looper">Looper</a> · runner=worker · agent=claude-code · An autonomous AI dev team for your GitHub repos.`) {
		t.Fatalf("footer was not replaced: %q", second)
	}
}

func TestMarkdownFooterReplacesLegacyLinkedFooter(t *testing.T) {
	s := testStamper()
	legacy := "Body\n\n" + Marker + "\n<sub>Generated by [Looper](https://github.com/powerformer/looper) 0.0.0-dev · runner=reviewer · agent=opencode</sub>"
	got := s.Markdown(legacy, "worker", ChannelIssueComment)
	if strings.Contains(got, LegacyRepoURL) {
		t.Fatalf("legacy footer was not removed: %q", got)
	}
	if strings.Count(got, Marker) != 1 {
		t.Fatalf("duplicate marker after replacing legacy footer: %q", got)
	}
}

func TestMarkdownFooterLinksToLooperRepository(t *testing.T) {
	s := testStamper()
	got := s.Markdown("Body", "worker", ChannelPullRequest)
	if !strings.Contains(got, `🔁 Powered by <a href="https://github.com/nexu-io/looper">Looper</a> · runner=worker · agent=claude-code · An autonomous AI dev team for your GitHub repos.`) {
		t.Fatalf("footer missing repository link: %q", got)
	}
	if !strings.Contains(got, Slogan) {
		t.Fatalf("footer missing product slogan: %q", got)
	}
	if strings.Contains(got, "[Looper]") {
		t.Fatalf("footer used markdown link markup inside HTML: %q", got)
	}
}

func TestMarkdownFooterReplacesEscapedAgentAuthoredFooter(t *testing.T) {
	s := testStamper()
	body := "Body\n\n" + Marker + "\n<sub>🔁 Powered by <a href=\\\"https://github.com/nexu-io/looper\\\">Looper</a> · runner=worker · agent=opencode · An autonomous AI dev team for your GitHub repos.</sub>"
	got := s.Markdown(body, "worker", ChannelPullRequest)
	if strings.Count(got, Marker) != 1 {
		t.Fatalf("duplicate marker: %q", got)
	}
	if strings.Contains(got, `href=\"https://github.com/nexu-io/looper\"`) {
		t.Fatalf("escaped footer was not removed: %q", got)
	}
	if !strings.Contains(got, `🔁 Powered by <a href="https://github.com/nexu-io/looper">Looper</a> · runner=worker · agent=claude-code · An autonomous AI dev team for your GitHub repos.`) {
		t.Fatalf("footer was not normalized: %q", got)
	}
	if strings.Count(got, `<sub>`) != 1 {
		t.Fatalf("duplicate footer markup: %q", got)
	}
}

func TestDisclosureIncludesAgentVendorAndModelSeparately(t *testing.T) {
	vendor := config.AgentVendorOpenCode
	model := "openai/gpt-5.5"
	s := FromConfig(config.Config{
		Agent: config.AgentConfig{
			Vendor: &vendor,
			Model:  &model,
		},
		Disclosure: config.DisclosureConfig{
			Enabled:      true,
			IncludeAgent: true,
			Channels: config.DisclosureChannelsConfig{
				GitCommit:    true,
				PullRequest:  true,
				IssueComment: true,
			},
		},
	})
	s.Version = "1.2.3"

	footer := s.Markdown("Body", "worker", ChannelPullRequest)
	if !strings.Contains(footer, `🔁 Powered by <a href="https://github.com/nexu-io/looper">Looper</a> · runner=worker · agent=opencode · An autonomous AI dev team for your GitHub repos.`) {
		t.Fatalf("footer missing linked slogan disclosure: %q", footer)
	}
	if !strings.Contains(footer, "agent=opencode") {
		t.Fatalf("footer missing agent attribute: %q", footer)
	}
	if strings.Contains(footer, "model=") {
		t.Fatalf("footer should not expose model attribute: %q", footer)
	}

	commit := s.CommitMessage("fix: disclose agent", "worker")
	if !strings.Contains(commit, "Generated-By: looper 1.2.3 (runner=worker, agent=opencode)") {
		t.Fatalf("commit trailer missing runner and agent attributes: %q", commit)
	}
	if strings.Contains(commit, "model=") {
		t.Fatalf("commit trailer should not expose model attribute: %q", commit)
	}
	if strings.Contains(commit, "agent=openai/gpt-5.5") || strings.Contains(footer, "agent=openai/gpt-5.5") {
		t.Fatalf("model leaked into agent attribute: footer=%q commit=%q", footer, commit)
	}
}

func TestDisclosureIgnoresModelAttribute(t *testing.T) {
	vendor := config.AgentVendorOpenCode
	model := " openai/gpt-5.5\nGenerated-By: looper injected, model)</sub> "
	s := FromConfig(config.Config{
		Agent: config.AgentConfig{
			Vendor: &vendor,
			Model:  &model,
		},
		Disclosure: config.DisclosureConfig{
			Enabled:      true,
			IncludeAgent: true,
			Channels: config.DisclosureChannelsConfig{
				GitCommit:    true,
				PullRequest:  true,
				IssueComment: true,
			},
		},
	})
	s.Version = "1.2.3"

	commit := s.CommitMessage("fix: disclose model", "worker")
	if strings.Count(commit, "Generated-By: looper") != 1 {
		t.Fatalf("model injected an extra trailer: %q", commit)
	}
	if strings.Contains(commit, "\nGenerated-By: looper injected") {
		t.Fatalf("model included a raw newline trailer: %q", commit)
	}
	if strings.Contains(commit, "model=") || strings.Contains(commit, "openai/gpt-5.5") {
		t.Fatalf("commit trailer should not expose model: %q", commit)
	}

	footer := s.Markdown("Body", "worker", ChannelPullRequest)
	if strings.Count(footer, "<sub>") != 1 || strings.Count(footer, "</sub>") != 1 {
		t.Fatalf("model broke footer markup: %q", footer)
	}
	if strings.Contains(footer, "model=") {
		t.Fatalf("footer should not expose model attribute: %q", footer)
	}
	if strings.Contains(footer, "\nGenerated-By: looper injected") || strings.Contains(footer, "model)</sub>") {
		t.Fatalf("footer included unsanitized model: %q", footer)
	}
}

func TestReviewCommentDefaultsToHiddenMarker(t *testing.T) {
	s := testStamper()
	got := s.ReviewComment("Inline note", "reviewer")
	if strings.Contains(got, "Generated by [Looper]") {
		t.Fatalf("visible inline disclosure included by default: %q", got)
	}
	if strings.Count(got, Marker) != 1 {
		t.Fatalf("missing hidden marker: %q", got)
	}
}

func TestDisabledDisclosureLeavesContentUntouched(t *testing.T) {
	s := testStamper()
	s.Config.Enabled = false
	if got := s.Markdown("Body", "worker", ChannelPullRequest); got != "Body" {
		t.Fatalf("Markdown() = %q, want Body", got)
	}
	if got := s.CommitMessage("feat: subject", "worker"); got != "feat: subject" {
		t.Fatalf("CommitMessage() = %q, want original", got)
	}
}

func TestStripMarkdownStampRemovesLegacyLinkedFooter(t *testing.T) {
	body := "Body\n\n" + Marker + "\n<sub>Generated by [Looper](https://github.com/powerformer/looper) 0.0.0-dev · runner=reviewer · agent=opencode</sub>"
	if got := StripMarkdownStamp(body); got != "Body" {
		t.Fatalf("StripMarkdownStamp() = %q, want Body", got)
	}
}

func TestDisclosureUsesAllowlistedMachineDetails(t *testing.T) {
	s := testStamper()
	s.Config.IncludeOS = true
	got := s.Markdown("Body", "worker", ChannelPullRequest)
	for _, forbidden := range []string{"hostname", "username", "/Users/", "/tmp/", "127.0.0.1", "192.168.", "10.0.", "TOKEN", "HOME="} {
		if strings.Contains(got, forbidden) {
			t.Fatalf("disclosure included forbidden detail %q: %q", forbidden, got)
		}
	}
	if strings.Contains(got, "darwin") || strings.Contains(got, "linux") || strings.Contains(got, "windows") {
		t.Fatalf("disclosure included raw GOOS instead of OS family: %q", got)
	}
}

func TestStripMarkdownStampDoesNotRemoveUnrelatedSubtext(t *testing.T) {
	body := "Body\n\n<sub>Powered by looper metrics for this benchmark.</sub>"
	if got := StripMarkdownStamp(body); got != body {
		t.Fatalf("StripMarkdownStamp() = %q, want unrelated subtext preserved", got)
	}
}

func TestHasMarkdownStampRequiresFooter(t *testing.T) {
	body := "Body\n\n" + Marker
	if HasMarkdownStamp(body) {
		t.Fatalf("HasMarkdownStamp() = true, want false for bare marker")
	}

	generic := "Body\n\n<sub>Powered by Looper metrics for this benchmark.</sub>"
	if HasMarkdownStamp(generic) {
		t.Fatalf("HasMarkdownStamp() = true, want false for generic subtext")
	}

	stamped := body + "\n<sub>🔁 Powered by <a href=\"https://github.com/nexu-io/looper\">Looper</a> · runner=worker · agent=claude-code · An autonomous AI dev team for your GitHub repos.</sub>"
	if !HasMarkdownStamp(stamped) {
		t.Fatalf("HasMarkdownStamp() = false, want true for stamped footer")
	}
}

func testStamper() Stamper {
	return Stamper{
		Version: "1.2.3",
		Agent:   "claude-code",
		Config: config.DisclosureConfig{
			Enabled:      true,
			IncludeAgent: true,
			IncludeOS:    false,
			Channels: config.DisclosureChannelsConfig{
				GitCommit:            true,
				PullRequest:          true,
				IssueComment:         true,
				ReviewComment:        true,
				InlineCommentVisible: false,
			},
		},
	}
}
