package server

import (
	"context"
	"testing"
	"time"

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

func TestStartTenantSchemaInitResumeUsesLegacyProvisioner(t *testing.T) {
	ctx := context.Background()
	tenantID := "tenant-legacy-resume"
	db, cipher := newSchemaResumeTenant(t, tenantID, tenant.ProviderTiDBCloudStarterLegacy)
	prov := &fakeProvisioner{provider: tenant.ProviderTiDBCloudStarterLegacy}
	srv := NewWithConfig(Config{
		Meta:                     db.Meta,
		Pool:                     db.Pool,
		LegacyStarterProvisioner: prov,
	})
	t.Cleanup(srv.Close)

	startSchemaResume(srv, ctx, tenantID, tenant.ProviderTiDBCloudStarterLegacy, db, cipher)
	srv.forkWorkerWG.Wait()
	assertTenantStatus(t, db, tenantID, meta.TenantActive)
}

func TestStartTenantSchemaInitResumeSkipsMissingLegacyProvisioner(t *testing.T) {
	ctx := context.Background()
	tenantID := "tenant-legacy-resume-missing-provisioner"
	db, cipher := newSchemaResumeTenant(t, tenantID, tenant.ProviderTiDBCloudStarterLegacy)
	srv := NewWithConfig(Config{
		Meta: db.Meta,
		Pool: db.Pool,
	})
	t.Cleanup(srv.Close)

	startSchemaResume(srv, ctx, tenantID, tenant.ProviderTiDBCloudStarterLegacy, db, cipher)
	srv.forkWorkerWG.Wait()
	assertTenantStatus(t, db, tenantID, meta.TenantProvisioning)
}

func TestStartTenantSchemaInitResumeUsesLocalProvisioner(t *testing.T) {
	ctx := context.Background()
	tenantID := "tenant-local-resume"
	db, cipher := newSchemaResumeTenant(t, tenantID, tenant.ProviderLocal)
	prov := &fakeProvisioner{provider: tenant.ProviderLocal}
	srv := NewWithConfig(Config{
		Meta:        db.Meta,
		Pool:        db.Pool,
		Provisioner: prov,
	})
	t.Cleanup(srv.Close)

	startSchemaResume(srv, ctx, tenantID, tenant.ProviderLocal, db, cipher)
	srv.forkWorkerWG.Wait()
	assertTenantStatus(t, db, tenantID, meta.TenantActive)
}

func TestStartTenantSchemaInitResumeSkipsMissingLocalProvisioner(t *testing.T) {
	ctx := context.Background()
	tenantID := "tenant-local-resume-missing-provisioner"
	db, cipher := newSchemaResumeTenant(t, tenantID, tenant.ProviderLocal)
	srv := NewWithConfig(Config{
		Meta: db.Meta,
		Pool: db.Pool,
	})
	t.Cleanup(srv.Close)

	startSchemaResume(srv, ctx, tenantID, tenant.ProviderLocal, db, cipher)
	srv.forkWorkerWG.Wait()
	assertTenantStatus(t, db, tenantID, meta.TenantProvisioning)
}

func TestStartTenantSchemaInitResumeSkipsLocalOnNativeProvisioner(t *testing.T) {
	ctx := context.Background()
	tenantID := "tenant-local-resume-native-provisioner"
	db, cipher := newSchemaResumeTenant(t, tenantID, tenant.ProviderLocal)
	srv := NewWithConfig(Config{
		Meta:        db.Meta,
		Pool:        db.Pool,
		Provisioner: &fakeProvisioner{provider: tenant.ProviderTiDBCloudNative},
	})
	t.Cleanup(srv.Close)

	startSchemaResume(srv, ctx, tenantID, tenant.ProviderLocal, db, cipher)
	srv.forkWorkerWG.Wait()
	assertTenantStatus(t, db, tenantID, meta.TenantProvisioning)
}

func startSchemaResume(srv *Server, ctx context.Context, tenantID, provider string, db *testDBInfo, cipher []byte) {
	srv.startTenantSchemaInitResume(ctx, meta.Tenant{
		ID:               tenantID,
		Provider:         provider,
		DBHost:           db.DBHost,
		DBPort:           db.DBPort,
		DBUser:           db.DBUser,
		DBPasswordCipher: cipher,
		DBName:           db.DBName,
		DBTLS:            false,
	})
}

func assertTenantStatus(t *testing.T, db *testDBInfo, tenantID string, want meta.TenantStatus) {
	t.Helper()
	tenantMeta, err := db.Meta.GetTenant(context.Background(), tenantID)
	if err != nil {
		t.Fatal(err)
	}
	if tenantMeta.Status != want {
		t.Fatalf("tenant status = %s, want %s", tenantMeta.Status, want)
	}
}

func newSchemaResumeTenant(t *testing.T, tenantID, provider string) (*testDBInfo, []byte) {
	t.Helper()
	db := newTenantDeleteDBInfo(t)
	ctx := context.Background()
	cipher, err := db.Pool.Encrypt(ctx, []byte(db.DBPass))
	if err != nil {
		t.Fatal(err)
	}
	now := time.Now().UTC()
	if err := db.Meta.InsertTenant(ctx, &meta.Tenant{
		ID:               tenantID,
		Status:           meta.TenantProvisioning,
		Provider:         provider,
		DBHost:           db.DBHost,
		DBPort:           db.DBPort,
		DBUser:           db.DBUser,
		DBPasswordCipher: cipher,
		DBName:           db.DBName,
		DBTLS:            false,
		SchemaVersion:    1,
		CreatedAt:        now,
		UpdatedAt:        now,
	}); err != nil {
		t.Fatal(err)
	}
	return db, cipher
}
