package knowledge

import (
	"context"
	"fmt"
	"strings"

	"cyberstrike-ai/internal/config"

	"github.com/cloudwego/eino/callbacks"
	"github.com/cloudwego/eino/components"
	"github.com/cloudwego/eino/components/retriever"
	"github.com/cloudwego/eino/schema"
	"go.uber.org/zap"
)

// knowledgePipelineRetriever: MultiQuery → vector candidates → rerank → post-process.
type knowledgePipelineRetriever struct {
	inner retriever.Retriever
	base  *Retriever
}

func newKnowledgePipelineRetriever(inner retriever.Retriever, base *Retriever) *knowledgePipelineRetriever {
	if inner == nil || base == nil {
		return nil
	}
	return &knowledgePipelineRetriever{inner: inner, base: base}
}

func (p *knowledgePipelineRetriever) GetType() string {
	return "KnowledgeRAGPipeline"
}

func (p *knowledgePipelineRetriever) Retrieve(ctx context.Context, query string, opts ...retriever.Option) (out []*schema.Document, err error) {
	if p == nil || p.inner == nil || p.base == nil {
		return nil, fmt.Errorf("knowledge pipeline retriever: nil")
	}
	q := strings.TrimSpace(query)
	if q == "" {
		return nil, fmt.Errorf("查询不能为空")
	}

	ro := retriever.GetCommonOptions(nil, opts...)
	finalTopK := p.base.config.TopK
	if finalTopK <= 0 {
		finalTopK = 5
	}
	if ro.TopK != nil && *ro.TopK > 0 {
		finalTopK = *ro.TopK
	}

	ctx = callbacks.EnsureRunInfo(ctx, p.GetType(), components.ComponentOfRetriever)
	ctx = callbacks.OnStart(ctx, &retriever.CallbackInput{Query: q, TopK: finalTopK, Extra: ro.DSLInfo})
	defer func() {
		if err != nil {
			_ = callbacks.OnError(ctx, err)
			return
		}
		_ = callbacks.OnEnd(ctx, &retriever.CallbackOutput{Docs: out})
	}()

	out, err = p.inner.Retrieve(ctx, q, opts...)
	if err != nil {
		return nil, err
	}
	if len(out) == 0 {
		return out, nil
	}

	if rr := p.base.documentReranker(); rr != nil && len(out) > 1 {
		reranked, rerr := rr.Rerank(ctx, q, out)
		if rerr != nil {
			if p.base.logger != nil {
				p.base.logger.Warn("知识检索重排失败，已使用融合序", zap.Error(rerr))
			}
		} else if len(reranked) > 0 {
			out = reranked
		}
	}

	tokenModel := ""
	if p.base.embedder != nil {
		tokenModel = p.base.embedder.EmbeddingModelName()
	}
	var postPO *config.PostRetrieveConfig
	if p.base.config != nil {
		postPO = &p.base.config.PostRetrieve
	}
	out, err = ApplyPostRetrieve(out, postPO, tokenModel, finalTopK)
	if err != nil {
		return nil, err
	}
	return out, nil
}

var _ retriever.Retriever = (*knowledgePipelineRetriever)(nil)
