package mongo

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

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

	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.com/internal/contracts/graph"
	"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"
)

const collectionGraphDeltas = "graph_deltas"
const collectionCommitProcessingState = "commit_processing_state"

// CountGraphDeltasBySnapshot counts persisted graph deltas for a snapshot.
func (s *Store) CountGraphDeltasBySnapshot(ctx context.Context, repoID, snapshotID string) (int64, error) {
	if err := ctx.Err(); err != nil {
		return 0, err
	}
	return s.deltasCollection(collectionGraphDeltas).CountDocuments(ctx, bson.M{
		"repo_id":     repoID,
		"snapshot_id": snapshotID,
	})
}

// CountCompletedCommitsBySnapshot counts commits marked completed for ordered fan-in.
func (s *Store) CountCompletedCommitsBySnapshot(ctx context.Context, repoID, snapshotID string) (int64, error) {
	if err := ctx.Err(); err != nil {
		return 0, err
	}
	return s.deltasCollection(collectionCommitProcessingState).CountDocuments(ctx, bson.M{
		"repo_id":     repoID,
		"snapshot_id": snapshotID,
		"status":      "completed",
	})
}

type graphDeltaDoc struct {
	DeltaID         string                     `bson:"delta_id"`
	RepoID          string                     `bson:"repo_id"`
	SnapshotID      string                     `bson:"snapshot_id"`
	BaseCommitSHA   string                     `bson:"base_commit_sha"`
	TargetCommitSHA string                     `bson:"target_commit_sha"`
	SchemaVersion   string                     `bson:"schema_version"`
	ChangedFiles    []string                   `bson:"changed_files,omitempty"`
	FileDeltas      []graph.FileDelta          `bson:"file_deltas,omitempty"`
	GitCommit       metadata.GitCommitMetadata `bson:"git_commit,omitempty"`
}

// LoadDeltaByURI loads a graph delta from mongo://graph_deltas/{delta_id}.
func (s *Store) LoadDeltaByURI(ctx context.Context, deltaURI string) (graph.Delta, error) {
	var empty graph.Delta
	if err := ctx.Err(); err != nil {
		return empty, err
	}

	collection, deltaID, err := store.ParseMongoURI(deltaURI)
	if err != nil {
		return empty, err
	}
	if collection != collectionGraphDeltas {
		return empty, fmt.Errorf("unsupported mongo collection for delta: %s", collection)
	}

	var doc graphDeltaDoc
	err = s.deltasCollection(collectionGraphDeltas).FindOne(ctx, bson.M{"delta_id": deltaID}).Decode(&doc)
	if err != nil {
		if errors.Is(err, mongo.ErrNoDocuments) {
			return empty, fmt.Errorf("%w: %s", store.ErrArtifactNotFound, deltaID)
		}
		return empty, fmt.Errorf("find graph delta: %w", err)
	}

	return docToDelta(doc), nil
}

// LoadDeltaByID loads a graph delta by deterministic delta artifact ID.
func (s *Store) LoadDeltaByID(ctx context.Context, deltaID string) (graph.Delta, error) {
	return s.LoadDeltaByURI(ctx, fmt.Sprintf("mongo://%s/%s", collectionGraphDeltas, deltaID))
}

// LoadArtifactByURI is not used by commit intelligence analysis today.
func (s *Store) LoadArtifactByURI(ctx context.Context, artifactURI string) (graph.Artifact, error) {
	var empty graph.Artifact
	if err := ctx.Err(); err != nil {
		return empty, err
	}

	collection, _, err := store.ParseMongoURI(artifactURI)
	if err != nil {
		return empty, err
	}
	if strings.TrimSpace(collection) == "" {
		return empty, fmt.Errorf("artifact uri is required")
	}
	return empty, fmt.Errorf("artifact loading from mongo collection %q is not supported", collection)
}

func docToDelta(doc graphDeltaDoc) graph.Delta {
	schema := doc.SchemaVersion
	if schema == "" {
		schema = graph.DeltaSchemaVersionV1
	}
	return graph.Delta{
		SchemaVersion:   schema,
		RepoID:          doc.RepoID,
		SnapshotID:      doc.SnapshotID,
		BaseCommitSHA:   doc.BaseCommitSHA,
		TargetCommitSHA: doc.TargetCommitSHA,
		ChangedFiles:    doc.ChangedFiles,
		FileDeltas:      doc.FileDeltas,
		GitCommit:       doc.GitCommit,
	}
}
