package mongo

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"time"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"

	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.com/internal/contracts/metadata"
	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.com/internal/store"
)

type commitAnalysisBSON struct {
	AnalysisID       string    `bson:"analysis_id"`
	RepoID           string    `bson:"repo_id"`
	SnapshotID       string    `bson:"snapshot_id"`
	CommitSHA        string    `bson:"commit_sha"`
	BaseCommitSHA    string    `bson:"base_commit_sha"`
	ChangedFiles     []string  `bson:"changed_files,omitempty"`
	ImpactedSymbols  []string  `bson:"impacted_symbols,omitempty"`
	Summary          string    `bson:"summary,omitempty"`
	RelatedDocChunks []string  `bson:"related_doc_chunks,omitempty"`
	EventID          string    `bson:"event_id,omitempty"`
	DeltaID          string    `bson:"delta_id,omitempty"`
	AuthorName       string    `bson:"author_name,omitempty"`
	AuthorEmail      string    `bson:"author_email,omitempty"`
	CommitterName    string    `bson:"committer_name,omitempty"`
	CommitterEmail   string    `bson:"committer_email,omitempty"`
	AuthoredAt       string    `bson:"authored_at,omitempty"`
	CommittedAt      string    `bson:"committed_at,omitempty"`
	CommitMessage    string    `bson:"commit_message,omitempty"`
	UpdatedAt        time.Time `bson:"updated_at"`
}

// SaveAnalysis upserts a commit analysis document.
func (s *Store) SaveAnalysis(ctx context.Context, doc store.CommitAnalysisDoc) error {
	if err := ctx.Err(); err != nil {
		return err
	}
	if strings.TrimSpace(doc.AnalysisID) == "" {
		return errors.New("analysis_id is required")
	}

	record := commitAnalysisBSON{
		AnalysisID:       doc.AnalysisID,
		RepoID:           doc.RepoID,
		SnapshotID:       doc.SnapshotID,
		CommitSHA:        doc.CommitSHA,
		BaseCommitSHA:    doc.BaseCommitSHA,
		ChangedFiles:     doc.ChangedFiles,
		ImpactedSymbols:  doc.ImpactedSymbols,
		Summary:          doc.Summary,
		RelatedDocChunks: doc.RelatedDocChunks,
		EventID:          doc.EventID,
		DeltaID:          doc.DeltaID,
		AuthorName:       doc.AuthorName,
		AuthorEmail:      doc.AuthorEmail,
		CommitterName:    doc.CommitterName,
		CommitterEmail:   doc.CommitterEmail,
		AuthoredAt:       doc.AuthoredAt,
		CommittedAt:      doc.CommittedAt,
		CommitMessage:    doc.CommitMessage,
		UpdatedAt:        time.Now().UTC(),
	}
	_, err := s.collection(collectionCommitAnalyses).UpdateOne(
		ctx,
		bson.M{"analysis_id": doc.AnalysisID},
		bson.M{"$set": record},
		options.Update().SetUpsert(true),
	)
	if err != nil {
		return fmt.Errorf("upsert commit analysis: %w", err)
	}
	return nil
}

// GetAnalysis loads a commit analysis by ID.
func (s *Store) GetAnalysis(ctx context.Context, analysisID string) (store.CommitAnalysisDoc, error) {
	var doc commitAnalysisBSON
	var empty store.CommitAnalysisDoc
	if err := ctx.Err(); err != nil {
		return empty, err
	}

	err := s.collection(collectionCommitAnalyses).FindOne(ctx, bson.M{
		"analysis_id": analysisID,
	}).Decode(&doc)
	if err != nil {
		if errors.Is(err, mongo.ErrNoDocuments) {
			return empty, fmt.Errorf("%w: %s", store.ErrArtifactNotFound, analysisID)
		}
		return empty, fmt.Errorf("find commit analysis: %w", err)
	}
	return bsonToCommitAnalysisDoc(doc), nil
}

// ExistsByEventID reports whether an analysis was already persisted for an event.
func (s *Store) ExistsByEventID(ctx context.Context, eventID string) (bool, error) {
	if err := ctx.Err(); err != nil {
		return false, err
	}
	if strings.TrimSpace(eventID) == "" {
		return false, nil
	}

	err := s.collection(collectionCommitAnalyses).FindOne(ctx, bson.M{
		"event_id": eventID,
	}, options.FindOne().SetProjection(bson.M{"analysis_id": 1})).Err()
	if err != nil {
		if errors.Is(err, mongo.ErrNoDocuments) {
			return false, nil
		}
		return false, fmt.Errorf("check commit analysis event_id: %w", err)
	}
	return true, nil
}

// CountAnalysesBySnapshot returns persisted analyses for fan-in completion checks.
func (s *Store) CountAnalysesBySnapshot(ctx context.Context, repoID, snapshotID string) (int64, error) {
	if err := ctx.Err(); err != nil {
		return 0, err
	}
	return s.collection(collectionCommitAnalyses).CountDocuments(ctx, bson.M{
		"repo_id":     repoID,
		"snapshot_id": snapshotID,
	})
}

// GetAnalysisResult loads a commit analysis as the debug API result shape.
func (s *Store) GetAnalysisResult(ctx context.Context, analysisID string) (store.AnalysisResult, error) {
	doc, err := s.GetAnalysis(ctx, analysisID)
	if err != nil {
		return store.AnalysisResult{}, err
	}
	return commitAnalysisDocToResult(doc), nil
}

func bsonToCommitAnalysisDoc(doc commitAnalysisBSON) store.CommitAnalysisDoc {
	return store.CommitAnalysisDoc{
		AnalysisID:       doc.AnalysisID,
		RepoID:           doc.RepoID,
		SnapshotID:       doc.SnapshotID,
		CommitSHA:        doc.CommitSHA,
		BaseCommitSHA:    doc.BaseCommitSHA,
		ChangedFiles:     append([]string(nil), doc.ChangedFiles...),
		ImpactedSymbols:  append([]string(nil), doc.ImpactedSymbols...),
		Summary:          doc.Summary,
		RelatedDocChunks: append([]string(nil), doc.RelatedDocChunks...),
		EventID:          doc.EventID,
		DeltaID:          doc.DeltaID,
		GitCommitMetadata: metadata.GitCommitMetadata{
			AuthorName:     doc.AuthorName,
			AuthorEmail:    doc.AuthorEmail,
			CommitterName:  doc.CommitterName,
			CommitterEmail: doc.CommitterEmail,
			AuthoredAt:     doc.AuthoredAt,
			CommittedAt:    doc.CommittedAt,
			CommitMessage:  doc.CommitMessage,
		},
	}
}

func commitAnalysisDocToResult(doc store.CommitAnalysisDoc) store.AnalysisResult {
	return store.AnalysisResult{
		AnalysisID:       doc.AnalysisID,
		RepoID:           doc.RepoID,
		SnapshotID:       doc.SnapshotID,
		CommitSHA:        doc.CommitSHA,
		BaseCommitSHA:    doc.BaseCommitSHA,
		ImpactedSymbols:  append([]string(nil), doc.ImpactedSymbols...),
		ChangedFiles:     append([]string(nil), doc.ChangedFiles...),
		Summary:          doc.Summary,
		RelatedDocChunks: append([]string(nil), doc.RelatedDocChunks...),
		GitCommitMetadata: doc.GitCommitMetadata,
	}
}
