package repo

import (
	"fmt"
	"net/http"
	"slices"

	"github.com/bluesky-social/indigo/atproto/syntax"
	"tangled.org/core/appview/models"
	"tangled.org/core/appview/pages"
	"tangled.org/core/appview/pagination"
)

func (rp *Repo) Search(w http.ResponseWriter, r *http.Request) {
	if r.Header.Get("Hx-Request") == "true" {
		if rp.oauth.GetMultiAccountUser(r) != nil {
			rp.searchResultsFragment(w, r)
		} else {
			w.WriteHeader(http.StatusForbidden)
		}
		return
	}
	if err := rp.pages.RepoSearchPage(w, pages.RepoSearchParams{
		BaseParams:  pages.BaseParamsFromContext(r.Context()),
		RepoInfo:    rp.repoResolver.GetRepoInfo(r, rp.oauth.GetMultiAccountUser(r)),
		FilterQuery: r.URL.Query().Get("q"),
	}); err != nil {
		rp.logger.Error("failed to render", "err", err)
	}
}

func (rp *Repo) searchResultsFragment(w http.ResponseWriter, r *http.Request) {
	l := rp.logger.With("handler", "Search")
	repo, err := rp.repoResolver.Resolve(r)
	if err != nil {
		l.Error("failed to get repo", "err", err)
		return
	}
	q := r.URL.Query().Get("q")

	var params pages.RepoSearchResultsFragmentParams
	params.Query = q
	defer func() {
		if err := rp.pages.RepoSearchResultsFragment(w, params); err != nil {
			l.Error("failed to render", "err", err)
		}
	}()

	if q == "" {
		return
	}

	q = fmt.Sprintf(`meta.did:%s %q`, repo.RepoDid, q)

	ctx := r.Context()

	res, err := rp.codesearch.Search(ctx, q, pagination.Page{Limit: 50})
	if err != nil {
		l.Error("failed to search files", "err", err)
		params.ErrorMsg = "Failed to perform search. Please try again later."
		return
	}

	if len(res.Results) == 0 {
		return
	}

	out := make([]pages.SearchResult, 0, len(res.Results))
	for _, res := range res.Results {
		if res.RepoDID != syntax.DID(repo.RepoDid) {
			continue
		}
		csr := pages.SearchResult{
			RepoDID:  res.RepoDID,
			Repo:     repo,
			FilePath: res.FilePath,
			Branches: res.Branches,
			Commit:   res.Commit,
			Language: res.Language,
		}
		if f := res.File; f != nil {
			csr.File = &pages.CodeSearchResult_File{
				NameSpans: pages.FileNameSpans(res.FilePath, f.Ranges),
			}
		}
		slices.SortStableFunc(res.Chunks, func(a, b models.Result_ChunkMatch) int {
			return a.ContentStartLine - b.ContentStartLine
		})
		for _, c := range res.Chunks {
			csr.Chunks = append(csr.Chunks, pages.CodeSearchResult_Chunk{
				Lines:      pages.ChunkLines(c.Content, c.ContentStartLine, c.Ranges),
				MatchCount: len(c.Ranges),
			})
		}
		out = append(out, csr)
	}

	l.Debug("repo file search result", "len", len(out), "duration", res.Stats.Duration)
	params.Results = out
}
