package kb

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"errors"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestAGSHTTPKBWikiReaderCallsAGSWikiPrimitives(t *testing.T) {
	seen := []string{}
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		require.Equal(t, "Bearer user", r.Header.Get("Authorization"))
		seen = append(seen, r.Method+" "+r.URL.Path)
		w.Header().Set("Content-Type", "application/json")
		switch r.URL.Path {
		case "/api/v3/repos/pingcap/agentteam/wiki/search":
			require.Equal(t, "daemon mcp", r.URL.Query().Get("q"))
			require.Equal(t, "7", r.URL.Query().Get("limit"))
			require.Equal(t, "3", r.URL.Query().Get("offset"))
			require.NoError(t, json.NewEncoder(w).Encode(map[string]any{"results": []map[string]any{{"slug": "wiki-mcp", "title": "Wiki MCP", "snippet": "AGS wiki", "score": 2.5}}}))
		case "/api/v3/repos/pingcap/agentteam/wiki/pages/wiki-mcp":
			require.Equal(t, "sha123", r.URL.Query().Get("ref"))
			require.NoError(t, json.NewEncoder(w).Encode(map[string]any{"slug": "wiki-mcp", "title": "Wiki MCP", "body": "content", "sha": "sha123"}))
		case "/api/v3/repos/pingcap/agentteam/wiki/tree":
			require.Equal(t, "docs", r.URL.Query().Get("path"))
			require.Equal(t, "sha123", r.URL.Query().Get("ref"))
			require.NoError(t, json.NewEncoder(w).Encode([]map[string]any{{"path": "docs/wiki-mcp.md", "name": "wiki-mcp.md", "kind": "page", "slug": "wiki-mcp", "title": "Wiki MCP", "sha": "sha-tree", "size": 42}}))
		default:
			http.NotFound(w, r)
		}
	})
	reader := AGSHTTPKBWikiReader{Handler: handler}

	search, err := reader.SearchWiki(context.Background(), KBWikiSearchRequest{AuthorizationHeader: "Bearer user", Owner: "pingcap", Repo: "agentteam", Query: "daemon mcp", Limit: 7, Offset: 3})
	require.NoError(t, err)
	require.Equal(t, "Wiki MCP", search.Results[0].Title)

	page, err := reader.ReadWikiPage(context.Background(), KBWikiReadRequest{AuthorizationHeader: "Bearer user", Owner: "pingcap", Repo: "agentteam", Slug: "wiki-mcp", Revision: "sha123"})
	require.NoError(t, err)
	require.Equal(t, "content", page.Body)

	tree, err := reader.ListWikiTree(context.Background(), KBWikiTreeRequest{AuthorizationHeader: "Bearer user", Owner: "pingcap", Repo: "agentteam", Path: "docs", Revision: "sha123"})
	require.NoError(t, err)
	require.Equal(t, "sha-tree", tree[0].SHA)
	require.Equal(t, []string{
		"GET /api/v3/repos/pingcap/agentteam/wiki/search",
		"GET /api/v3/repos/pingcap/agentteam/wiki/pages/wiki-mcp",
		"GET /api/v3/repos/pingcap/agentteam/wiki/tree",
	}, seen)
}

func TestKBWikiHTTPErrorTreatsAGSNotFoundAsNotFound(t *testing.T) {
	err := kbWikiHTTPError(http.StatusNotFound, "list wiki tree", []byte("not found"))

	require.Error(t, err)
	require.True(t, errors.Is(err, ErrNotFound))
	require.Equal(t, "not_found", Code(err))
	require.False(t, errors.Is(err, ErrInvalidArgument))
}

func TestAGSHTTPKBWikiReaderReadsRepositoryFilesForMirrorSource(t *testing.T) {
	seen := []string{}
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		require.Equal(t, "Bearer user", r.Header.Get("Authorization"))
		seen = append(seen, r.Method+" "+r.URL.Path)
		w.Header().Set("Content-Type", "application/json")
		switch r.URL.Path {
		case "/api/v3/search/code":
			require.Equal(t, "architecture repo:usr_1/agentteam", r.URL.Query().Get("q"))
			require.Equal(t, "1", r.URL.Query().Get("text_matches"))
			require.NoError(t, json.NewEncoder(w).Encode(map[string]any{
				"total_count": 4,
				"items": []map[string]any{
					{
						"path":       "private/secret.md",
						"score":      99.0,
						"repository": map[string]any{"full_name": "other/secret"},
					},
					{
						"path":       "docs/architecture.md:9",
						"score":      2.5,
						"repository": map[string]any{"full_name": "usr_1/agentteam"},
					},
					{
						"path":         "docs/architecture.md",
						"score":        1.0,
						"repository":   map[string]any{"full_name": "usr_1/agentteam"},
						"text_matches": []map[string]any{{"fragment": "# Architecture\nAgentTeam server and web layers"}},
					},
					{
						"path":         "docs/architecture.md:12:## Architecture",
						"score":        1.5,
						"repository":   map[string]any{"full_name": "usr_1/agentteam"},
						"text_matches": []map[string]any{{"fragment": "## Architecture"}},
					},
				},
			}))
		case "/api/v3/repos/usr_1/agentteam/contents/docs/architecture.md":
			require.Equal(t, "commit-sha", r.URL.Query().Get("ref"))
			require.NoError(t, json.NewEncoder(w).Encode(map[string]any{
				"type":     "file",
				"name":     "architecture.md",
				"path":     "docs/architecture.md",
				"sha":      "blob-sha",
				"encoding": "base64",
				"content":  base64.StdEncoding.EncodeToString([]byte("# Architecture\nAgentTeam server and web layers")),
			}))
		case "/api/v3/repos/usr_1/agentteam/contents/docs":
			require.Equal(t, "commit-sha", r.URL.Query().Get("ref"))
			require.NoError(t, json.NewEncoder(w).Encode([]map[string]any{
				{"type": "file", "name": "architecture.md", "path": "docs/architecture.md", "sha": "blob-sha", "size": 44},
				{"type": "dir", "name": "adr", "path": "docs/adr", "sha": "tree-sha", "size": 0},
			}))
		default:
			http.NotFound(w, r)
		}
	})
	reader := AGSHTTPKBWikiReader{Handler: handler}

	search, err := reader.SearchWiki(context.Background(), KBWikiSearchRequest{
		AuthorizationHeader: "Bearer user",
		Owner:               "usr_1",
		Repo:                "agentteam",
		RepoRole:            "mirror",
		Query:               "architecture",
		Limit:               7,
		Revision:            "commit-sha",
	})
	require.NoError(t, err)
	require.Len(t, search.Results, 1, "duplicate line matches for one file must collapse")
	require.Equal(t, "docs/architecture.md", search.Results[0].Slug)
	require.Equal(t, "docs/architecture.md", search.Results[0].Title)
	require.Equal(t, "commit-sha", search.Results[0].SHA)
	require.Contains(t, search.Results[0].Snippet, "AgentTeam server")

	page, err := reader.ReadWikiPage(context.Background(), KBWikiReadRequest{
		AuthorizationHeader: "Bearer user",
		Owner:               "usr_1",
		Repo:                "agentteam",
		RepoRole:            "mirror",
		Slug:                "docs/architecture.md",
		Revision:            "commit-sha",
	})
	require.NoError(t, err)
	require.Equal(t, "# Architecture\nAgentTeam server and web layers", page.Body)
	require.Equal(t, "commit-sha", page.SHA, "citations must keep the commit ref, not an unreadable blob ref")

	tree, err := reader.ListWikiTree(context.Background(), KBWikiTreeRequest{
		AuthorizationHeader: "Bearer user",
		Owner:               "usr_1",
		Repo:                "agentteam",
		RepoRole:            "mirror",
		Path:                "docs",
		Revision:            "commit-sha",
	})
	require.NoError(t, err)
	require.Len(t, tree, 2)
	require.Equal(t, "docs/architecture.md", tree[0].Slug)
	require.Equal(t, "commit-sha", tree[0].SHA)
	require.True(t, tree[1].IsDir)

	_, err = reader.ReadWikiPage(context.Background(), KBWikiReadRequest{
		AuthorizationHeader: "Bearer user",
		Owner:               "usr_1",
		Repo:                "agentteam",
		RepoRole:            "mirror",
		Slug:                "../secret.md",
	})
	require.Error(t, err)
	require.ErrorIs(t, err, ErrInvalidArgument)
	require.Equal(t, []string{
		"GET /api/v3/search/code",
		"GET /api/v3/repos/usr_1/agentteam/contents/docs/architecture.md",
		"GET /api/v3/repos/usr_1/agentteam/contents/docs",
	}, seen)
}

func TestScopedRepositoryCodeQueryCannotBroadenSourceScope(t *testing.T) {
	require.Equal(t,
		"architecture filename:README.md repo:usr_1/agentteam",
		scopedRepositoryCodeQuery("architecture repo:other/secret filename:README.md", "usr_1", "agentteam"),
	)
}
