//go:build integration

package mongo_test

import (
	"context"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"os"
	"testing"
	"time"

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

func testStore(t *testing.T) *mongostore.Store {
	t.Helper()

	uri := os.Getenv("MONGO_URI")
	if uri == "" {
		uri = "mongodb://localhost:27018/adpilot_repo_sync"
	}
	database := os.Getenv("MONGODB_DATABASE")
	if database == "" {
		database = "adpilot_repo_sync"
	}

	store, err := mongostore.New(config.MongoConfig{URI: uri, Database: database})
	if err != nil {
		t.Skipf("mongodb unavailable: %v", err)
	}
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()
	if !store.Ready(ctx) {
		t.Skip("mongodb not reachable")
	}
	if err := store.EnsureIndexes(ctx); err != nil {
		t.Fatalf("ensure indexes: %v", err)
	}
	t.Cleanup(func() {
		_ = store.Close(context.Background())
	})
	return store
}

func uniqueTestID(prefix string) string {
	var b [4]byte
	_, _ = rand.Read(b[:])
	return fmt.Sprintf("%s_%s", prefix, hex.EncodeToString(b[:]))
}

func TestStoreRepositoryCRUD(t *testing.T) {
	store := testStore(t)
	ctx := context.Background()
	now := time.Now().UTC()

	repo := domain.Repository{
		RepoID:        "ad/integration-repo",
		CloneURL:      "https://bit.admedia.com/scm/ad/integration-repo.git",
		DefaultRef:    "main",
		LastCommitSHA: "abc123",
		LastSyncAt:    now,
		Status:        domain.RepositoryStatusActive,
		UpdatedAt:     now,
	}
	if err := store.UpsertRepository(ctx, repo); err != nil {
		t.Fatalf("UpsertRepository: %v", err)
	}

	got, ok, err := store.GetRepository(ctx, repo.RepoID)
	if err != nil {
		t.Fatalf("GetRepository: %v", err)
	}
	if !ok {
		t.Fatal("expected repository")
	}
	if got.LastCommitSHA != "abc123" {
		t.Fatalf("last commit: got %q", got.LastCommitSHA)
	}
}

func TestStoreSnapshotCRUD(t *testing.T) {
	store := testStore(t)
	ctx := context.Background()
	now := time.Now().UTC()

	snap := domain.Snapshot{
		SnapshotID: uniqueTestID("snap_integration"),
		RepoID:     uniqueTestID("ad/integration"),
		CommitSHA:  "abc123",
		Ref:        "main",
		FileCount:  3,
		Status:     domain.SnapshotStatusReady,
		CreatedAt:  now,
	}
	if err := store.SaveSnapshot(ctx, snap); err != nil {
		t.Fatalf("SaveSnapshot: %v", err)
	}

	got, ok, err := store.GetSnapshot(ctx, snap.RepoID, snap.SnapshotID)
	if err != nil || !ok {
		t.Fatalf("GetSnapshot: ok=%v err=%v", ok, err)
	}
	if got.FileCount != 3 {
		t.Fatalf("file count: got %d", got.FileCount)
	}

	latest, ok, err := store.GetLatestSnapshot(ctx, snap.RepoID)
	if err != nil || !ok {
		t.Fatalf("GetLatestSnapshot: ok=%v err=%v", ok, err)
	}
	if latest.SnapshotID != snap.SnapshotID {
		t.Fatalf("latest snapshot: got %q", latest.SnapshotID)
	}
}

func TestStoreSyncJobCRUD(t *testing.T) {
	store := testStore(t)
	ctx := context.Background()
	now := time.Now().UTC()

	job := domain.SyncJob{
		JobID:     uniqueTestID("job_integration"),
		RepoID:    uniqueTestID("ad/integration"),
		Ref:       "main",
		Status:    domain.SyncStatusRunning,
		StartedAt: now,
	}
	if err := store.CreateSyncJob(ctx, job); err != nil {
		t.Fatalf("CreateSyncJob: %v", err)
	}

	completed := now.Add(time.Minute)
	job.Status = domain.SyncStatusCompleted
	job.SnapshotID = uniqueTestID("snap_integration")
	job.CompletedAt = &completed
	if err := store.UpdateSyncJob(ctx, job); err != nil {
		t.Fatalf("UpdateSyncJob: %v", err)
	}
}

func TestEnsureIndexesIdempotent(t *testing.T) {
	store := testStore(t)
	ctx := context.Background()
	if err := store.EnsureIndexes(ctx); err != nil {
		t.Fatalf("second EnsureIndexes: %v", err)
	}
}

func TestStoreIndexingRunCRUD(t *testing.T) {
	store := testStore(t)
	ctx := context.Background()
	now := time.Now().UTC()

	run := domain.IndexingRun{
		IndexingRunID:     uniqueTestID("run_integration"),
		JobID:             uniqueTestID("job_integration"),
		RepoID:            uniqueTestID("ad/integration"),
		SnapshotID:        uniqueTestID("snap_integration"),
		CommitSHA:         "abc123",
		Ref:               "main",
		Mode:              domain.IndexingRunModeFresh,
		Status:            domain.IndexingRunStatusRunning,
		ExpectedCodeFiles: 2,
		ExpectedDocsFiles: 1,
		ExpectedCommits:   3,
		Stages: domain.IndexingRunStages{
			RepoSync: domain.StageStatusRunning,
		},
		StartedAt: now,
	}
	if err := store.CreateIndexingRun(ctx, run); err != nil {
		t.Fatalf("CreateIndexingRun: %v", err)
	}

	got, ok, err := store.GetIndexingRun(ctx, run.IndexingRunID)
	if err != nil || !ok {
		t.Fatalf("GetIndexingRun: ok=%v err=%v", ok, err)
	}
	if got.ExpectedCodeFiles != 2 {
		t.Fatalf("expected code files: got %d", got.ExpectedCodeFiles)
	}

	bySnap, ok, err := store.GetIndexingRunBySnapshot(ctx, run.RepoID, run.SnapshotID)
	if err != nil || !ok {
		t.Fatalf("GetIndexingRunBySnapshot: ok=%v err=%v", ok, err)
	}
	if bySnap.IndexingRunID != run.IndexingRunID {
		t.Fatalf("snapshot lookup: got %q", bySnap.IndexingRunID)
	}

	completed := now.Add(time.Minute)
	run.Status = domain.IndexingRunStatusCompleted
	run.Stages.RepoSync = domain.StageStatusCompleted
	run.CompletedAt = &completed
	if err := store.UpdateIndexingRun(ctx, run); err != nil {
		t.Fatalf("UpdateIndexingRun: %v", err)
	}
}

func TestStoreSnapshotFilesCRUD(t *testing.T) {
	store := testStore(t)
	ctx := context.Background()
	now := time.Now().UTC()
	repoID := uniqueTestID("ad/integration")
	snapshotID := uniqueTestID("snap_integration")

	files := []domain.SnapshotFile{
		{
			RepoID:     repoID,
			SnapshotID: snapshotID,
			CommitSHA:  "abc123",
			FilePath:   "main.go",
			Language:   "go",
			FileKind:   "code",
			ChangeType: "added",
			Status:     domain.SnapshotFileStatusPending,
			CreatedAt:  now,
		},
		{
			RepoID:     repoID,
			SnapshotID: snapshotID,
			CommitSHA:  "abc123",
			FilePath:   "docs/readme.md",
			Language:   "markdown",
			FileKind:   "docs",
			ChangeType: "added",
			Status:     domain.SnapshotFileStatusPending,
			CreatedAt:  now,
		},
	}
	if err := store.InsertSnapshotFiles(ctx, files); err != nil {
		t.Fatalf("InsertSnapshotFiles: %v", err)
	}

	got, err := store.ListSnapshotFiles(ctx, repoID, snapshotID)
	if err != nil {
		t.Fatalf("ListSnapshotFiles: %v", err)
	}
	if len(got) != 2 {
		t.Fatalf("snapshot files: got %d, want 2", len(got))
	}
}
