package service

import (
	"context"
	"log/slog"

	"github.com/pingcap/agent-git-service/internal/db"
	"github.com/pingcap/agent-git-service/internal/embedding"
	searchsvc "github.com/pingcap/agent-git-service/internal/service/search"
)

// Type aliases preserve the public API while delegating to the search package.
type CoreFilters = searchsvc.CoreFilters

type NegationFilters = searchsvc.NegationFilters

type MetadataFilters = searchsvc.MetadataFilters

type ParserFields = searchsvc.ParserFields

type SearchQualifiers = searchsvc.SearchQualifiers

type CommitSearchQuery = searchsvc.CommitSearchQuery

type CodeSearchQuery = searchsvc.CodeSearchQuery

type IssueListFilter = searchsvc.IssueListFilter

type SearchOptions = searchsvc.SearchOptions

type TextMatch = searchsvc.TextMatch

type TextMatchSpan = searchsvc.TextMatchSpan

type IssueSearchResult = searchsvc.IssueSearchResult

type PRSearchResult = searchsvc.PRSearchResult

// ParseSearchQuery parses a GitHub-style search query into structured qualifiers.
func ParseSearchQuery(query string) SearchQualifiers {
	return searchsvc.ParseSearchQuery(query)
}

// ParseCommitSearchQuery parses a commit search query, extracting free text.
func ParseCommitSearchQuery(query string) CommitSearchQuery {
	return searchsvc.ParseCommitSearchQuery(query)
}

// ParseCodeSearchQuery parses a code search query, extracting free text.
func ParseCodeSearchQuery(query string) CodeSearchQuery {
	return searchsvc.ParseCodeSearchQuery(query)
}

// GetExtensionsForLanguage returns file extensions for a given language.
func GetExtensionsForLanguage(language string) []string {
	return searchsvc.GetExtensionsForLanguage(language)
}

// HasRepoSearchFilters reports whether repo-specific qualifiers were provided.
func HasRepoSearchFilters(sq SearchQualifiers) bool {
	return searchsvc.HasRepoSearchFilters(sq)
}

// SearchIssues performs a hybrid search on the issues table.
func (s *Service) SearchIssues(ctx context.Context, query string) ([]db.Issue, error) {
	return searchsvc.SearchIssues(ctx, searchsvc.IssueSearchDeps{
		DBForCtx:                  s.DBForCtx,
		PreloadIssue:              preloadIssue,
		DefaultListLimit:          defaultListLimit,
		IssueSortQualifier:        issueSortQualifier,
		EmbedQuery:                s.embedQuery,
		UserFromContext:           UserFromContext,
		ApplyRepositoryVisibility: s.FilterDiscoverableRepositories,
	}, query)
}

// SearchIssuesDetailed performs issue search and returns ranking observability.
func (s *Service) SearchIssuesDetailed(ctx context.Context, query string, opts SearchOptions) ([]IssueSearchResult, error) {
	return searchsvc.SearchIssuesDetailed(ctx, searchsvc.IssueSearchDeps{
		DBForCtx:                  s.DBForCtx,
		PreloadIssue:              preloadIssue,
		DefaultListLimit:          defaultListLimit,
		IssueSortQualifier:        issueSortQualifier,
		EmbedQuery:                s.embedQuery,
		UserFromContext:           UserFromContext,
		ApplyRepositoryVisibility: s.FilterDiscoverableRepositories,
	}, query, opts)
}

// SearchPRs performs a hybrid search on the pull_requests table.
func (s *Service) SearchPRs(ctx context.Context, query string) ([]db.PullRequest, error) {
	return searchsvc.SearchPRs(ctx, searchsvc.PRSearchDeps{
		DBForCtx:                  s.DBForCtx,
		PreloadPR:                 preloadPRFull,
		DefaultListLimit:          defaultListLimit,
		IssueSortQualifier:        issueSortQualifier,
		EmbedQuery:                s.embedQuery,
		UserFromContext:           UserFromContext,
		ApplyRepositoryVisibility: s.FilterDiscoverableRepositories,
	}, query)
}

// SearchPRsDetailed performs pull request search and returns ranking observability.
func (s *Service) SearchPRsDetailed(ctx context.Context, query string, opts SearchOptions) ([]PRSearchResult, error) {
	return searchsvc.SearchPRsDetailed(ctx, searchsvc.PRSearchDeps{
		DBForCtx:                  s.DBForCtx,
		PreloadPR:                 preloadPRFull,
		DefaultListLimit:          defaultListLimit,
		IssueSortQualifier:        issueSortQualifier,
		EmbedQuery:                s.embedQuery,
		UserFromContext:           UserFromContext,
		ApplyRepositoryVisibility: s.FilterDiscoverableRepositories,
	}, query, opts)
}

// ListIssuesFiltered returns issues for a repo with filterBy support.
func (s *Service) ListIssuesFiltered(ctx context.Context, filter IssueListFilter) ([]db.Issue, error) {
	return searchsvc.ListIssuesFiltered(ctx, searchsvc.IssueListDeps{
		DBForCtx:                  s.DBForCtx,
		PreloadIssue:              preloadIssue,
		DefaultListLimit:          defaultListLimit,
		IssueSortQualifier:        issueSortQualifier,
		GetRepo:                   s.GetRepo,
		ApplyIssueSinceFilter:     applyIssueSinceFilter,
		ApplyIssueMilestoneFilter: applyIssueMilestoneFilter,
	}, filter)
}

// sortOrder returns the SQL ORDER BY clause for the given sort qualifier.
func sortOrder(sort, prefix string) string {
	return searchsvc.SortOrder(sort, prefix)
}

// embedQuery attempts to embed a search query string. Provider failures degrade
// to lexical search, while tenant-scope failures remain fatal.
func (s *Service) embedQuery(ctx context.Context, text string) (string, error) {
	embedder, err := s.EmbedderForCtx(ctx)
	if err != nil {
		return "", err
	}
	if embedding.IsNop(embedder) {
		return "", nil
	}
	text = embedding.TruncateInput(text)
	vec, err := embedder.Embed(ctx, text)
	if err != nil {
		slog.WarnContext(ctx, "search embed query failed; falling back to lexical search", "error", err)
		return "", nil
	}
	if len(vec) == 0 {
		return "", nil
	}
	targetDB, err := s.RequireDB(ctx)
	if err != nil {
		return "", err
	}
	s.ensureVectorInit(ctx, targetDB, len(vec))
	return embedding.FormatVector(vec), nil
}
