package service

import (
	"context"
	"fmt"
	"time"

	apicontract "bit.admedia.com/scm/ad/adpilot-indexing-repo-sync.com/internal/contracts/api"
	"bit.admedia.com/scm/ad/adpilot-indexing-repo-sync.com/internal/domain"
	"bit.admedia.com/scm/ad/adpilot-indexing-repo-sync.com/internal/git"
)

// ListIndexedRepos returns tracked repositories with minimal UI indexing status.
// When orgID is empty, all repositories are listed (direct repo-sync access).
// When orgID is set, only gateway-imported repos linked to that org are returned.
func (s *SyncService) ListIndexedRepos(orgID string) ([]apicontract.IndexedRepoResponse, error) {
	orgID = trimOrgID(orgID)
	if orgID == "" {
		return s.listAllIndexedRepos()
	}
	return s.listIndexedReposForOrg(orgID)
}

func (s *SyncService) listAllIndexedRepos() ([]apicontract.IndexedRepoResponse, error) {
	ctx := context.Background()
	repos, err := s.metadata.ListRepositories(ctx)
	if err != nil {
		return nil, fmt.Errorf("list repositories: %w", err)
	}

	fallbackTimes := make(map[string]time.Time, len(repos))
	summaries := make([]apicontract.IndexedRepoResponse, 0, len(repos))

	for _, repo := range repos {
		summary, fallback, err := s.indexedRepoSummary(ctx, repo)
		if err != nil {
			return nil, err
		}
		fallbackTimes[repo.RepoID] = fallback
		summaries = append(summaries, summary)
	}

	apicontract.SortIndexedReposByLastIndexedAt(summaries, fallbackTimes)
	return summaries, nil
}

func (s *SyncService) listIndexedReposForOrg(orgID string) ([]apicontract.IndexedRepoResponse, error) {
	ctx := context.Background()
	repoIDs, err := s.metadata.ListRepoIDsByOrg(ctx, orgID)
	if err != nil {
		return nil, fmt.Errorf("list org repository links: %w", err)
	}

	fallbackTimes := make(map[string]time.Time, len(repoIDs))
	summaries := make([]apicontract.IndexedRepoResponse, 0, len(repoIDs))

	for _, repoID := range repoIDs {
		if !git.IsGatewayRepoID(repoID) {
			continue
		}

		repo, found, err := s.metadata.GetRepository(ctx, repoID)
		if err != nil {
			return nil, fmt.Errorf("get repository %s: %w", repoID, err)
		}
		if !found {
			summaries = append(summaries, apicontract.IndexedRepoResponse{
				RepoID:   repoID,
				RepoName: apicontract.RepoNameFromID(repoID),
				Status:   apicontract.IndexedRepoStatusIndexing,
			})
			continue
		}

		summary, fallback, err := s.indexedRepoSummary(ctx, repo)
		if err != nil {
			return nil, err
		}
		fallbackTimes[repoID] = fallback
		summaries = append(summaries, summary)
	}

	apicontract.SortIndexedReposByLastIndexedAt(summaries, fallbackTimes)
	return summaries, nil
}

func (s *SyncService) indexedRepoSummary(ctx context.Context, repo domain.Repository) (apicontract.IndexedRepoResponse, time.Time, error) {
	run, hasRun, err := s.metadata.GetLatestIndexingRunByRepo(ctx, repo.RepoID)
	if err != nil {
		return apicontract.IndexedRepoResponse{}, time.Time{}, fmt.Errorf("get latest indexing run for %s: %w", repo.RepoID, err)
	}
	return apicontract.IndexedRepoFromDomain(repo, run, hasRun), repo.UpdatedAt, nil
}

func trimOrgID(orgID string) string {
	for len(orgID) > 0 && (orgID[0] == ' ' || orgID[0] == '\t') {
		orgID = orgID[1:]
	}
	for len(orgID) > 0 {
		last := orgID[len(orgID)-1]
		if last != ' ' && last != '\t' {
			break
		}
		orgID = orgID[:len(orgID)-1]
	}
	return orgID
}
