package api

import (
	"sort"
	"strings"
	"time"

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

// Minimal UI indexing status values for indexed-repo list responses.
const (
	IndexedRepoStatusIndexing = "indexing"
	IndexedRepoStatusReady    = "ready"
	IndexedRepoStatusFailed   = "failed"
)

// IndexedRepoResponse is the minimal REST view of a tracked repository for UI listing.
type IndexedRepoResponse struct {
	RepoID           string  `json:"repo_id"`
	RepoName         string  `json:"repo_name"`
	GitURL           string  `json:"git_url"`
	Status           string  `json:"status"`
	LastIndexedAt    *string `json:"last_indexed_at"`
	LatestSnapshotID string  `json:"latest_snapshot_id,omitempty"`
	Error            string  `json:"error,omitempty"`
	FailedStage      string  `json:"failed_stage,omitempty"`
}

// IndexedReposResponse wraps a list of indexed repository summaries.
type IndexedReposResponse struct {
	Repos []IndexedRepoResponse `json:"repos"`
}

// RepoNameFromID returns the display name segment after the project key slash.
func RepoNameFromID(repoID string) string {
	if idx := strings.LastIndex(repoID, "/"); idx >= 0 && idx < len(repoID)-1 {
		return repoID[idx+1:]
	}
	return repoID
}

// DeriveMinimalIndexingStatus maps an indexing run to a minimal UI status.
// Returns indexing when run is absent (caller passes hasRun=false).
func DeriveMinimalIndexingStatus(run domain.IndexingRun, hasRun bool) string {
	if !hasRun {
		return IndexedRepoStatusIndexing
	}

	if run.Status == domain.IndexingRunStatusFailed {
		return IndexedRepoStatusFailed
	}

	for _, stage := range []string{
		run.Stages.RepoSync,
		run.Stages.DocsParse,
		run.Stages.CodeParse,
		run.Stages.GraphFinalize,
		run.Stages.CommitIntel,
		run.Stages.Embedding,
	} {
		if stage == domain.StageStatusFailed {
			return IndexedRepoStatusFailed
		}
	}

	if allRequiredStagesCompleted(run) {
		return IndexedRepoStatusReady
	}

	return IndexedRepoStatusIndexing
}

// IndexedRepoFromDomain builds a minimal indexed-repo response from repository and run metadata.
func IndexedRepoFromDomain(repo domain.Repository, run domain.IndexingRun, hasRun bool) IndexedRepoResponse {
	resp := IndexedRepoResponse{
		RepoID:   repo.RepoID,
		RepoName: RepoNameFromID(repo.RepoID),
		GitURL:   repo.CloneURL,
		Status:   DeriveMinimalIndexingStatus(run, hasRun),
	}
	if hasRun {
		resp.LatestSnapshotID = run.SnapshotID
		resp.Error = strings.TrimSpace(run.Error)
		resp.FailedStage = firstFailedStage(run)
	}
	if ts := resolveLastIndexedAt(repo, run, hasRun); ts != nil {
		formatted := ts.UTC().Format(time.RFC3339)
		resp.LastIndexedAt = &formatted
	}
	return resp
}

func firstFailedStage(run domain.IndexingRun) string {
	stages := []struct {
		name   string
		status string
	}{
		{"repo_sync", run.Stages.RepoSync},
		{"docs_parse", run.Stages.DocsParse},
		{"code_parse", run.Stages.CodeParse},
		{"graph_finalize", run.Stages.GraphFinalize},
		{"commit_intel", run.Stages.CommitIntel},
		{"embedding", run.Stages.Embedding},
	}
	for _, stage := range stages {
		if stage.status == domain.StageStatusFailed {
			return stage.name
		}
	}
	if run.Status == domain.IndexingRunStatusFailed {
		return "pipeline"
	}
	return ""
}

func resolveLastIndexedAt(repo domain.Repository, run domain.IndexingRun, hasRun bool) *time.Time {
	if hasRun && run.CompletedAt != nil {
		return run.CompletedAt
	}
	if !repo.LastSyncAt.IsZero() {
		ts := repo.LastSyncAt
		return &ts
	}
	return nil
}

// SortIndexedReposByLastIndexedAt sorts repos newest-first by last_indexed_at, then updated_at fallback.
func SortIndexedReposByLastIndexedAt(repos []IndexedRepoResponse, updatedAtFallback map[string]time.Time) {
	sortKeys := make([]indexedRepoSortKey, len(repos))
	for i, repo := range repos {
		var ts time.Time
		if repo.LastIndexedAt != nil {
			if parsed, err := time.Parse(time.RFC3339, *repo.LastIndexedAt); err == nil {
				ts = parsed
			}
		}
		if ts.IsZero() {
			ts = updatedAtFallback[repo.RepoID]
		}
		sortKeys[i] = indexedRepoSortKey{repo: repo, sortTime: ts}
	}

	sort.Slice(sortKeys, func(i, j int) bool {
		return sortKeys[i].sortTime.After(sortKeys[j].sortTime)
	})

	for i := range repos {
		repos[i] = sortKeys[i].repo
	}
}

type indexedRepoSortKey struct {
	repo     IndexedRepoResponse
	sortTime time.Time
}
