package tenant

import (
	"context"
	"strings"
	"testing"
	"time"

	"github.com/mem9-ai/drive9/pkg/datastore"
	"github.com/mem9-ai/drive9/pkg/tenant/schema"
)

func TestDB9ContentLayoutRepairStatements(t *testing.T) {
	stmts := schema.DB9ContentLayoutRepairStatements()
	for _, want := range []string{
		"ALTER TABLE files ADD COLUMN IF NOT EXISTS content_layout",
		"ALTER TABLE contents ADD COLUMN IF NOT EXISTS content_layout",
		"ALTER TABLE file_gc_tasks ADD COLUMN IF NOT EXISTS content_layout",
	} {
		found := false
		for _, stmt := range stmts {
			if strings.HasPrefix(stmt, want) {
				found = true
				break
			}
		}
		if !found {
			t.Fatalf("active DB9 repair statements missing %q: %q", want, stmts)
		}
	}
}

func TestDB9ColdAcquireRepairsHistoricalContentLayout(t *testing.T) {
	for _, tc := range []struct {
		name             string
		clearSplitTables bool
	}{
		{name: "pre split", clearSplitTables: true},
		{name: "post split", clearSplitTables: false},
	} {
		t.Run(tc.name, func(t *testing.T) {
			ctx := context.Background()
			pool, tenant := newTestPoolAndTenant(t, 1, "tenant-db9-layout-"+strings.ReplaceAll(tc.name, " ", "-"))
			tenant.Provider = ProviderDB9
			store, err := datastore.Open(testDSN)
			if err != nil {
				t.Fatal(err)
			}
			t.Cleanup(func() { _ = store.Close() })

			now := time.Now().UTC()
			legacy := &datastore.File{
				FileID:      "legacy-file",
				StorageType: datastore.StorageDB9,
				ContentBlob: []byte("legacy-data"),
				SizeBytes:   int64(len("legacy-data")),
				Revision:    1,
				Status:      datastore.StatusConfirmed,
				CreatedAt:   now,
			}
			if err := store.InsertFile(ctx, legacy); err != nil {
				t.Fatalf("insert legacy file: %v", err)
			}
			if err := store.InsertNode(ctx, &datastore.FileNode{
				NodeID:     "legacy-node",
				Path:       "/legacy.txt",
				ParentPath: "/",
				Name:       "legacy.txt",
				FileID:     legacy.FileID,
				CreatedAt:  now,
			}); err != nil {
				t.Fatalf("insert legacy node: %v", err)
			}
			task, err := datastore.NewFileGCTaskFromFile(legacy, now)
			if err != nil {
				t.Fatalf("create legacy file GC task: %v", err)
			}
			if _, err := store.EnqueueFileGCTaskTx(store.DB(), task); err != nil {
				t.Fatalf("insert legacy file GC task: %v", err)
			}

			if tc.clearSplitTables {
				for _, table := range []string{"contents", "semantic", "inodes"} {
					if _, err := store.DB().Exec("DELETE FROM " + table); err != nil {
						t.Fatalf("clear %s: %v", table, err)
					}
				}
			}
			for _, table := range []string{"files", "contents", "file_gc_tasks"} {
				if _, err := store.DB().Exec("ALTER TABLE " + table + " DROP COLUMN content_layout"); err != nil {
					t.Fatalf("drop %s.content_layout: %v", table, err)
				}
			}

			backend, release, err := pool.Acquire(ctx, tenant)
			if err != nil {
				t.Fatalf("cold acquire legacy DB9 tenant: %v", err)
			}
			defer release()

			node, err := backend.StatNodeCtx(ctx, "/legacy.txt")
			if err != nil {
				t.Fatalf("stat migrated legacy file: %v", err)
			}
			if node.File == nil || node.File.ContentLayout != datastore.ContentLayoutSingle {
				t.Fatalf("migrated layout = %+v, want single", node.File)
			}
			data, err := backend.ReadCtx(ctx, "/legacy.txt", 0, -1)
			if err != nil {
				t.Fatalf("read migrated legacy file: %v", err)
			}
			if got := string(data); got != "legacy-data" {
				t.Fatalf("migrated data = %q, want legacy-data", got)
			}
			gotTask, err := backend.Store().GetFileGCTaskByFileID(ctx, legacy.FileID)
			if err != nil {
				t.Fatalf("get migrated file GC task: %v", err)
			}
			if gotTask.ContentLayout != datastore.ContentLayoutSingle {
				t.Fatalf("file GC layout = %q, want single", gotTask.ContentLayout)
			}
		})
	}
}

func TestDB9ColdAcquireCurrentSplitSchemaWithoutLegacyFiles(t *testing.T) {
	ctx := context.Background()
	pool, tenant := newTestPoolAndTenant(t, 1, "tenant-db9-current-split")
	tenant.Provider = ProviderDB9

	store, err := datastore.Open(testDSN)
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { _ = store.Close() })
	if _, err := store.DB().Exec(`DROP TABLE files`); err != nil {
		t.Fatalf("drop legacy files table: %v", err)
	}
	for _, table := range []string{"contents", "file_gc_tasks"} {
		if _, err := store.DB().Exec("ALTER TABLE " + table + " DROP COLUMN content_layout"); err != nil {
			t.Fatalf("drop %s.content_layout: %v", table, err)
		}
	}

	_, release, err := pool.Acquire(ctx, tenant)
	if err != nil {
		t.Fatalf("cold acquire current split DB9 tenant: %v", err)
	}
	defer release()
	for _, table := range []string{"contents", "file_gc_tasks"} {
		var count int
		if err := store.DB().QueryRow(`SELECT COUNT(*) FROM information_schema.columns
			WHERE table_schema = DATABASE() AND table_name = ? AND column_name = 'content_layout'`, table).Scan(&count); err != nil {
			t.Fatalf("check %s.content_layout: %v", table, err)
		}
		if count != 1 {
			t.Fatalf("%s.content_layout count = %d, want 1", table, count)
		}
	}
}
