package sink

import (
	"bytes"
	"context"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"incredigo/internal/discover"
)

// writeFakeGopass writes a stub `gopass` binary that stores entries as files under
// $FAKE_GOPASS_STORE, supporting the ls/show/insert subcommands the bundle code
// uses. This exercises the real framing + subprocess plumbing without touching the
// user's actual gopass store.
func writeFakeGopass(t *testing.T) string {
	t.Helper()
	bin := filepath.Join(t.TempDir(), "gopass")
	script := `#!/usr/bin/env bash
store="$FAKE_GOPASS_STORE"
case "$1" in
  ls) (cd "$store" 2>/dev/null && find . -type f | sed 's#^\./##' | sort) ;;
  show) cat "$store/$3" ;;
  insert)
    shift; path=""
    for a in "$@"; do case "$a" in --multiline=false|-f) ;; *) path="$a";; esac; done
    mkdir -p "$store/$(dirname "$path")"; cat > "$store/$path" ;;
esac
`
	if err := os.WriteFile(bin, []byte(script), 0o755); err != nil {
		t.Fatal(err)
	}
	return bin
}

func seed(t *testing.T, store string, entries map[string]string) {
	t.Helper()
	for path, secret := range entries {
		full := filepath.Join(store, path)
		if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(full, []byte(secret), 0o600); err != nil {
			t.Fatal(err)
		}
	}
}

func TestBundleRoundTrip(t *testing.T) {
	bin := writeFakeGopass(t)
	gp := &Gopass{Bin: bin}
	ctx := context.Background()

	src := t.TempDir()
	seed(t, src, map[string]string{
		"imported/aws/default": "AKIA/secret+with=specials",
		"imported/env/api":     "unicode-末-\U0001f511",
		"other/skip":           "should-not-export",
	})

	// Export the imported/ subtree to a framed (unsealed) buffer.
	t.Setenv("FAKE_GOPASS_STORE", src)
	var buf bytes.Buffer
	n, err := gp.ExportTo(ctx, &buf, "imported/")
	if err != nil {
		t.Fatal(err)
	}
	if n != 2 {
		t.Fatalf("exported %d entries, want 2 (other/ excluded by prefix)", n)
	}

	// Import into a fresh store and assert byte-exact restoration.
	dst := t.TempDir()
	t.Setenv("FAKE_GOPASS_STORE", dst)
	m, err := gp.ImportFrom(ctx, &buf, true)
	if err != nil {
		t.Fatal(err)
	}
	if m != 2 {
		t.Fatalf("imported %d entries, want 2", m)
	}

	want := map[string]string{
		"imported/aws/default": "AKIA/secret+with=specials",
		"imported/env/api":     "unicode-末-\U0001f511",
	}
	for path, exp := range want {
		got, err := os.ReadFile(filepath.Join(dst, path))
		if err != nil {
			t.Fatalf("restored %s: %v", path, err)
		}
		if string(got) != exp {
			t.Errorf("%s: got %q want %q", path, got, exp)
		}
	}
	if _, err := os.Stat(filepath.Join(dst, "other", "skip")); !os.IsNotExist(err) {
		t.Error("other/skip leaked across the prefix filter")
	}
}

func TestStorePathNoDoubleSegment(t *testing.T) {
	gp := &Gopass{Prefix: "imported/"}
	c := discover.Credential{Source: "file", Identity: "keys/deploy"}
	if got := gp.StorePath(c); got != "imported/file/keys/deploy" {
		t.Errorf("StorePath = %q, want imported/file/keys/deploy", got)
	}
}

// TestStorePathNoDotSegment guards Hard Rule #1: a dot-prefixed path segment
// (e.g. ".env") is hidden by gopass `ls --flat`, which would make the entry
// invisible to the backup gate's coverage check. slug() must strip leading
// dots so every stored credential is enumerable and therefore backed up.
func TestStorePathNoDotSegment(t *testing.T) {
	gp := &Gopass{Prefix: "imported/"}
	cases := []struct {
		identity string
		want     string
	}{
		{".env / API_TOKEN", "imported/env/env/api_token"},
		{".env.prod / DATABASE_URL", "imported/env/env.prod/database_url"},
		{".netrc / password", "imported/env/netrc/password"},
	}
	for _, tc := range cases {
		c := discover.Credential{Source: "env", Identity: tc.identity}
		got := gp.StorePath(c)
		if got != tc.want {
			t.Errorf("StorePath(%q) = %q, want %q", tc.identity, got, tc.want)
		}
		for _, seg := range strings.Split(got, "/") {
			if strings.HasPrefix(seg, ".") {
				t.Errorf("StorePath(%q) = %q has hidden dot-segment %q", tc.identity, got, seg)
			}
		}
	}
}
