package forwarder

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
)

type KnowledgeQARuntimeGateway struct{ upstream *Handler }

type KnowledgeQARuntimePrincipal struct {
	OrganizationID string
	WorkspaceID    string
	RequesterID    string
	TurnID         string
}

type KnowledgeQARuntimeRequest struct {
	Principal        KnowledgeQARuntimePrincipal
	Question         string
	KnowledgeBaseIDs []string
	SourceIDs        []string
	RootHandleIDs    []string
	WebSearchEnabled bool
	Model            string
	ReasonLevel      string
	MaaSCapability   string
	IdempotencyKey   string
	RuntimeContext   KnowledgeQARuntimeContext
}

type KnowledgeQARuntimePrepareRequest struct {
	Principal        KnowledgeQARuntimePrincipal
	KnowledgeBaseIDs []string
	SourceIDs        []string
}

type KnowledgeQARuntimeHistoryTurn struct {
	TurnID     string `json:"turnId"`
	Status     string `json:"status"`
	Question   string `json:"question"`
	Answer     string `json:"answer,omitempty"`
	Provenance string `json:"provenance"`
	Evidence   bool   `json:"evidence"`
}

type KnowledgeQARuntimeContext struct {
	Generation             int64                           `json:"generation"`
	Summary                string                          `json:"summary,omitempty"`
	CompactedThroughTurnID string                          `json:"compactedThroughTurnId,omitempty"`
	SummaryVersion         string                          `json:"summaryVersion,omitempty"`
	Turns                  []KnowledgeQARuntimeHistoryTurn `json:"turns,omitempty"`
}

type KnowledgeQARuntimeContextUpdate struct {
	ExpectedGeneration     int64  `json:"expectedGeneration"`
	Summary                string `json:"summary"`
	CompactedThroughTurnID string `json:"compactedThroughTurnId"`
	SummaryVersion         string `json:"summaryVersion"`
	CompactedTurnCount     int    `json:"compactedTurnCount"`
}

type KnowledgeQARuntimeRoot struct {
	HandleID        string `json:"handleId"`
	KnowledgeBaseID string `json:"knowledgeBaseId"`
	SourceID        string `json:"sourceId,omitempty"`
	SourceName      string `json:"sourceName,omitempty"`
	RootKind        string `json:"rootKind"`
	RepoRole        string `json:"repoRole"`
	Revision        string `json:"revision"`
	FreshnessStatus string `json:"freshnessStatus,omitempty"`
	DefaultRef      string `json:"defaultRef,omitempty"`
}

type KnowledgeQARuntimeCitation struct {
	CitationKind    string `json:"citationKind,omitempty"`
	KnowledgeBaseID string `json:"knowledgeBaseId"`
	SourceID        string `json:"sourceId,omitempty"`
	RootKind        string `json:"rootKind"`
	RepoRole        string `json:"repoRole"`
	Revision        string `json:"revision"`
	ResolvedRef     string `json:"resolvedRef,omitempty"`
	ResolvedRefKind string `json:"resolvedRefKind,omitempty"`
	Path            string `json:"path"`
	LineStart       int64  `json:"lineStart"`
	LineEnd         int64  `json:"lineEnd"`
	URL             string `json:"url,omitempty"`
	Title           string `json:"title,omitempty"`
	Snippet         string `json:"snippet,omitempty"`
	FetchedAt       string `json:"fetchedAt,omitempty"`
}

type KnowledgeQARuntimeDiagnostic struct {
	Code     string `json:"code"`
	Severity string `json:"severity"`
}

type KnowledgeQARuntimeClaim struct {
	Text        string   `json:"text"`
	EvidenceIDs []string `json:"evidenceIds"`
}

type KnowledgeQARuntimeEvidence struct {
	ID       string                     `json:"id"`
	Citation KnowledgeQARuntimeCitation `json:"citation"`
}

type KnowledgeQARuntimeResult struct {
	Status             string                           `json:"status"`
	ErrorCode          string                           `json:"errorCode,omitempty"`
	Claims             []KnowledgeQARuntimeClaim        `json:"claims,omitempty"`
	Evidence           []KnowledgeQARuntimeEvidence     `json:"evidence,omitempty"`
	Diagnostics        []KnowledgeQARuntimeDiagnostic   `json:"diagnostics,omitempty"`
	Roots              []KnowledgeQARuntimeRoot         `json:"roots"`
	SearchedRoots      int                              `json:"searchedRoots"`
	Usage              map[string]any                   `json:"usage,omitempty"`
	SettlementRef      string                           `json:"settlementRef,omitempty"`
	SettlementRefs     []string                         `json:"settlementRefs,omitempty"`
	RuntimeVersion     string                           `json:"runtimeVersion"`
	PromptVersion      string                           `json:"promptVersion"`
	ToolUsage          KnowledgeQARuntimeToolUsage      `json:"toolUsage"`
	RuntimeContext     *KnowledgeQARuntimeContextUpdate `json:"runtimeContext,omitempty"`
	SourceBranchChange *KnowledgeQASourceBranchChange   `json:"sourceBranchChange,omitempty"`
	DebugModelOutput   string                           `json:"debugModelOutput,omitempty"`
}

type KnowledgeQASourceBranchChange struct {
	KnowledgeBaseID string `json:"knowledgeBaseId"`
	SourceID        string `json:"sourceId"`
	CurrentRef      string `json:"currentRef,omitempty"`
	TargetRef       string `json:"targetRef"`
}

type KnowledgeQARuntimePrepareResult struct {
	Roots            []KnowledgeQARuntimeRoot           `json:"roots"`
	ScopeDiagnostics KnowledgeQARuntimeScopeDiagnostics `json:"scopeDiagnostics"`
	RuntimeVersion   string                             `json:"runtimeVersion"`
	PromptVersion    string                             `json:"promptVersion"`
}

// KnowledgeQAAccountingRecoveryRequest identifies one exact requester-owned
// runtime attempt whose content-free accounting checkpoint should be read.
type KnowledgeQAAccountingRecoveryRequest struct {
	Principal KnowledgeQARuntimePrincipal
	AttemptID string
}

// KnowledgeQAAccountingRecoveryResult is the bounded aggregate returned by
// MeshX without prompt, answer, evidence, or repository content.
type KnowledgeQAAccountingRecoveryResult struct {
	Found          bool           `json:"found"`
	Terminal       bool           `json:"terminal,omitempty"`
	Generation     int            `json:"generation,omitempty"`
	Usage          map[string]any `json:"usage,omitempty"`
	SettlementRef  string         `json:"settlementRef,omitempty"`
	SettlementRefs []string       `json:"settlementRefs,omitempty"`
}

type KnowledgeQARuntimeScopeDiagnostics struct {
	AuthorizedKnowledgeBaseCount  int `json:"authorizedKnowledgeBaseCount"`
	AuthorizedSourceCount         int `json:"authorizedSourceCount"`
	UnavailableKnowledgeBaseCount int `json:"unavailableKnowledgeBaseCount"`
	UnavailableSourceCount        int `json:"unavailableSourceCount"`
	PartiallyAvailableSourceCount int `json:"partiallyAvailableSourceCount"`
}

type KnowledgeQARuntimeToolUsage struct {
	GrepCalls   int `json:"grepCalls"`
	ListCalls   int `json:"listCalls"`
	ReadCalls   int `json:"readCalls"`
	RefCalls    int `json:"refCalls"`
	ModelCalls  int `json:"modelCalls"`
	ResultBytes int `json:"resultBytes"`

	WebSearchCalls int `json:"webSearchCalls"`
	WebReadCalls   int `json:"webReadCalls"`
	WebSources     int `json:"webSources"`
}

type KnowledgeQACitationOpenRequest struct {
	Principal               KnowledgeQARuntimePrincipal
	Citation                KnowledgeQARuntimeCitation
	AllowRestrictedEvidence bool
}

type KnowledgeQACitationOpenResult struct {
	Status    string `json:"status"`
	OpenMode  string `json:"openMode,omitempty"`
	Content   string `json:"content,omitempty"`
	Path      string `json:"path,omitempty"`
	Revision  string `json:"revision,omitempty"`
	LineStart int64  `json:"lineStart,omitempty"`
	LineEnd   int64  `json:"lineEnd,omitempty"`
	TargetURL string `json:"targetUrl,omitempty"`
	Truncated bool   `json:"truncated,omitempty"`
}

func NewKnowledgeQARuntimeGateway(deps Dependencies) (*KnowledgeQARuntimeGateway, error) {
	upstream, err := NewHandler(deps)
	if err != nil {
		return nil, err
	}
	return &KnowledgeQARuntimeGateway{upstream: upstream}, nil
}

func (gateway *KnowledgeQARuntimeGateway) PrepareKnowledgeQATurn(ctx context.Context, request KnowledgeQARuntimePrepareRequest) (KnowledgeQARuntimePrepareResult, error) {
	if gateway == nil || gateway.upstream == nil {
		return KnowledgeQARuntimePrepareResult{}, fmt.Errorf("Knowledge Q&A runtime is not configured")
	}
	request.Principal = normalizeKnowledgeQAPrincipal(request.Principal)
	if !completeKnowledgeQAPrincipal(request.Principal) {
		return KnowledgeQARuntimePrepareResult{}, fmt.Errorf("Knowledge Q&A runtime principal is incomplete")
	}
	payload, err := json.Marshal(map[string]any{
		"organizationId":   request.Principal.OrganizationID,
		"turnId":           request.Principal.TurnID,
		"knowledgeBaseIds": request.KnowledgeBaseIDs,
		"sourceIds":        request.SourceIDs,
	})
	if err != nil {
		return KnowledgeQARuntimePrepareResult{}, err
	}
	var response KnowledgeQARuntimePrepareResult
	err = gateway.upstream.ForwardJSON(ctx, JSONRequest{
		Method: http.MethodPost, Path: "/api/v1/kb/knowledge-qa/runtime/prepare",
		WorkspaceID: request.Principal.WorkspaceID,
		Identity: TrustedIdentity{
			UserID: request.Principal.RequesterID, AgentTeamUserID: request.Principal.RequesterID,
			PreferredUsername: normalizePreferredUsernameCandidate(request.Principal.RequesterID),
		},
		Body: json.RawMessage(payload), InternalPurpose: "knowledge_qa_runtime_prepare",
	}, &response)
	if err != nil {
		return KnowledgeQARuntimePrepareResult{}, err
	}
	return response, nil
}

func (gateway *KnowledgeQARuntimeGateway) ExecuteKnowledgeQATurn(ctx context.Context, request KnowledgeQARuntimeRequest) (KnowledgeQARuntimeResult, error) {
	if gateway == nil || gateway.upstream == nil {
		return KnowledgeQARuntimeResult{}, fmt.Errorf("Knowledge Q&A runtime is not configured")
	}
	request.Principal = normalizeKnowledgeQAPrincipal(request.Principal)
	if !completeKnowledgeQAPrincipal(request.Principal) {
		return KnowledgeQARuntimeResult{}, fmt.Errorf("Knowledge Q&A runtime principal is incomplete")
	}
	payload := map[string]any{
		"organizationId":   request.Principal.OrganizationID,
		"turnId":           request.Principal.TurnID,
		"question":         strings.TrimSpace(request.Question),
		"knowledgeBaseIds": request.KnowledgeBaseIDs,
		"sourceIds":        request.SourceIDs,
		"rootHandleIds":    request.RootHandleIDs,
		"webSearchEnabled": request.WebSearchEnabled,
		"model":            strings.TrimSpace(request.Model),
		"reasonLevel":      strings.TrimSpace(request.ReasonLevel),
		"maasCapability":   strings.TrimSpace(request.MaaSCapability),
		"idempotencyKey":   strings.TrimSpace(request.IdempotencyKey),
		"runtimeContext":   request.RuntimeContext,
	}
	body, err := json.Marshal(payload)
	if err != nil {
		return KnowledgeQARuntimeResult{}, err
	}
	var response KnowledgeQARuntimeResult
	err = gateway.upstream.ForwardJSON(ctx, JSONRequest{
		Method:      "POST",
		Path:        "/api/v1/kb/knowledge-qa/runtime/execute",
		WorkspaceID: request.Principal.WorkspaceID,
		Identity: TrustedIdentity{
			UserID:            request.Principal.RequesterID,
			AgentTeamUserID:        request.Principal.RequesterID,
			PreferredUsername: normalizePreferredUsernameCandidate(request.Principal.RequesterID),
		},
		Body:            json.RawMessage(body),
		InternalPurpose: "knowledge_qa_runtime",
	}, &response)
	if err != nil {
		return KnowledgeQARuntimeResult{}, err
	}
	return response, nil
}

// RecoverKnowledgeQATurnAccounting reads one content-free, owner-bound MeshX
// accounting checkpoint for terminal-first reconciliation.
func (gateway *KnowledgeQARuntimeGateway) RecoverKnowledgeQATurnAccounting(
	ctx context.Context,
	request KnowledgeQAAccountingRecoveryRequest,
) (KnowledgeQAAccountingRecoveryResult, error) {
	if gateway == nil || gateway.upstream == nil {
		return KnowledgeQAAccountingRecoveryResult{}, fmt.Errorf("Knowledge Q&A runtime is not configured")
	}
	request.Principal = normalizeKnowledgeQAPrincipal(request.Principal)
	request.AttemptID = strings.TrimSpace(request.AttemptID)
	if !completeKnowledgeQAPrincipal(request.Principal) || request.AttemptID == "" {
		return KnowledgeQAAccountingRecoveryResult{}, fmt.Errorf("Knowledge Q&A accounting recovery authority is incomplete")
	}
	payload, err := json.Marshal(map[string]any{
		"organizationId": request.Principal.OrganizationID,
		"turnId":         request.Principal.TurnID,
		"attemptId":      request.AttemptID,
	})
	if err != nil {
		return KnowledgeQAAccountingRecoveryResult{}, err
	}
	var response KnowledgeQAAccountingRecoveryResult
	err = gateway.upstream.ForwardJSON(ctx, JSONRequest{
		Method: http.MethodPost, Path: "/api/v1/kb/knowledge-qa/runtime/accounting/recover",
		WorkspaceID: request.Principal.WorkspaceID,
		Identity: TrustedIdentity{
			UserID: request.Principal.RequesterID, AgentTeamUserID: request.Principal.RequesterID,
			PreferredUsername: normalizePreferredUsernameCandidate(request.Principal.RequesterID),
		},
		Body: json.RawMessage(payload), InternalPurpose: "knowledge_qa_runtime_accounting",
	}, &response)
	if err != nil {
		return KnowledgeQAAccountingRecoveryResult{}, err
	}
	return response, nil
}

func normalizeKnowledgeQAPrincipal(principal KnowledgeQARuntimePrincipal) KnowledgeQARuntimePrincipal {
	principal.OrganizationID = strings.TrimSpace(principal.OrganizationID)
	principal.WorkspaceID = strings.TrimSpace(principal.WorkspaceID)
	principal.RequesterID = strings.TrimSpace(principal.RequesterID)
	principal.TurnID = strings.TrimSpace(principal.TurnID)
	return principal
}

func completeKnowledgeQAPrincipal(principal KnowledgeQARuntimePrincipal) bool {
	return principal.OrganizationID != "" && principal.WorkspaceID != "" &&
		principal.RequesterID != "" && principal.TurnID != ""
}

func (gateway *KnowledgeQARuntimeGateway) OpenKnowledgeQACitation(ctx context.Context, request KnowledgeQACitationOpenRequest) (KnowledgeQACitationOpenResult, error) {
	if gateway == nil || gateway.upstream == nil {
		return KnowledgeQACitationOpenResult{}, fmt.Errorf("Knowledge Q&A citation reader is not configured")
	}
	request.Principal = normalizeKnowledgeQAPrincipal(request.Principal)
	if !completeKnowledgeQAPrincipal(request.Principal) {
		return KnowledgeQACitationOpenResult{}, fmt.Errorf("Knowledge Q&A citation principal is incomplete")
	}
	payload, err := json.Marshal(map[string]any{
		"organizationId":          request.Principal.OrganizationID,
		"turnId":                  request.Principal.TurnID,
		"citation":                request.Citation,
		"allowRestrictedEvidence": request.AllowRestrictedEvidence,
	})
	if err != nil {
		return KnowledgeQACitationOpenResult{}, err
	}
	var response KnowledgeQACitationOpenResult
	err = gateway.upstream.ForwardJSON(ctx, JSONRequest{
		Method: http.MethodPost, Path: "/api/v1/kb/knowledge-qa/citations/open",
		WorkspaceID: request.Principal.WorkspaceID,
		Identity: TrustedIdentity{
			UserID: request.Principal.RequesterID, AgentTeamUserID: request.Principal.RequesterID,
			PreferredUsername: normalizePreferredUsernameCandidate(request.Principal.RequesterID),
		},
		Body:            json.RawMessage(payload),
		InternalPurpose: "knowledge_qa_citation_open",
	}, &response)
	if err != nil {
		return KnowledgeQACitationOpenResult{}, err
	}
	return response, nil
}
