package forwarder

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

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

func TestKnowledgeQARuntimeGatewayProjectsRequesterAndFixedEndpoint(t *testing.T) {
	var seenPath, seenAuthorization, seenWorkspace string
	var seenBody map[string]any
	upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		seenPath = r.URL.Path
		seenAuthorization = r.Header.Get("Authorization")
		seenWorkspace = r.Header.Get("X-Workspace-Id")
		require.NoError(t, json.NewDecoder(r.Body).Decode(&seenBody))
		_ = json.NewEncoder(w).Encode(KnowledgeQARuntimeResult{
			Status: "completed",
			Claims: []KnowledgeQARuntimeClaim{{
				Text: "Grounded [E1]", EvidenceIDs: []string{"E1"},
			}},
			Evidence: []KnowledgeQARuntimeEvidence{{
				ID: "E1", Citation: KnowledgeQARuntimeCitation{
					KnowledgeBaseID: "kb_1", SourceID: "src_1", RootKind: "primary",
					RepoRole: "primary", Revision: "sha_1", Path: "README.md",
					LineStart: 1, LineEnd: 2,
				},
			}},
			Roots:          []KnowledgeQARuntimeRoot{},
			RuntimeVersion: "knowledge-qa-v3", PromptVersion: "exploration-v2",
		})
	}))
	defer upstream.Close()

	gateway, err := NewKnowledgeQARuntimeGateway(Dependencies{
		BaseURL: upstream.URL, HTTPClient: upstream.Client(), TrustedJWTAudience: "meshx",
		Now: func() time.Time { return time.Unix(1700000000, 0) },
	})
	require.NoError(t, err)
	result, err := gateway.ExecuteKnowledgeQATurn(context.Background(), KnowledgeQARuntimeRequest{
		Principal: KnowledgeQARuntimePrincipal{
			OrganizationID: "org_1", WorkspaceID: "wsp_1", RequesterID: "usr_1", TurnID: "kqt_1",
		},
		Question: "hello", KnowledgeBaseIDs: []string{"kb_1"}, SourceIDs: []string{"src_1"},
		Model: "model-a", ReasonLevel: "medium", MaaSCapability: "cap", IdempotencyKey: "kqa_1",
	})
	require.NoError(t, err)
	require.Equal(t, "completed", result.Status)
	require.Equal(t, "Grounded [E1]", result.Claims[0].Text)
	require.Equal(t, "sha_1", result.Evidence[0].Citation.Revision)
	require.Equal(t, "/api/v1/kb/knowledge-qa/runtime/execute", seenPath)
	require.Equal(t, "wsp_1", seenWorkspace)
	claims := decodeClaims(t, strings.TrimPrefix(seenAuthorization, "Bearer "))
	require.Equal(t, "usr_1", claims["sub"])
	require.Equal(t, "kqt_1", seenBody["turnId"])
	require.Equal(t, "org_1", seenBody["organizationId"])
	require.Equal(t, false, seenBody["webSearchEnabled"])
	require.Equal(t, "cap", seenBody["maasCapability"])
	require.Equal(t, "knowledge_qa_runtime", claims["agentteam_internal_purpose"])
}

func TestKnowledgeQARuntimeGatewayPreparesFrozenRootsBeforeExecution(t *testing.T) {
	var seenPath, seenAuthorization string
	upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		seenPath = r.URL.Path
		seenAuthorization = r.Header.Get("Authorization")
		_ = json.NewEncoder(w).Encode(KnowledgeQARuntimePrepareResult{
			Roots: []KnowledgeQARuntimeRoot{{HandleID: "kqr_1", Revision: "sha-fixed"}},
			ScopeDiagnostics: KnowledgeQARuntimeScopeDiagnostics{
				AuthorizedSourceCount:         2,
				PartiallyAvailableSourceCount: 1,
			},
			RuntimeVersion: "knowledge-qa-v3", PromptVersion: "exploration-v2",
		})
	}))
	defer upstream.Close()
	gateway, err := NewKnowledgeQARuntimeGateway(Dependencies{
		BaseURL: upstream.URL, HTTPClient: upstream.Client(), TrustedJWTAudience: "meshx",
	})
	require.NoError(t, err)
	result, err := gateway.PrepareKnowledgeQATurn(context.Background(), KnowledgeQARuntimePrepareRequest{
		Principal: KnowledgeQARuntimePrincipal{
			OrganizationID: "org_1", WorkspaceID: "wsp_1", RequesterID: "usr_1", TurnID: "kqt_1",
		},
		KnowledgeBaseIDs: []string{"kb_1"},
	})
	require.NoError(t, err)
	require.Equal(t, "kqr_1", result.Roots[0].HandleID)
	require.Equal(t, 2, result.ScopeDiagnostics.AuthorizedSourceCount)
	require.Equal(t, 1, result.ScopeDiagnostics.PartiallyAvailableSourceCount)
	require.Equal(t, "/api/v1/kb/knowledge-qa/runtime/prepare", seenPath)
	claims := decodeClaims(t, strings.TrimPrefix(seenAuthorization, "Bearer "))
	require.Equal(t, "knowledge_qa_runtime_prepare", claims["agentteam_internal_purpose"])
}

func TestKnowledgeQARuntimeGatewayRejectsIncompletePrincipal(t *testing.T) {
	gateway := &KnowledgeQARuntimeGateway{upstream: &Handler{}}
	_, err := gateway.ExecuteKnowledgeQATurn(context.Background(), KnowledgeQARuntimeRequest{})
	require.ErrorContains(t, err, "principal is incomplete")
}

func TestKnowledgeQARuntimeGatewayPreservesStructuredFailureCode(t *testing.T) {
	upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusPaymentRequired)
		_ = json.NewEncoder(w).Encode(map[string]string{
			"code":    "resolver.credit.insufficient",
			"message": "organization credits are insufficient",
		})
	}))
	defer upstream.Close()
	gateway, err := NewKnowledgeQARuntimeGateway(Dependencies{
		BaseURL: upstream.URL, HTTPClient: upstream.Client(), TrustedJWTAudience: "meshx",
	})
	require.NoError(t, err)

	_, err = gateway.ExecuteKnowledgeQATurn(context.Background(), KnowledgeQARuntimeRequest{
		Principal: KnowledgeQARuntimePrincipal{
			OrganizationID: "org_1", WorkspaceID: "wsp_1", RequesterID: "usr_1", TurnID: "kqt_1",
		},
		Question: "hello", Model: "model-a", ReasonLevel: "medium",
		MaaSCapability: "cap", IdempotencyKey: "kqa_1",
	})
	var upstreamErr UpstreamHTTPError
	require.ErrorAs(t, err, &upstreamErr)
	require.Equal(t, "resolver.credit.insufficient", upstreamErr.Code)
}

func TestKnowledgeQARuntimeGatewayRecoversOwnerBoundAccountingFromFixedEndpoint(t *testing.T) {
	var seenPath, seenAuthorization string
	var seenBody map[string]any
	upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		seenPath = r.URL.Path
		seenAuthorization = r.Header.Get("Authorization")
		require.NoError(t, json.NewDecoder(r.Body).Decode(&seenBody))
		_ = json.NewEncoder(w).Encode(KnowledgeQAAccountingRecoveryResult{
			Found: true, Terminal: true, Generation: 2,
			Usage:         map[string]any{"input_tokens": 17},
			SettlementRef: "usage_2", SettlementRefs: []string{"usage_1", "usage_2"},
		})
	}))
	defer upstream.Close()
	gateway, err := NewKnowledgeQARuntimeGateway(Dependencies{
		BaseURL: upstream.URL, HTTPClient: upstream.Client(), TrustedJWTAudience: "meshx",
	})
	require.NoError(t, err)
	result, err := gateway.RecoverKnowledgeQATurnAccounting(context.Background(), KnowledgeQAAccountingRecoveryRequest{
		Principal: KnowledgeQARuntimePrincipal{
			OrganizationID: "org_1", WorkspaceID: "wsp_1", RequesterID: "usr_1", TurnID: "kqt_1",
		},
		AttemptID: "kqa_1",
	})
	require.NoError(t, err)
	require.True(t, result.Found)
	require.True(t, result.Terminal)
	require.Equal(t, 2, result.Generation)
	require.Equal(t, []string{"usage_1", "usage_2"}, result.SettlementRefs)
	require.Equal(t, "/api/v1/kb/knowledge-qa/runtime/accounting/recover", seenPath)
	require.Equal(t, "kqa_1", seenBody["attemptId"])
	claims := decodeClaims(t, strings.TrimPrefix(seenAuthorization, "Bearer "))
	require.Equal(t, "usr_1", claims["sub"])
	require.Equal(t, "knowledge_qa_runtime_accounting", claims["agentteam_internal_purpose"])
}

func TestKnowledgeQARuntimeGatewayOpensCitationThroughFixedRequesterEndpoint(t *testing.T) {
	var seenPath, seenAuthorization string
	var seenBody map[string]any
	upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		seenPath = r.URL.Path
		seenAuthorization = r.Header.Get("Authorization")
		require.NoError(t, json.NewDecoder(r.Body).Decode(&seenBody))
		_ = json.NewEncoder(w).Encode(KnowledgeQACitationOpenResult{
			Status: "available", OpenMode: "evidence", Content: "bounded\n",
			Path: "docs/design.md", Revision: "sha-old", LineStart: 1, LineEnd: 2, Truncated: true,
		})
	}))
	defer upstream.Close()
	gateway, err := NewKnowledgeQARuntimeGateway(Dependencies{
		BaseURL: upstream.URL, HTTPClient: upstream.Client(), TrustedJWTAudience: "meshx",
	})
	require.NoError(t, err)
	result, err := gateway.OpenKnowledgeQACitation(context.Background(), KnowledgeQACitationOpenRequest{
		Principal: KnowledgeQARuntimePrincipal{OrganizationID: "org_1", WorkspaceID: "wsp_1", RequesterID: "usr_1", TurnID: "kqt_1"},
		Citation: KnowledgeQARuntimeCitation{
			KnowledgeBaseID: "kb_1", SourceID: "src_1", RootKind: "primary",
			RepoRole: "primary", Revision: "sha-old", Path: "docs/design.md", LineStart: 1, LineEnd: 2,
		},
		AllowRestrictedEvidence: true,
	})
	require.NoError(t, err)
	require.Equal(t, "available", result.Status)
	require.Equal(t, "evidence", result.OpenMode)
	require.Equal(t, "bounded\n", result.Content)
	require.True(t, result.Truncated)
	require.Equal(t, "/api/v1/kb/knowledge-qa/citations/open", seenPath)
	require.Equal(t, "kqt_1", seenBody["turnId"])
	require.Equal(t, true, seenBody["allowRestrictedEvidence"])
	claims := decodeClaims(t, strings.TrimPrefix(seenAuthorization, "Bearer "))
	require.Equal(t, "usr_1", claims["sub"])
	require.Equal(t, "knowledge_qa_citation_open", claims["agentteam_internal_purpose"])
}
